From 8a17e159435f8c10e930c4bcf3f6f2ed9fd303de Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Tue, 30 Aug 2011 20:58:43 +0200 Subject: Various cleanups and TODOs. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 110 ++++++++++--------------- 1 file changed, 44 insertions(+), 66 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 530ecd54bb..d05997c0c4 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -333,12 +333,6 @@ static const char *tevRasTable[] = "float4(0.0f, 0.0f, 0.0f, 0.0f)", // zero }; -static const char *alphaRef[2] = -{ - I_ALPHA"[0].r", - I_ALPHA"[0].g" -}; - //static const char *tevTexFunc[] = { "tex2D", "texRECT" }; static const char *tevCOutputTable[] = { "prev.rgb", "c0.rgb", "c1.rgb", "c2.rgb" }; @@ -385,7 +379,7 @@ const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType setlocale(LC_NUMERIC, "C"); // Reset locale for compilation text[sizeof(text) - 1] = 0x7C; // canary - BuildSwapModeTable(); + BuildSwapModeTable(); // Needed for WriteStage int numStages = bpmem.genMode.numtevstages + 1; int numTexgen = bpmem.genMode.numtexgens; @@ -505,10 +499,6 @@ const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType { // alpha test will always fail, so restart the shader and just make it an empty function WRITE(p, "ocol0 = 0;\n"); - if(DepthTextureEnable) - WRITE(p, "depth = 1.f;\n"); - if(dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND) - WRITE(p, "ocol1 = 0;\n"); WRITE(p, "discard;\n"); if(ApiType != API_D3D11) WRITE(p, "return;\n"); @@ -626,6 +616,7 @@ const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType if(RegisterStates[0].AlphaNeedOverflowControl || RegisterStates[0].ColorNeedOverflowControl) WRITE(p, "prev = frac(prev * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + // TODO: Why are we doing a second alpha pretest here? if (!WriteAlphaTest(p, ApiType, dstAlphaMode)) { // alpha test will always fail, so restart the shader and just make it an empty function @@ -740,10 +731,6 @@ static const char *TEVCMPAlphaOPTable[16] = static void WriteStage(char *&p, int n, API_TYPE ApiType) { - char *rasswap = swapModeTable[bpmem.combiners[n].alphaC.rswap]; - char *texswap = swapModeTable[bpmem.combiners[n].alphaC.tswap]; - - int texcoord = bpmem.tevorders[n/2].getTexCoord(n&1); bool bHasTexCoord = (u32)texcoord < bpmem.genMode.numtexgens; bool bHasIndStage = bpmem.tevind[n].IsActive() && bpmem.tevind[n].bt < bpmem.genMode.numindstages; @@ -780,11 +767,13 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) } else if (bpmem.tevind[n].mid <= 7 && bHasTexCoord) { // s matrix + // TODO: Might become negative? int mtxidx = 2*(bpmem.tevind[n].mid-5); WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.xx;\n", n, mtxidx, texcoord, n); } else if (bpmem.tevind[n].mid <= 11 && bHasTexCoord) { // t matrix + // TODO: Might become negative? int mtxidx = 2*(bpmem.tevind[n].mid-9); WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.yy;\n", n, mtxidx, texcoord, n); } @@ -823,11 +812,14 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) TevStageCombiner::ColorCombiner &cc = bpmem.combiners[n].colorC; TevStageCombiner::AlphaCombiner &ac = bpmem.combiners[n].alphaC; - bool bCRas = cc.a == TEVCOLORARG_RASA || cc.a == TEVCOLORARG_RASC || cc.b == TEVCOLORARG_RASA || cc.b == TEVCOLORARG_RASC || cc.c == TEVCOLORARG_RASA || cc.c == TEVCOLORARG_RASC || cc.d == TEVCOLORARG_RASA || cc.d == TEVCOLORARG_RASC; - bool bARas = ac.a == TEVALPHAARG_RASA || ac.b == TEVALPHAARG_RASA || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA; - - if(bCRas || bARas) + if(cc.a == TEVCOLORARG_RASA || cc.a == TEVCOLORARG_RASC + || cc.b == TEVCOLORARG_RASA || cc.b == TEVCOLORARG_RASC + || cc.c == TEVCOLORARG_RASA || cc.c == TEVCOLORARG_RASC + || cc.d == TEVCOLORARG_RASA || cc.d == TEVCOLORARG_RASC + || ac.a == TEVALPHAARG_RASA || ac.b == TEVALPHAARG_RASA + || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA) { + char *rasswap = swapModeTable[bpmem.combiners[n].alphaC.rswap]; WRITE(p, "rastemp = %s.%s;\n", tevRasTable[bpmem.tevorders[n / 2].getColorChan(n & 1)], rasswap); WRITE(p, "crastemp = frac(rastemp * (255.0f/256.0f)) * (256.0f/255.0f);\n"); } @@ -835,7 +827,6 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) if (bpmem.tevorders[n/2].getEnable(n&1)) { - int texmap = bpmem.tevorders[n/2].getTexMap(n&1); if(!bHasIndStage) { // calc tevcord @@ -845,20 +836,19 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) WRITE(p, "tevcoord.xy = float2(0.0f, 0.0f);\n"); } + char *texswap = swapModeTable[bpmem.combiners[n].alphaC.tswap]; + int texmap = bpmem.tevorders[n/2].getTexMap(n&1); SampleTexture(p, "textemp", "tevcoord", texswap, texmap, ApiType); } else WRITE(p, "textemp = float4(1.0f, 1.0f, 1.0f, 1.0f);\n"); - int kc = bpmem.tevksel[n / 2].getKC(n & 1); - int ka = bpmem.tevksel[n / 2].getKA(n & 1); - - - bool bCKonst = cc.a == TEVCOLORARG_KONST || cc.b == TEVCOLORARG_KONST || cc.c == TEVCOLORARG_KONST || cc.d == TEVCOLORARG_KONST; - bool bAKonst = ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST || ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST; - if (bCKonst || bAKonst ) + if (cc.a == TEVCOLORARG_KONST || cc.b == TEVCOLORARG_KONST || cc.c == TEVCOLORARG_KONST || cc.d == TEVCOLORARG_KONST + || ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST || ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST) { + int kc = bpmem.tevksel[n / 2].getKC(n & 1); + int ka = bpmem.tevksel[n / 2].getKA(n & 1); WRITE(p, "konsttemp = float4(%s, %s);\n", tevKSelTableC[kc], tevKSelTableA[ka]); if(kc > 7 || ka > 7) { @@ -870,15 +860,10 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) } } - if(cc.a == TEVCOLORARG_CPREV - || cc.a == TEVCOLORARG_APREV - || cc.b == TEVCOLORARG_CPREV - || cc.b == TEVCOLORARG_APREV - || cc.c == TEVCOLORARG_CPREV - || cc.c == TEVCOLORARG_APREV - || ac.a == TEVALPHAARG_APREV - || ac.b == TEVALPHAARG_APREV - || ac.c == TEVALPHAARG_APREV) + if(cc.a == TEVCOLORARG_CPREV || cc.a == TEVCOLORARG_APREV + || cc.b == TEVCOLORARG_CPREV || cc.b == TEVCOLORARG_APREV + || cc.c == TEVCOLORARG_CPREV || cc.c == TEVCOLORARG_APREV + || ac.a == TEVALPHAARG_APREV || ac.b == TEVALPHAARG_APREV || ac.c == TEVALPHAARG_APREV) { if(RegisterStates[0].AlphaNeedOverflowControl || RegisterStates[0].ColorNeedOverflowControl) { @@ -893,15 +878,10 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) RegisterStates[0].AuxStored = true; } - if(cc.a == TEVCOLORARG_C0 - || cc.a == TEVCOLORARG_A0 - || cc.b == TEVCOLORARG_C0 - || cc.b == TEVCOLORARG_A0 - || cc.c == TEVCOLORARG_C0 - || cc.c == TEVCOLORARG_A0 - || ac.a == TEVALPHAARG_A0 - || ac.b == TEVALPHAARG_A0 - || ac.c == TEVALPHAARG_A0) + if(cc.a == TEVCOLORARG_C0 || cc.a == TEVCOLORARG_A0 + || cc.b == TEVCOLORARG_C0 || cc.b == TEVCOLORARG_A0 + || cc.c == TEVCOLORARG_C0 || cc.c == TEVCOLORARG_A0 + || ac.a == TEVALPHAARG_A0 || ac.b == TEVALPHAARG_A0 || ac.c == TEVALPHAARG_A0) { if(RegisterStates[1].AlphaNeedOverflowControl || RegisterStates[1].ColorNeedOverflowControl) { @@ -916,15 +896,10 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) RegisterStates[1].AuxStored = true; } - if(cc.a == TEVCOLORARG_C1 - || cc.a == TEVCOLORARG_A1 - || cc.b == TEVCOLORARG_C1 - || cc.b == TEVCOLORARG_A1 - || cc.c == TEVCOLORARG_C1 - || cc.c == TEVCOLORARG_A1 - || ac.a == TEVALPHAARG_A1 - || ac.b == TEVALPHAARG_A1 - || ac.c == TEVALPHAARG_A1) + if(cc.a == TEVCOLORARG_C1 || cc.a == TEVCOLORARG_A1 + || cc.b == TEVCOLORARG_C1 || cc.b == TEVCOLORARG_A1 + || cc.c == TEVCOLORARG_C1 || cc.c == TEVCOLORARG_A1 + || ac.a == TEVALPHAARG_A1 || ac.b == TEVALPHAARG_A1 || ac.c == TEVALPHAARG_A1) { if(RegisterStates[2].AlphaNeedOverflowControl || RegisterStates[2].ColorNeedOverflowControl) { @@ -939,15 +914,10 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) RegisterStates[2].AuxStored = true; } - if(cc.a == TEVCOLORARG_C2 - || cc.a == TEVCOLORARG_A2 - || cc.b == TEVCOLORARG_C2 - || cc.b == TEVCOLORARG_A2 - || cc.c == TEVCOLORARG_C2 - || cc.c == TEVCOLORARG_A2 - || ac.a == TEVALPHAARG_A2 - || ac.b == TEVALPHAARG_A2 - || ac.c == TEVALPHAARG_A2) + if(cc.a == TEVCOLORARG_C2 || cc.a == TEVCOLORARG_A2 + || cc.b == TEVCOLORARG_C2 || cc.b == TEVCOLORARG_A2 + || cc.c == TEVCOLORARG_C2 || cc.c == TEVCOLORARG_A2 + || ac.a == TEVALPHAARG_A2 || ac.b == TEVALPHAARG_A2 || ac.c == TEVALPHAARG_A2) { if(RegisterStates[3].AlphaNeedOverflowControl || RegisterStates[3].ColorNeedOverflowControl) { @@ -994,7 +964,7 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) WRITE(p, "%s", tevBiasTable[cc.bias]); - if (cc.shift > 0) + if (cc.shift > TEVSCALE_1) WRITE(p, ")"); } else @@ -1122,6 +1092,11 @@ static int AlphaPreTest() static bool WriteAlphaTest(char *&p, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode) { + static const char *alphaRef[2] = + { + I_ALPHA"[0].r", + I_ALPHA"[0].g" + }; int Pretest = AlphaPreTest(); if(Pretest >= 0) @@ -1139,7 +1114,10 @@ static bool WriteAlphaTest(char *&p, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode compindex = bpmem.alphaFunc.comp1 % 8; WRITE(p, tevAlphaFuncsTable[compindex],alphaRef[1]);//lookup the second component from the alpha function table - WRITE(p, ")){ocol0 = 0;%s%sdiscard;%s}\n",dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "ocol1 = 0;" : "",DepthTextureEnable ? "depth = 1.f;" : "",(ApiType != API_D3D11)? "return;" : ""); + WRITE(p, ")){ocol0 = 0;%s%s discard;%s}\n", + dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "ocol1 = 0;" : "", + DepthTextureEnable ? "depth = 1.f;" : "", + (ApiType != API_D3D11) ? "return;" : ""); return true; } @@ -1197,4 +1175,4 @@ static void WriteFog(char *&p) WRITE(p, " prev.rgb = lerp(prev.rgb,"I_FOG"[0].rgb,fog);\n"); -} +} \ No newline at end of file -- cgit v1.2.3 From 08e06b22937fa79db165e5a31dfd4ccf67ef57db Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Wed, 31 Aug 2011 16:09:54 +0200 Subject: Partially revert revision d511b506120c. Slightly slows down emulation, but deobfuscates the pixel shader gen greatly... --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 102 +++---------------------- 1 file changed, 12 insertions(+), 90 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index d05997c0c4..ade8a0865e 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -353,15 +353,6 @@ static char swapModeTable[4][5]; static char text[16384]; static bool DepthTextureEnable; -struct RegisterState -{ - bool ColorNeedOverflowControl; - bool AlphaNeedOverflowControl; - bool AuxStored; -}; - -static RegisterState RegisterStates[4]; - static void BuildSwapModeTable() { for (int i = 0; i < 4; i++) @@ -582,16 +573,6 @@ const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType } } - RegisterStates[0].AlphaNeedOverflowControl = false; - RegisterStates[0].ColorNeedOverflowControl = false; - RegisterStates[0].AuxStored = false; - for(int i = 1; i < 4; i++) - { - RegisterStates[i].AlphaNeedOverflowControl = true; - RegisterStates[i].ColorNeedOverflowControl = true; - RegisterStates[i].AuxStored = false; - } - for (int i = 0; i < numStages; i++) WriteStage(p, i, ApiType); //build the equation for this stage @@ -599,22 +580,11 @@ const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType { // The results of the last texenv stage are put onto the screen, // regardless of the used destination register - if(bpmem.combiners[numStages - 1].colorC.dest != 0) - { - bool retrieveFromAuxRegister = !RegisterStates[bpmem.combiners[numStages - 1].colorC.dest].ColorNeedOverflowControl && RegisterStates[bpmem.combiners[numStages - 1].colorC.dest].AuxStored; - WRITE(p, "prev.rgb = %s%s;\n", retrieveFromAuxRegister ? "c" : "" , tevCOutputTable[bpmem.combiners[numStages - 1].colorC.dest]); - RegisterStates[0].ColorNeedOverflowControl = RegisterStates[bpmem.combiners[numStages - 1].colorC.dest].ColorNeedOverflowControl; - } - if(bpmem.combiners[numStages - 1].alphaC.dest != 0) - { - bool retrieveFromAuxRegister = !RegisterStates[bpmem.combiners[numStages - 1].alphaC.dest].AlphaNeedOverflowControl && RegisterStates[bpmem.combiners[numStages - 1].alphaC.dest].AuxStored; - WRITE(p, "prev.a = %s%s;\n", retrieveFromAuxRegister ? "c" : "" , tevAOutputTable[bpmem.combiners[numStages - 1].alphaC.dest]); - RegisterStates[0].AlphaNeedOverflowControl = RegisterStates[bpmem.combiners[numStages - 1].alphaC.dest].AlphaNeedOverflowControl; - } - } - // emulation of unisgned 8 overflow when casting if needed - if(RegisterStates[0].AlphaNeedOverflowControl || RegisterStates[0].ColorNeedOverflowControl) - WRITE(p, "prev = frac(prev * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + WRITE(p, "prev.rgb = %s;\n",tevCOutputTable[bpmem.combiners[numStages-1].colorC.dest]); + WRITE(p, "prev.a = %s;\n",tevAOutputTable[bpmem.combiners[numStages-1].alphaC.dest]); + } + // emulation of unsigned 8 overflow when casting + WRITE(p, "prev = frac(4.0f + prev * (255.0f/256.0f)) * (256.0f/255.0f);\n"); // TODO: Why are we doing a second alpha pretest here? if (!WriteAlphaTest(p, ApiType, dstAlphaMode)) @@ -864,76 +834,30 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) || cc.b == TEVCOLORARG_CPREV || cc.b == TEVCOLORARG_APREV || cc.c == TEVCOLORARG_CPREV || cc.c == TEVCOLORARG_APREV || ac.a == TEVALPHAARG_APREV || ac.b == TEVALPHAARG_APREV || ac.c == TEVALPHAARG_APREV) - { - if(RegisterStates[0].AlphaNeedOverflowControl || RegisterStates[0].ColorNeedOverflowControl) - { - WRITE(p, "cprev = frac(prev * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - RegisterStates[0].AlphaNeedOverflowControl = false; - RegisterStates[0].ColorNeedOverflowControl = false; - } - else - { - WRITE(p, "cprev = prev;\n"); - } - RegisterStates[0].AuxStored = true; - } + WRITE(p, "cprev = frac(prev * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + if(cc.a == TEVCOLORARG_C0 || cc.a == TEVCOLORARG_A0 || cc.b == TEVCOLORARG_C0 || cc.b == TEVCOLORARG_A0 || cc.c == TEVCOLORARG_C0 || cc.c == TEVCOLORARG_A0 || ac.a == TEVALPHAARG_A0 || ac.b == TEVALPHAARG_A0 || ac.c == TEVALPHAARG_A0) - { - if(RegisterStates[1].AlphaNeedOverflowControl || RegisterStates[1].ColorNeedOverflowControl) - { - WRITE(p, "cc0 = frac(c0 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - RegisterStates[1].AlphaNeedOverflowControl = false; - RegisterStates[1].ColorNeedOverflowControl = false; - } - else - { - WRITE(p, "cc0 = c0;\n"); - } - RegisterStates[1].AuxStored = true; - } + WRITE(p, "cc0 = frac(c0 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + if(cc.a == TEVCOLORARG_C1 || cc.a == TEVCOLORARG_A1 || cc.b == TEVCOLORARG_C1 || cc.b == TEVCOLORARG_A1 || cc.c == TEVCOLORARG_C1 || cc.c == TEVCOLORARG_A1 || ac.a == TEVALPHAARG_A1 || ac.b == TEVALPHAARG_A1 || ac.c == TEVALPHAARG_A1) - { - if(RegisterStates[2].AlphaNeedOverflowControl || RegisterStates[2].ColorNeedOverflowControl) - { - WRITE(p, "cc1 = frac(c1 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - RegisterStates[2].AlphaNeedOverflowControl = false; - RegisterStates[2].ColorNeedOverflowControl = false; - } - else - { - WRITE(p, "cc1 = c1;\n"); - } - RegisterStates[2].AuxStored = true; - } + WRITE(p, "cc1 = frac(c1 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + if(cc.a == TEVCOLORARG_C2 || cc.a == TEVCOLORARG_A2 || cc.b == TEVCOLORARG_C2 || cc.b == TEVCOLORARG_A2 || cc.c == TEVCOLORARG_C2 || cc.c == TEVCOLORARG_A2 || ac.a == TEVALPHAARG_A2 || ac.b == TEVALPHAARG_A2 || ac.c == TEVALPHAARG_A2) - { - if(RegisterStates[3].AlphaNeedOverflowControl || RegisterStates[3].ColorNeedOverflowControl) - { WRITE(p, "cc2 = frac(c2 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - RegisterStates[3].AlphaNeedOverflowControl = false; - RegisterStates[3].ColorNeedOverflowControl = false; - } - else - { - WRITE(p, "cc2 = c2;\n"); - } - RegisterStates[3].AuxStored = true; - } - RegisterStates[cc.dest].ColorNeedOverflowControl = (cc.clamp == 0); - RegisterStates[cc.dest].AuxStored = false; + if (cc.clamp) WRITE(p, "%s = saturate(", tevCOutputTable[cc.dest]); else @@ -980,8 +904,6 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) WRITE(p, ")"); WRITE(p,";\n"); - RegisterStates[ac.dest].AlphaNeedOverflowControl = (ac.clamp == 0); - RegisterStates[ac.dest].AuxStored = false; // combine the alpha channel if (ac.clamp) WRITE(p, "%s = saturate(", tevAOutputTable[ac.dest]); -- cgit v1.2.3 From 7f01139d13ad917d333548918492e92e2bcb436a Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Wed, 31 Aug 2011 03:18:19 +0200 Subject: Replace the pixel shader UID generation algorithm with a better one which reduces the number of redundant shader compilations by around 30% (can be optimized even further though). This should help some games which suffer from heavy stuttering like e.g. F-Zero GX or Red Steel 2. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 2360 +++++++++++++----------- Source/Core/VideoCommon/Src/PixelShaderGen.h | 4 +- 2 files changed, 1267 insertions(+), 1097 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index ade8a0865e..6d0cc64964 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -1,1100 +1,1270 @@ -// Copyright (C) 2003 Dolphin Project. - -// This program 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, version 2.0. - -// This program 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 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include -#include -#include - -#include "LightingShaderGen.h" -#include "PixelShaderGen.h" -#include "XFMemory.h" // for texture projection mode -#include "BPMemory.h" -#include "VideoConfig.h" -#include "NativeVertexFormat.h" - -PIXELSHADERUID last_pixel_shader_uid; - -// Mash together all the inputs that contribute to the code of a generated pixel shader into -// a unique identifier, basically containing all the bits. Yup, it's a lot .... -// It would likely be a lot more efficient to build this incrementally as the attributes -// are set... -void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) -{ - u32 numstages = bpmem.genMode.numtevstages + 1; - u32 projtexcoords = 0; - for (u32 i = 0; i < numstages; i++) - { - if (bpmem.tevorders[i/2].getEnable(i & 1)) - { - int texcoord = bpmem.tevorders[i / 2].getTexCoord(i & 1); - if (xfregs.texMtxInfo[i].projection) - projtexcoords |= 1 << texcoord; - } - } - uid->values[0] = (u32)bpmem.genMode.numtevstages | - ((u32)bpmem.genMode.numindstages << 4) | - ((u32)bpmem.genMode.numtexgens << 7) | - ((u32)dstAlphaMode << 11) | - ((u32)((bpmem.alphaFunc.hex >> 16) & 0xff) << 13) | - (projtexcoords << 21) | - ((u32)bpmem.ztex2.op << 29); - - // swap table - for (int i = 0; i < 8; i += 2) - ((u8*)&uid->values[1])[i / 2] = (bpmem.tevksel[i].hex & 0xf) | ((bpmem.tevksel[i + 1].hex & 0xf) << 4); - - u32 enableZTexture = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) || g_ActiveConfig.bEnablePerPixelDepth ? 1 : 0; - - uid->values[2] = (u32)bpmem.fog.c_proj_fsel.fsel | - ((u32)bpmem.fog.c_proj_fsel.proj << 3) | - ((u32)enableZTexture << 4) | ((u32)bpmem.fogRange.Base.Enabled << 5); - - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - { - for (int i = 0; i < 2; ++i) { - uid->values[3 + i] = xfregs.color[i].enablelighting ? - (u32)xfregs.color[i].hex : - (u32)xfregs.color[i].matsource; - uid->values[3 + i] |= (xfregs.alpha[i].enablelighting ? - (u32)xfregs.alpha[i].hex : - (u32)xfregs.alpha[i].matsource) << 15; - } - } - uid->values[4] |= (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) << 31; - - int hdr = 5; - u32 *pcurvalue = &uid->values[hdr]; - for (u32 i = 0; i < numstages; ++i) - { - TevStageCombiner::ColorCombiner &cc = bpmem.combiners[i].colorC; - TevStageCombiner::AlphaCombiner &ac = bpmem.combiners[i].alphaC; - - u32 val0 = cc.hex & 0xffffff; - u32 val1 = ac.hex & 0xffffff; - val0 |= bpmem.tevksel[i / 2].getKC(i & 1) << 24; - val1 |= bpmem.tevksel[i / 2].getKA(i & 1) << 24; - pcurvalue[0] = val0; - pcurvalue[1] = val1; - pcurvalue += 2; - } - - for (u32 i = 0; i < numstages / 2; ++i) - { - u32 val0, val1; - if (bpmem.tevorders[i].hex & 0x40) - val0 = bpmem.tevorders[i].hex & 0x3ff; - else - val0 = bpmem.tevorders[i].hex & 0x380; - if (bpmem.tevorders[i].hex & 0x40000) - val1 = (bpmem.tevorders[i].hex & 0x3ff000) >> 12; - else - val1 = (bpmem.tevorders[i].hex & 0x380000) >> 12; - - switch (i % 3) { - case 0: pcurvalue[0] = val0|(val1<<10); break; - case 1: pcurvalue[0] |= val0<<20; pcurvalue[1] = val1; pcurvalue++; break; - case 2: pcurvalue[1] |= (val0<<10)|(val1<<20); pcurvalue++; break; - default: PanicAlert("Unknown case for Tev Stages / 2: %08x", (i % 3)); - } - } - - if (numstages & 1) { // odd - u32 val0; - if (bpmem.tevorders[bpmem.genMode.numtevstages/2].hex & 0x40) - val0 = bpmem.tevorders[bpmem.genMode.numtevstages/2].hex & 0x3ff; - else - val0 = bpmem.tevorders[bpmem.genMode.numtevstages/2].hex & 0x380; - - switch (bpmem.genMode.numtevstages % 3) - { - case 0: pcurvalue[0] = val0; break; - case 1: pcurvalue[0] |= val0 << 20; break; - case 2: pcurvalue[1] |= val0 << 10; pcurvalue++; break; - default: PanicAlert("Unknown case for Tev Stages: %08x", bpmem.genMode.numtevstages % 3); - } - } - - if ((bpmem.genMode.numtevstages % 3) != 2) - ++pcurvalue; - - uid->tevstages = (u32)(pcurvalue - &uid->values[0] - hdr); - - for (u32 i = 0; i < bpmem.genMode.numindstages; ++i) - { - u32 val = bpmem.tevind[i].hex & 0x1fffff; // 21 bits - switch (i % 3) - { - case 0: pcurvalue[0] = val; break; - case 1: pcurvalue[0] |= val << 21; pcurvalue[1] = val >> 11; ++pcurvalue; break; - case 2: pcurvalue[0] |= val << 10; ++pcurvalue; break; - default: PanicAlert("Unknown case for Ind Stages: %08x", (i % 3)); - } - } - - // yeah, well .... - uid->indstages = (u32)(pcurvalue - &uid->values[0] - (hdr - 1) - uid->tevstages); - -} - -// old tev->pixelshader notes -// -// color for this stage (alpha, color) is given by bpmem.tevorders[0].colorchan0 -// konstant for this stage (alpha, color) is given by bpmem.tevksel -// inputs are given by bpmem.combiners[0].colorC.a/b/c/d << could be current chan color -// according to GXTevColorArg table above -// output is given by .outreg -// tevtemp is set according to swapmodetables and - -static void WriteStage(char *&p, int n, API_TYPE ApiType); -static void SampleTexture(char *&p, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType); -// static void WriteAlphaCompare(char *&p, int num, int comp); -static bool WriteAlphaTest(char *&p, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode); -static void WriteFog(char *&p); -static int AlphaPreTest(); - -static const char *tevKSelTableC[] = // KCSEL -{ - "1.0f,1.0f,1.0f", // 1 = 0x00 - "0.875f,0.875f,0.875f", // 7_8 = 0x01 - "0.75f,0.75f,0.75f", // 3_4 = 0x02 - "0.625f,0.625f,0.625f", // 5_8 = 0x03 - "0.5f,0.5f,0.5f", // 1_2 = 0x04 - "0.375f,0.375f,0.375f", // 3_8 = 0x05 - "0.25f,0.25f,0.25f", // 1_4 = 0x06 - "0.125f,0.125f,0.125f", // 1_8 = 0x07 - "ERROR", // 0x08 - "ERROR", // 0x09 - "ERROR", // 0x0a - "ERROR", // 0x0b - I_KCOLORS"[0].rgb", // K0 = 0x0C - I_KCOLORS"[1].rgb", // K1 = 0x0D - I_KCOLORS"[2].rgb", // K2 = 0x0E - I_KCOLORS"[3].rgb", // K3 = 0x0F - I_KCOLORS"[0].rrr", // K0_R = 0x10 - I_KCOLORS"[1].rrr", // K1_R = 0x11 - I_KCOLORS"[2].rrr", // K2_R = 0x12 - I_KCOLORS"[3].rrr", // K3_R = 0x13 - I_KCOLORS"[0].ggg", // K0_G = 0x14 - I_KCOLORS"[1].ggg", // K1_G = 0x15 - I_KCOLORS"[2].ggg", // K2_G = 0x16 - I_KCOLORS"[3].ggg", // K3_G = 0x17 - I_KCOLORS"[0].bbb", // K0_B = 0x18 - I_KCOLORS"[1].bbb", // K1_B = 0x19 - I_KCOLORS"[2].bbb", // K2_B = 0x1A - I_KCOLORS"[3].bbb", // K3_B = 0x1B - I_KCOLORS"[0].aaa", // K0_A = 0x1C - I_KCOLORS"[1].aaa", // K1_A = 0x1D - I_KCOLORS"[2].aaa", // K2_A = 0x1E - I_KCOLORS"[3].aaa", // K3_A = 0x1F -}; - -static const char *tevKSelTableA[] = // KASEL -{ - "1.0f", // 1 = 0x00 - "0.875f",// 7_8 = 0x01 - "0.75f", // 3_4 = 0x02 - "0.625f",// 5_8 = 0x03 - "0.5f", // 1_2 = 0x04 - "0.375f",// 3_8 = 0x05 - "0.25f", // 1_4 = 0x06 - "0.125f",// 1_8 = 0x07 - "ERROR", // 0x08 - "ERROR", // 0x09 - "ERROR", // 0x0a - "ERROR", // 0x0b - "ERROR", // 0x0c - "ERROR", // 0x0d - "ERROR", // 0x0e - "ERROR", // 0x0f - I_KCOLORS"[0].r", // K0_R = 0x10 - I_KCOLORS"[1].r", // K1_R = 0x11 - I_KCOLORS"[2].r", // K2_R = 0x12 - I_KCOLORS"[3].r", // K3_R = 0x13 - I_KCOLORS"[0].g", // K0_G = 0x14 - I_KCOLORS"[1].g", // K1_G = 0x15 - I_KCOLORS"[2].g", // K2_G = 0x16 - I_KCOLORS"[3].g", // K3_G = 0x17 - I_KCOLORS"[0].b", // K0_B = 0x18 - I_KCOLORS"[1].b", // K1_B = 0x19 - I_KCOLORS"[2].b", // K2_B = 0x1A - I_KCOLORS"[3].b", // K3_B = 0x1B - I_KCOLORS"[0].a", // K0_A = 0x1C - I_KCOLORS"[1].a", // K1_A = 0x1D - I_KCOLORS"[2].a", // K2_A = 0x1E - I_KCOLORS"[3].a", // K3_A = 0x1F -}; - -static const char *tevScaleTable[] = // CS -{ - "1.0f", // SCALE_1 - "2.0f", // SCALE_2 - "4.0f", // SCALE_4 - "0.5f", // DIVIDE_2 -}; - -static const char *tevBiasTable[] = // TB -{ - "", // ZERO, - "+0.5f", // ADDHALF, - "-0.5f", // SUBHALF, - "", -}; - -static const char *tevOpTable[] = { // TEV - "+", // TEVOP_ADD = 0, - "-", // TEVOP_SUB = 1, -}; - -static const char *tevCInputTable[] = // CC -{ - "(prev.rgb)", // CPREV, - "(prev.aaa)", // APREV, - "(c0.rgb)", // C0, - "(c0.aaa)", // A0, - "(c1.rgb)", // C1, - "(c1.aaa)", // A1, - "(c2.rgb)", // C2, - "(c2.aaa)", // A2, - "(textemp.rgb)", // TEXC, - "(textemp.aaa)", // TEXA, - "(rastemp.rgb)", // RASC, - "(rastemp.aaa)", // RASA, - "float3(1.0f, 1.0f, 1.0f)", // ONE - "float3(0.5f, 0.5f, 0.5f)", // HALF - "(konsttemp.rgb)", //"konsttemp.rgb", // KONST - "float3(0.0f, 0.0f, 0.0f)", // ZERO - ///aded extra values to map clamped values - "(cprev.rgb)", // CPREV, - "(cprev.aaa)", // APREV, - "(cc0.rgb)", // C0, - "(cc0.aaa)", // A0, - "(cc1.rgb)", // C1, - "(cc1.aaa)", // A1, - "(cc2.rgb)", // C2, - "(cc2.aaa)", // A2, - "(textemp.rgb)", // TEXC, - "(textemp.aaa)", // TEXA, - "(crastemp.rgb)", // RASC, - "(crastemp.aaa)", // RASA, - "float3(1.0f, 1.0f, 1.0f)", // ONE - "float3(0.5f, 0.5f, 0.5f)", // HALF - "(ckonsttemp.rgb)", //"konsttemp.rgb", // KONST - "float3(0.0f, 0.0f, 0.0f)", // ZERO - "PADERROR", "PADERROR", "PADERROR", "PADERROR" -}; - -static const char *tevAInputTable[] = // CA -{ - "prev", // APREV, - "c0", // A0, - "c1", // A1, - "c2", // A2, - "textemp", // TEXA, - "rastemp", // RASA, - "konsttemp", // KONST, (hw1 had quarter) - "float4(0.0f, 0.0f, 0.0f, 0.0f)", // ZERO - ///aded extra values to map clamped values - "cprev", // APREV, - "cc0", // A0, - "cc1", // A1, - "cc2", // A2, - "textemp", // TEXA, - "crastemp", // RASA, - "ckonsttemp", // KONST, (hw1 had quarter) - "float4(0.0f, 0.0f, 0.0f, 0.0f)", // ZERO - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", -}; - -static const char *tevRasTable[] = -{ - "colors_0", - "colors_1", - "ERROR", //2 - "ERROR", //3 - "ERROR", //4 - "alphabump", // use bump alpha - "(alphabump*(255.0f/248.0f))", //normalized - "float4(0.0f, 0.0f, 0.0f, 0.0f)", // zero -}; - -//static const char *tevTexFunc[] = { "tex2D", "texRECT" }; - -static const char *tevCOutputTable[] = { "prev.rgb", "c0.rgb", "c1.rgb", "c2.rgb" }; -static const char *tevAOutputTable[] = { "prev.a", "c0.a", "c1.a", "c2.a" }; -static const char *tevIndAlphaSel[] = {"", "x", "y", "z"}; -//static const char *tevIndAlphaScale[] = {"", "*32", "*16", "*8"}; -static const char *tevIndAlphaScale[] = {"*(248.0f/255.0f)", "*(224.0f/255.0f)", "*(240.0f/255.0f)", "*(248.0f/255.0f)"}; -static const char *tevIndBiasField[] = {"", "x", "y", "xy", "z", "xz", "yz", "xyz"}; // indexed by bias -static const char *tevIndBiasAdd[] = {"-128.0f", "1.0f", "1.0f", "1.0f" }; // indexed by fmt -static const char *tevIndWrapStart[] = {"0.0f", "256.0f", "128.0f", "64.0f", "32.0f", "16.0f", "0.001f" }; -static const char *tevIndFmtScale[] = {"255.0f", "31.0f", "15.0f", "7.0f" }; - -#define WRITE p+=sprintf - -static const char *swapColors = "rgba"; -static char swapModeTable[4][5]; - -static char text[16384]; -static bool DepthTextureEnable; - -static void BuildSwapModeTable() -{ - for (int i = 0; i < 4; i++) - { - swapModeTable[i][0] = swapColors[bpmem.tevksel[i*2].swap1]; - swapModeTable[i][1] = swapColors[bpmem.tevksel[i*2].swap2]; - swapModeTable[i][2] = swapColors[bpmem.tevksel[i*2+1].swap1]; - swapModeTable[i][3] = swapColors[bpmem.tevksel[i*2+1].swap2]; - swapModeTable[i][4] = 0; - } -} - -const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType, u32 components) -{ - setlocale(LC_NUMERIC, "C"); // Reset locale for compilation - text[sizeof(text) - 1] = 0x7C; // canary - - BuildSwapModeTable(); // Needed for WriteStage - int numStages = bpmem.genMode.numtevstages + 1; - int numTexgen = bpmem.genMode.numtexgens; - - char *p = text; - WRITE(p, "//Pixel Shader for TEV stages\n"); - WRITE(p, "//%i TEV stages, %i texgens, %i IND stages\n", - numStages, numTexgen, bpmem.genMode.numindstages); - - int nIndirectStagesUsed = 0; - if (bpmem.genMode.numindstages > 0) - { - for (int i = 0; i < numStages; ++i) - { - if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) - nIndirectStagesUsed |= 1 << bpmem.tevind[i].bt; - } - } - DepthTextureEnable = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) || g_ActiveConfig.bEnablePerPixelDepth ; - // Declare samplers - - if(ApiType != API_D3D11) - { - WRITE(p, "uniform sampler2D "); - } - else - { - WRITE(p, "sampler "); - } - - bool bfirst = true; - for (int i = 0; i < 8; ++i) - { - WRITE(p, "%s samp%d : register(s%d)", bfirst?"":",", i, i); - bfirst = false; - } - WRITE(p, ";\n"); - if(ApiType == API_D3D11) - { - WRITE(p, "Texture2D "); - bfirst = true; - for (int i = 0; i < 8; ++i) - { - WRITE(p, "%s Tex%d : register(t%d)", bfirst?"":",", i, i); - bfirst = false; - } - WRITE(p, ";\n"); - } - - WRITE(p, "\n"); - - WRITE(p, "uniform float4 "I_COLORS"[4] : register(c%d);\n", C_COLORS); - WRITE(p, "uniform float4 "I_KCOLORS"[4] : register(c%d);\n", C_KCOLORS); - WRITE(p, "uniform float4 "I_ALPHA"[1] : register(c%d);\n", C_ALPHA); - WRITE(p, "uniform float4 "I_TEXDIMS"[8] : register(c%d);\n", C_TEXDIMS); - WRITE(p, "uniform float4 "I_ZBIAS"[2] : register(c%d);\n", C_ZBIAS); - WRITE(p, "uniform float4 "I_INDTEXSCALE"[2] : register(c%d);\n", C_INDTEXSCALE); - WRITE(p, "uniform float4 "I_INDTEXMTX"[6] : register(c%d);\n", C_INDTEXMTX); - WRITE(p, "uniform float4 "I_FOG"[3] : register(c%d);\n", C_FOG); - - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - { - WRITE(p,"typedef struct { float4 col; float4 cosatt; float4 distatt; float4 pos; float4 dir; } Light;\n"); - WRITE(p,"typedef struct { Light lights[8]; } s_"I_PLIGHTS";\n"); - WRITE(p, "uniform s_"I_PLIGHTS" "I_PLIGHTS" : register(c%d);\n", C_PLIGHTS); - WRITE(p, "typedef struct { float4 C0, C1, C2, C3; } s_"I_PMATERIALS";\n"); - WRITE(p, "uniform s_"I_PMATERIALS" "I_PMATERIALS" : register(c%d);\n", C_PMATERIALS); - } - - WRITE(p, "void main(\n"); - if(ApiType != API_D3D11) - { - WRITE(p, " out float4 ocol0 : COLOR0,%s%s\n in float4 rawpos : %s,\n", - dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "\n out float4 ocol1 : COLOR1," : "", - DepthTextureEnable ? "\n out float depth : DEPTH," : "", - ApiType & API_OPENGL ? "WPOS" : ApiType & API_D3D9_SM20 ? "POSITION" : "VPOS"); - } - else - { - WRITE(p, " out float4 ocol0 : SV_Target0,%s%s\n in float4 rawpos : SV_Position,\n", - dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "\n out float4 ocol1 : SV_Target1," : "", - DepthTextureEnable ? "\n out float depth : SV_Depth," : ""); - } - - WRITE(p, " in float4 colors_0 : COLOR0,\n"); - WRITE(p, " in float4 colors_1 : COLOR1"); - - // compute window position if needed because binding semantic WPOS is not widely supported - if (numTexgen < 7) - { - for (int i = 0; i < numTexgen; ++i) - WRITE(p, ",\n in float3 uv%d : TEXCOORD%d", i, i); - WRITE(p, ",\n in float4 clipPos : TEXCOORD%d", numTexgen); - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - WRITE(p, ",\n in float4 Normal : TEXCOORD%d", numTexgen + 1); - } - else - { - // wpos is in w of first 4 texcoords - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - { - for (int i = 0; i < 8; ++i) - WRITE(p, ",\n in float4 uv%d : TEXCOORD%d", i, i); - } - else - { - for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) - WRITE(p, ",\n in float%d uv%d : TEXCOORD%d", i < 4 ? 4 : 3 , i, i); - } - } - WRITE(p, " ) {\n"); - - char* pmainstart = p; - int Pretest = AlphaPreTest(); - if (dstAlphaMode == DSTALPHA_ALPHA_PASS && !DepthTextureEnable && Pretest >= 0) - { - if (!Pretest) - { - // alpha test will always fail, so restart the shader and just make it an empty function - WRITE(p, "ocol0 = 0;\n"); - WRITE(p, "discard;\n"); - if(ApiType != API_D3D11) - WRITE(p, "return;\n"); - } - else - { - WRITE(p, " ocol0 = "I_ALPHA"[0].aaaa;\n"); - } - WRITE(p, "}\n"); - return text; - } - - WRITE(p, " float4 c0 = "I_COLORS"[1], c1 = "I_COLORS"[2], c2 = "I_COLORS"[3], prev = float4(0.0f, 0.0f, 0.0f, 0.0f), textemp = float4(0.0f, 0.0f, 0.0f, 0.0f), rastemp = float4(0.0f, 0.0f, 0.0f, 0.0f), konsttemp = float4(0.0f, 0.0f, 0.0f, 0.0f);\n" - " float3 comp16 = float3(1.0f, 255.0f, 0.0f), comp24 = float3(1.0f, 255.0f, 255.0f*255.0f);\n" - " float4 alphabump=0;\n" - " float3 tevcoord;\n" - " float2 wrappedcoord, tempcoord;\n" - " float4 cc0, cc1, cc2, cprev,crastemp,ckonsttemp;\n\n"); - - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - { - if (xfregs.numTexGen.numTexGens < 7) - { - WRITE(p,"float3 _norm0 = normalize(Normal.xyz);\n\n"); - WRITE(p,"float3 pos = float3(clipPos.x,clipPos.y,Normal.w);\n"); - } - else - { - WRITE(p," float3 _norm0 = normalize(float3(uv4.w,uv5.w,uv6.w));\n\n"); - WRITE(p,"float3 pos = float3(uv0.w,uv1.w,uv7.w);\n"); - } - - - WRITE(p, "float4 mat, lacc;\n" - "float3 ldir, h;\n" - "float dist, dist2, attn;\n"); - - p = GenerateLightingShader(p, components, I_PMATERIALS, I_PLIGHTS, "colors_", "colors_"); - } - - if (numTexgen < 7) - WRITE(p, "clipPos = float4(rawpos.x, rawpos.y, clipPos.z, clipPos.w);\n"); - else - WRITE(p, "float4 clipPos = float4(rawpos.x, rawpos.y, uv2.w, uv3.w);\n"); - - // HACK to handle cases where the tex gen is not enabled - if (numTexgen == 0) - { - WRITE(p, "float3 uv0 = float3(0.0f, 0.0f, 0.0f);\n"); - } - else - { - for (int i = 0; i < numTexgen; ++i) - { - // optional perspective divides - if (xfregs.texMtxInfo[i].projection == XF_TEXPROJ_STQ) - { - WRITE(p, "if (uv%d.z)", i); - WRITE(p, " uv%d.xy = uv%d.xy / uv%d.z;\n", i, i, i); - } - - WRITE(p, "uv%d.xy = uv%d.xy * "I_TEXDIMS"[%d].zw;\n", i, i, i); - } - } - - // indirect texture map lookup - for(u32 i = 0; i < bpmem.genMode.numindstages; ++i) - { - if (nIndirectStagesUsed & (1< +#include +#include +#include + +#include "LightingShaderGen.h" +#include "PixelShaderGen.h" +#include "XFMemory.h" // for texture projection mode +#include "BPMemory.h" +#include "VideoConfig.h" +#include "NativeVertexFormat.h" + +PIXELSHADERUID last_pixel_shader_uid; + +static int AlphaPreTest(); + +static void StageHash(int stage, u32* out) +{ + out[0] |= bpmem.combiners[stage].colorC.hex & 0xFFFFFF; // 24 + u32 alphaC = bpmem.combiners[stage].alphaC.hex & 0xFFFFF0; // 24, strip out tswap and rswap for now + out[0] |= (alphaC&0xF0) << 24; // 8 + out[1] |= alphaC >> 8; // 16 + + // reserve 3 bits for bpmem.tevorders[stage/2].getTexMap + out[1] |= bpmem.tevorders[stage/2].getTexCoord(stage&1) << 19; // 3 + out[1] |= bpmem.tevorders[stage/2].getEnable(stage&1) << 22; // 1 + // reserve 3 bits for bpmem.tevorders[stage/2].getColorChan + + bool bHasIndStage = bpmem.tevind[stage].IsActive() && bpmem.tevind[stage].bt < bpmem.genMode.numindstages; + out[2] |= bHasIndStage << 2; // 1 + + bool needstexcoord = false; + + if (bHasIndStage) + { + out[2] |= (bpmem.tevind[stage].hex & 0x17FFFF) << 3; // 21, TODO: needs an explanation + needstexcoord = true; + } + + + TevStageCombiner::ColorCombiner& cc = bpmem.combiners[stage].colorC; + TevStageCombiner::AlphaCombiner& ac = bpmem.combiners[stage].alphaC; + + if(cc.a == TEVCOLORARG_RASA || cc.a == TEVCOLORARG_RASC + || cc.b == TEVCOLORARG_RASA || cc.b == TEVCOLORARG_RASC + || cc.c == TEVCOLORARG_RASA || cc.c == TEVCOLORARG_RASC + || cc.d == TEVCOLORARG_RASA || cc.d == TEVCOLORARG_RASC + || ac.a == TEVALPHAARG_RASA || ac.b == TEVALPHAARG_RASA + || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA) + { + out[0] |= bpmem.combiners[stage].alphaC.rswap; + out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2].swap1 << 24; // 2 + out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2].swap2 << 26; // 2 + out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2+1].swap1 << 28; // 2 + out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2+1].swap2 << 30; // 2 + out[1] |= (bpmem.tevorders[stage/2].getColorChan(stage&1)&1) << 23; + out[2] |= (bpmem.tevorders[stage/2].getColorChan(stage&1)&0x6) >> 1; + } + + out[3] |= bpmem.tevorders[stage/2].getEnable(stage&1); + if (bpmem.tevorders[stage/2].getEnable(stage&1)) + { + if (bHasIndStage) needstexcoord = true; + + out[0] |= bpmem.combiners[stage].alphaC.tswap; + out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2].swap1 << 1; // 2 + out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2].swap2 << 3; // 2 + out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2+1].swap1 << 5; // 2 + out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2+1].swap2 << 7; // 2 + out[1] |= bpmem.tevorders[stage/2].getTexMap(stage&1) << 16; + } + + if (cc.a == TEVCOLORARG_KONST || cc.b == TEVCOLORARG_KONST || cc.c == TEVCOLORARG_KONST || cc.d == TEVCOLORARG_KONST + || ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST || ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST) + { + out[3] |= bpmem.tevksel[stage/2].getKC(stage&1) << 9; // 5 + out[3] |= bpmem.tevksel[stage/2].getKA(stage&1) << 14; // 5 + } + + if (needstexcoord) + { + out[1] |= bpmem.tevorders[stage/2].getTexCoord(stage&1) << 16; + } +} + +void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) +{ + uid->values[0] |= bpmem.genMode.numtevstages; // 4 + uid->values[0] |= bpmem.genMode.numtexgens << 4; // 4 + uid->values[0] |= dstAlphaMode << 8; // 2 + + uid->tevstages = bpmem.genMode.numtevstages; + + bool DepthTextureEnable = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) || g_ActiveConfig.bEnablePerPixelDepth; + + uid->values[0] |= DepthTextureEnable << 10; // 1 + + bool enablePL = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; + uid->values[0] |= enablePL << 11; // 1 + + if (!enablePL) uid->values[0] |= xfregs.numTexGen.numTexGens << 12; // 4 + u32 alphaPreTest = AlphaPreTest()+1; + + uid->values[0] |= alphaPreTest << 16; // 2 + + if (alphaPreTest == 1 || (alphaPreTest && !DepthTextureEnable && dstAlphaMode == DSTALPHA_ALPHA_PASS)) + { + // Courtesy of PreAlphaTest, we're done already ;) + // TODO: There's a comment including bpmem.genmode.numindstages.. shouldnt really bother about that though. + return; + } + + if (enablePL) + { + // TODO: Include register states for lighting shader + } + + for (unsigned int i = 0; i < bpmem.genMode.numtexgens; ++i) + { + if (18+i < 32) + uid->values[0] |= xfregs.texMtxInfo[i].projection << (18+i); // 1 + else + uid->values[1] |= xfregs.texMtxInfo[i].projection << (i - 14); // 1 + } + + uid->indstages = bpmem.genMode.numindstages; + + uid->values[1] = bpmem.genMode.numindstages << 2; // 3 + u32 indirectStagesUsed = 0; + for (unsigned int i = 0; i < bpmem.genMode.numindstages; ++i) + if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) + indirectStagesUsed |= (1 << bpmem.tevind[i].bt); + + assert(indirectStagesUsed == (indirectStagesUsed & 0xF)); + + uid->values[1] |= indirectStagesUsed << 5; // 4; + + for (unsigned int i = 0; i < bpmem.genMode.numindstages; ++i) + { + if (indirectStagesUsed & (1 << i)) + { + uid->values[1] |= (bpmem.tevindref.getTexCoord(i) < bpmem.genMode.numtexgens) << (9 + 3*i); // 1 + if (bpmem.tevindref.getTexCoord(i) < bpmem.genMode.numtexgens) + uid->values[1] |= bpmem.tevindref.getTexCoord(i) << (10 + 3*i); // 2 + } + } + + u32* ptr = &uid->values[2]; + for (unsigned int i = 0; i < bpmem.genMode.numtevstages+1; ++i) + { + StageHash(i, ptr); + ptr += 4; // max: ptr = &uid->values[66] + } + + if (alphaPreTest == 0 || alphaPreTest == 2) + { + ptr[0] |= bpmem.fog.c_proj_fsel.fsel; // 3 + if (DepthTextureEnable) + { + ptr[0] |= bpmem.ztex2.op << 3; // 2 + ptr[0] |= bpmem.zcontrol.zcomploc << 5; // 1 + ptr[0] |= bpmem.zmode.testenable << 6; // 1 + ptr[0] |= bpmem.zmode.updateenable << 7; // 1 + } + } + + if (dstAlphaMode != DSTALPHA_ALPHA_PASS) + { + if (bpmem.fog.c_proj_fsel.fsel != 0) + { + ptr[0] |= bpmem.fog.c_proj_fsel.proj << 8; // 1 + ptr[0] |= bpmem.fogRange.Base.Enabled << 9; // 1 + } + } + uid->tevstages = (ptr+1) - uid->values; +} + +// Mash together all the inputs that contribute to the code of a generated pixel shader into +// a unique identifier, basically containing all the bits. Yup, it's a lot .... +// It would likely be a lot more efficient to build this incrementally as the attributes +// are set... +void _GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) +{ + u32 numstages = bpmem.genMode.numtevstages + 1; + u32 projtexcoords = 0; + for (u32 i = 0; i < numstages; i++) + { + if (bpmem.tevorders[i/2].getEnable(i & 1)) + { + int texcoord = bpmem.tevorders[i / 2].getTexCoord(i & 1); + if (xfregs.texMtxInfo[i].projection) + projtexcoords |= 1 << texcoord; + } + } + uid->values[0] = (u32)bpmem.genMode.numtevstages | + ((u32)bpmem.genMode.numindstages << 4) | + ((u32)bpmem.genMode.numtexgens << 7) | + ((u32)dstAlphaMode << 11) | + ((u32)((bpmem.alphaFunc.hex >> 16) & 0xff) << 13) | + (projtexcoords << 21) | + ((u32)bpmem.ztex2.op << 29); + + // swap table + for (int i = 0; i < 8; i += 2) + ((u8*)&uid->values[1])[i / 2] = (bpmem.tevksel[i].hex & 0xf) | ((bpmem.tevksel[i + 1].hex & 0xf) << 4); + + u32 enableZTexture = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) || g_ActiveConfig.bEnablePerPixelDepth ? 1 : 0; + + uid->values[2] = (u32)bpmem.fog.c_proj_fsel.fsel | + ((u32)bpmem.fog.c_proj_fsel.proj << 3) | + ((u32)enableZTexture << 4) | ((u32)bpmem.fogRange.Base.Enabled << 5); + + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + { + for (int i = 0; i < 2; ++i) { + uid->values[3 + i] = xfregs.color[i].enablelighting ? + (u32)xfregs.color[i].hex : + (u32)xfregs.color[i].matsource; + uid->values[3 + i] |= (xfregs.alpha[i].enablelighting ? + (u32)xfregs.alpha[i].hex : + (u32)xfregs.alpha[i].matsource) << 15; + } + } + uid->values[4] |= (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) << 31; + + int hdr = 5; + u32 *pcurvalue = &uid->values[hdr]; + for (u32 i = 0; i < numstages; ++i) + { + TevStageCombiner::ColorCombiner &cc = bpmem.combiners[i].colorC; + TevStageCombiner::AlphaCombiner &ac = bpmem.combiners[i].alphaC; + + u32 val0 = cc.hex & 0xffffff; + u32 val1 = ac.hex & 0xffffff; + val0 |= bpmem.tevksel[i / 2].getKC(i & 1) << 24; + val1 |= bpmem.tevksel[i / 2].getKA(i & 1) << 24; + pcurvalue[0] = val0; + pcurvalue[1] = val1; + pcurvalue += 2; + } + + for (u32 i = 0; i < numstages / 2; ++i) + { + u32 val0, val1; + if (bpmem.tevorders[i].hex & 0x40) + val0 = bpmem.tevorders[i].hex & 0x3ff; + else + val0 = bpmem.tevorders[i].hex & 0x380; + if (bpmem.tevorders[i].hex & 0x40000) + val1 = (bpmem.tevorders[i].hex & 0x3ff000) >> 12; + else + val1 = (bpmem.tevorders[i].hex & 0x380000) >> 12; + + switch (i % 3) { + case 0: pcurvalue[0] = val0|(val1<<10); break; + case 1: pcurvalue[0] |= val0<<20; pcurvalue[1] = val1; pcurvalue++; break; + case 2: pcurvalue[1] |= (val0<<10)|(val1<<20); pcurvalue++; break; + default: PanicAlert("Unknown case for Tev Stages / 2: %08x", (i % 3)); + } + } + + if (numstages & 1) { // odd + u32 val0; + if (bpmem.tevorders[bpmem.genMode.numtevstages/2].hex & 0x40) + val0 = bpmem.tevorders[bpmem.genMode.numtevstages/2].hex & 0x3ff; + else + val0 = bpmem.tevorders[bpmem.genMode.numtevstages/2].hex & 0x380; + + switch (bpmem.genMode.numtevstages % 3) + { + case 0: pcurvalue[0] = val0; break; + case 1: pcurvalue[0] |= val0 << 20; break; + case 2: pcurvalue[1] |= val0 << 10; pcurvalue++; break; + default: PanicAlert("Unknown case for Tev Stages: %08x", bpmem.genMode.numtevstages % 3); + } + } + + if ((bpmem.genMode.numtevstages % 3) != 2) + ++pcurvalue; + + uid->tevstages = (u32)(pcurvalue - &uid->values[0] - hdr); + + for (u32 i = 0; i < bpmem.genMode.numindstages; ++i) + { + u32 val = bpmem.tevind[i].hex & 0x1fffff; // 21 bits + switch (i % 3) + { + case 0: pcurvalue[0] = val; break; + case 1: pcurvalue[0] |= val << 21; pcurvalue[1] = val >> 11; ++pcurvalue; break; + case 2: pcurvalue[0] |= val << 10; ++pcurvalue; break; + default: PanicAlert("Unknown case for Ind Stages: %08x", (i % 3)); + } + } + + // yeah, well .... + uid->indstages = (u32)(pcurvalue - &uid->values[0] - (hdr - 1) - uid->tevstages); + +} + +// old tev->pixelshader notes +// +// color for this stage (alpha, color) is given by bpmem.tevorders[0].colorchan0 +// konstant for this stage (alpha, color) is given by bpmem.tevksel +// inputs are given by bpmem.combiners[0].colorC.a/b/c/d << could be current chan color +// according to GXTevColorArg table above +// output is given by .outreg +// tevtemp is set according to swapmodetables and + +static void WriteStage(char *&p, int n, API_TYPE ApiType); +static void SampleTexture(char *&p, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType); +// static void WriteAlphaCompare(char *&p, int num, int comp); +static bool WriteAlphaTest(char *&p, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode); +static void WriteFog(char *&p); + +static const char *tevKSelTableC[] = // KCSEL +{ + "1.0f,1.0f,1.0f", // 1 = 0x00 + "0.875f,0.875f,0.875f", // 7_8 = 0x01 + "0.75f,0.75f,0.75f", // 3_4 = 0x02 + "0.625f,0.625f,0.625f", // 5_8 = 0x03 + "0.5f,0.5f,0.5f", // 1_2 = 0x04 + "0.375f,0.375f,0.375f", // 3_8 = 0x05 + "0.25f,0.25f,0.25f", // 1_4 = 0x06 + "0.125f,0.125f,0.125f", // 1_8 = 0x07 + "ERROR", // 0x08 + "ERROR", // 0x09 + "ERROR", // 0x0a + "ERROR", // 0x0b + I_KCOLORS"[0].rgb", // K0 = 0x0C + I_KCOLORS"[1].rgb", // K1 = 0x0D + I_KCOLORS"[2].rgb", // K2 = 0x0E + I_KCOLORS"[3].rgb", // K3 = 0x0F + I_KCOLORS"[0].rrr", // K0_R = 0x10 + I_KCOLORS"[1].rrr", // K1_R = 0x11 + I_KCOLORS"[2].rrr", // K2_R = 0x12 + I_KCOLORS"[3].rrr", // K3_R = 0x13 + I_KCOLORS"[0].ggg", // K0_G = 0x14 + I_KCOLORS"[1].ggg", // K1_G = 0x15 + I_KCOLORS"[2].ggg", // K2_G = 0x16 + I_KCOLORS"[3].ggg", // K3_G = 0x17 + I_KCOLORS"[0].bbb", // K0_B = 0x18 + I_KCOLORS"[1].bbb", // K1_B = 0x19 + I_KCOLORS"[2].bbb", // K2_B = 0x1A + I_KCOLORS"[3].bbb", // K3_B = 0x1B + I_KCOLORS"[0].aaa", // K0_A = 0x1C + I_KCOLORS"[1].aaa", // K1_A = 0x1D + I_KCOLORS"[2].aaa", // K2_A = 0x1E + I_KCOLORS"[3].aaa", // K3_A = 0x1F +}; + +static const char *tevKSelTableA[] = // KASEL +{ + "1.0f", // 1 = 0x00 + "0.875f",// 7_8 = 0x01 + "0.75f", // 3_4 = 0x02 + "0.625f",// 5_8 = 0x03 + "0.5f", // 1_2 = 0x04 + "0.375f",// 3_8 = 0x05 + "0.25f", // 1_4 = 0x06 + "0.125f",// 1_8 = 0x07 + "ERROR", // 0x08 + "ERROR", // 0x09 + "ERROR", // 0x0a + "ERROR", // 0x0b + "ERROR", // 0x0c + "ERROR", // 0x0d + "ERROR", // 0x0e + "ERROR", // 0x0f + I_KCOLORS"[0].r", // K0_R = 0x10 + I_KCOLORS"[1].r", // K1_R = 0x11 + I_KCOLORS"[2].r", // K2_R = 0x12 + I_KCOLORS"[3].r", // K3_R = 0x13 + I_KCOLORS"[0].g", // K0_G = 0x14 + I_KCOLORS"[1].g", // K1_G = 0x15 + I_KCOLORS"[2].g", // K2_G = 0x16 + I_KCOLORS"[3].g", // K3_G = 0x17 + I_KCOLORS"[0].b", // K0_B = 0x18 + I_KCOLORS"[1].b", // K1_B = 0x19 + I_KCOLORS"[2].b", // K2_B = 0x1A + I_KCOLORS"[3].b", // K3_B = 0x1B + I_KCOLORS"[0].a", // K0_A = 0x1C + I_KCOLORS"[1].a", // K1_A = 0x1D + I_KCOLORS"[2].a", // K2_A = 0x1E + I_KCOLORS"[3].a", // K3_A = 0x1F +}; + +static const char *tevScaleTable[] = // CS +{ + "1.0f", // SCALE_1 + "2.0f", // SCALE_2 + "4.0f", // SCALE_4 + "0.5f", // DIVIDE_2 +}; + +static const char *tevBiasTable[] = // TB +{ + "", // ZERO, + "+0.5f", // ADDHALF, + "-0.5f", // SUBHALF, + "", +}; + +static const char *tevOpTable[] = { // TEV + "+", // TEVOP_ADD = 0, + "-", // TEVOP_SUB = 1, +}; + +static const char *tevCInputTable[] = // CC +{ + "(prev.rgb)", // CPREV, + "(prev.aaa)", // APREV, + "(c0.rgb)", // C0, + "(c0.aaa)", // A0, + "(c1.rgb)", // C1, + "(c1.aaa)", // A1, + "(c2.rgb)", // C2, + "(c2.aaa)", // A2, + "(textemp.rgb)", // TEXC, + "(textemp.aaa)", // TEXA, + "(rastemp.rgb)", // RASC, + "(rastemp.aaa)", // RASA, + "float3(1.0f, 1.0f, 1.0f)", // ONE + "float3(0.5f, 0.5f, 0.5f)", // HALF + "(konsttemp.rgb)", //"konsttemp.rgb", // KONST + "float3(0.0f, 0.0f, 0.0f)", // ZERO + ///aded extra values to map clamped values + "(cprev.rgb)", // CPREV, + "(cprev.aaa)", // APREV, + "(cc0.rgb)", // C0, + "(cc0.aaa)", // A0, + "(cc1.rgb)", // C1, + "(cc1.aaa)", // A1, + "(cc2.rgb)", // C2, + "(cc2.aaa)", // A2, + "(textemp.rgb)", // TEXC, + "(textemp.aaa)", // TEXA, + "(crastemp.rgb)", // RASC, + "(crastemp.aaa)", // RASA, + "float3(1.0f, 1.0f, 1.0f)", // ONE + "float3(0.5f, 0.5f, 0.5f)", // HALF + "(ckonsttemp.rgb)", //"konsttemp.rgb", // KONST + "float3(0.0f, 0.0f, 0.0f)", // ZERO + "PADERROR", "PADERROR", "PADERROR", "PADERROR" +}; + +static const char *tevAInputTable[] = // CA +{ + "prev", // APREV, + "c0", // A0, + "c1", // A1, + "c2", // A2, + "textemp", // TEXA, + "rastemp", // RASA, + "konsttemp", // KONST, (hw1 had quarter) + "float4(0.0f, 0.0f, 0.0f, 0.0f)", // ZERO + ///aded extra values to map clamped values + "cprev", // APREV, + "cc0", // A0, + "cc1", // A1, + "cc2", // A2, + "textemp", // TEXA, + "crastemp", // RASA, + "ckonsttemp", // KONST, (hw1 had quarter) + "float4(0.0f, 0.0f, 0.0f, 0.0f)", // ZERO + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", +}; + +static const char *tevRasTable[] = +{ + "colors_0", + "colors_1", + "ERROR", //2 + "ERROR", //3 + "ERROR", //4 + "alphabump", // use bump alpha + "(alphabump*(255.0f/248.0f))", //normalized + "float4(0.0f, 0.0f, 0.0f, 0.0f)", // zero +}; + +//static const char *tevTexFunc[] = { "tex2D", "texRECT" }; + +static const char *tevCOutputTable[] = { "prev.rgb", "c0.rgb", "c1.rgb", "c2.rgb" }; +static const char *tevAOutputTable[] = { "prev.a", "c0.a", "c1.a", "c2.a" }; +static const char *tevIndAlphaSel[] = {"", "x", "y", "z"}; +//static const char *tevIndAlphaScale[] = {"", "*32", "*16", "*8"}; +static const char *tevIndAlphaScale[] = {"*(248.0f/255.0f)", "*(224.0f/255.0f)", "*(240.0f/255.0f)", "*(248.0f/255.0f)"}; +static const char *tevIndBiasField[] = {"", "x", "y", "xy", "z", "xz", "yz", "xyz"}; // indexed by bias +static const char *tevIndBiasAdd[] = {"-128.0f", "1.0f", "1.0f", "1.0f" }; // indexed by fmt +static const char *tevIndWrapStart[] = {"0.0f", "256.0f", "128.0f", "64.0f", "32.0f", "16.0f", "0.001f" }; +static const char *tevIndFmtScale[] = {"255.0f", "31.0f", "15.0f", "7.0f" }; + +#define WRITE p+=sprintf + +static char swapModeTable[4][5]; + +static char text[16384]; +static bool DepthTextureEnable; + +static void BuildSwapModeTable() +{ + static const char *swapColors = "rgba"; + for (int i = 0; i < 4; i++) + { + swapModeTable[i][0] = swapColors[bpmem.tevksel[i*2].swap1]; + swapModeTable[i][1] = swapColors[bpmem.tevksel[i*2].swap2]; + swapModeTable[i][2] = swapColors[bpmem.tevksel[i*2+1].swap1]; + swapModeTable[i][3] = swapColors[bpmem.tevksel[i*2+1].swap2]; + swapModeTable[i][4] = 0; + } +} + +const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType, u32 components) +{ + setlocale(LC_NUMERIC, "C"); // Reset locale for compilation + text[sizeof(text) - 1] = 0x7C; // canary + + BuildSwapModeTable(); // Needed for WriteStage + int numStages = bpmem.genMode.numtevstages + 1; + int numTexgen = bpmem.genMode.numtexgens; + + char *p = text; + WRITE(p, "//Pixel Shader for TEV stages\n"); + WRITE(p, "//%i TEV stages, %i texgens, %i IND stages\n", + numStages, numTexgen, bpmem.genMode.numindstages); + + int nIndirectStagesUsed = 0; + if (bpmem.genMode.numindstages > 0) + { + for (int i = 0; i < numStages; ++i) + { + if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) + nIndirectStagesUsed |= 1 << bpmem.tevind[i].bt; + } + } + DepthTextureEnable = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) || g_ActiveConfig.bEnablePerPixelDepth ; + // Declare samplers + + if(ApiType != API_D3D11) + { + WRITE(p, "uniform sampler2D "); + } + else + { + WRITE(p, "sampler "); + } + + bool bfirst = true; + for (int i = 0; i < 8; ++i) + { + WRITE(p, "%s samp%d : register(s%d)", bfirst?"":",", i, i); + bfirst = false; + } + WRITE(p, ";\n"); + if(ApiType == API_D3D11) + { + WRITE(p, "Texture2D "); + bfirst = true; + for (int i = 0; i < 8; ++i) + { + WRITE(p, "%s Tex%d : register(t%d)", bfirst?"":",", i, i); + bfirst = false; + } + WRITE(p, ";\n"); + } + + WRITE(p, "\n"); + + WRITE(p, "uniform float4 "I_COLORS"[4] : register(c%d);\n", C_COLORS); + WRITE(p, "uniform float4 "I_KCOLORS"[4] : register(c%d);\n", C_KCOLORS); + WRITE(p, "uniform float4 "I_ALPHA"[1] : register(c%d);\n", C_ALPHA); + WRITE(p, "uniform float4 "I_TEXDIMS"[8] : register(c%d);\n", C_TEXDIMS); + WRITE(p, "uniform float4 "I_ZBIAS"[2] : register(c%d);\n", C_ZBIAS); + WRITE(p, "uniform float4 "I_INDTEXSCALE"[2] : register(c%d);\n", C_INDTEXSCALE); + WRITE(p, "uniform float4 "I_INDTEXMTX"[6] : register(c%d);\n", C_INDTEXMTX); + WRITE(p, "uniform float4 "I_FOG"[3] : register(c%d);\n", C_FOG); + + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + { + WRITE(p,"typedef struct { float4 col; float4 cosatt; float4 distatt; float4 pos; float4 dir; } Light;\n"); + WRITE(p,"typedef struct { Light lights[8]; } s_"I_PLIGHTS";\n"); + WRITE(p, "uniform s_"I_PLIGHTS" "I_PLIGHTS" : register(c%d);\n", C_PLIGHTS); + WRITE(p, "typedef struct { float4 C0, C1, C2, C3; } s_"I_PMATERIALS";\n"); + WRITE(p, "uniform s_"I_PMATERIALS" "I_PMATERIALS" : register(c%d);\n", C_PMATERIALS); + } + + WRITE(p, "void main(\n"); + if(ApiType != API_D3D11) + { + WRITE(p, " out float4 ocol0 : COLOR0,%s%s\n in float4 rawpos : %s,\n", + dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "\n out float4 ocol1 : COLOR1," : "", + DepthTextureEnable ? "\n out float depth : DEPTH," : "", + ApiType & API_OPENGL ? "WPOS" : ApiType & API_D3D9_SM20 ? "POSITION" : "VPOS"); + } + else + { + WRITE(p, " out float4 ocol0 : SV_Target0,%s%s\n in float4 rawpos : SV_Position,\n", + dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "\n out float4 ocol1 : SV_Target1," : "", + DepthTextureEnable ? "\n out float depth : SV_Depth," : ""); + } + + WRITE(p, " in float4 colors_0 : COLOR0,\n"); + WRITE(p, " in float4 colors_1 : COLOR1"); + + // compute window position if needed because binding semantic WPOS is not widely supported + if (numTexgen < 7) + { + for (int i = 0; i < numTexgen; ++i) + WRITE(p, ",\n in float3 uv%d : TEXCOORD%d", i, i); + WRITE(p, ",\n in float4 clipPos : TEXCOORD%d", numTexgen); + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + WRITE(p, ",\n in float4 Normal : TEXCOORD%d", numTexgen + 1); + } + else + { + // wpos is in w of first 4 texcoords + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + { + for (int i = 0; i < 8; ++i) + WRITE(p, ",\n in float4 uv%d : TEXCOORD%d", i, i); + } + else + { + for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) + WRITE(p, ",\n in float%d uv%d : TEXCOORD%d", i < 4 ? 4 : 3 , i, i); + } + } + WRITE(p, " ) {\n"); + + char* pmainstart = p; + int Pretest = AlphaPreTest(); + if (dstAlphaMode == DSTALPHA_ALPHA_PASS && !DepthTextureEnable && Pretest >= 0) + { + if (!Pretest) + { + // alpha test will always fail, so restart the shader and just make it an empty function + WRITE(p, "ocol0 = 0;\n"); + WRITE(p, "discard;\n"); + if(ApiType != API_D3D11) + WRITE(p, "return;\n"); + } + else + { + WRITE(p, " ocol0 = "I_ALPHA"[0].aaaa;\n"); + } + WRITE(p, "}\n"); + return text; + } + + WRITE(p, " float4 c0 = "I_COLORS"[1], c1 = "I_COLORS"[2], c2 = "I_COLORS"[3], prev = float4(0.0f, 0.0f, 0.0f, 0.0f), textemp = float4(0.0f, 0.0f, 0.0f, 0.0f), rastemp = float4(0.0f, 0.0f, 0.0f, 0.0f), konsttemp = float4(0.0f, 0.0f, 0.0f, 0.0f);\n" + " float3 comp16 = float3(1.0f, 255.0f, 0.0f), comp24 = float3(1.0f, 255.0f, 255.0f*255.0f);\n" + " float4 alphabump=0;\n" + " float3 tevcoord;\n" + " float2 wrappedcoord, tempcoord;\n" + " float4 cc0, cc1, cc2, cprev,crastemp,ckonsttemp;\n\n"); + + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + { + if (xfregs.numTexGen.numTexGens < 7) + { + WRITE(p,"float3 _norm0 = normalize(Normal.xyz);\n\n"); + WRITE(p,"float3 pos = float3(clipPos.x,clipPos.y,Normal.w);\n"); + } + else + { + WRITE(p," float3 _norm0 = normalize(float3(uv4.w,uv5.w,uv6.w));\n\n"); + WRITE(p,"float3 pos = float3(uv0.w,uv1.w,uv7.w);\n"); + } + + + WRITE(p, "float4 mat, lacc;\n" + "float3 ldir, h;\n" + "float dist, dist2, attn;\n"); + + p = GenerateLightingShader(p, components, I_PMATERIALS, I_PLIGHTS, "colors_", "colors_"); + } + + if (numTexgen < 7) + WRITE(p, "clipPos = float4(rawpos.x, rawpos.y, clipPos.z, clipPos.w);\n"); + else + WRITE(p, "float4 clipPos = float4(rawpos.x, rawpos.y, uv2.w, uv3.w);\n"); + + // HACK to handle cases where the tex gen is not enabled + if (numTexgen == 0) + { + WRITE(p, "float3 uv0 = float3(0.0f, 0.0f, 0.0f);\n"); + } + else + { + for (int i = 0; i < numTexgen; ++i) + { + // optional perspective divides + if (xfregs.texMtxInfo[i].projection == XF_TEXPROJ_STQ) + { + WRITE(p, "if (uv%d.z)", i); + WRITE(p, " uv%d.xy = uv%d.xy / uv%d.z;\n", i, i, i); + } + + WRITE(p, "uv%d.xy = uv%d.xy * "I_TEXDIMS"[%d].zw;\n", i, i, i); + } + } + + // indirect texture map lookup + for(u32 i = 0; i < bpmem.genMode.numindstages; ++i) + { + if (nIndirectStagesUsed & (1<= %s.r + (0.25f/255.0f)) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_R8_GT 8 - " %s + ((abs(%s.r - %s.r) < (0.5f/255.0f)) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_R8_EQ 9 - " %s + (( dot(%s.rgb, comp16) >= (dot(%s.rgb, comp16) + (0.25f/255.0f))) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_GR16_GT 10 - " %s + (abs(dot(%s.rgb, comp16) - dot(%s.rgb, comp16)) < (0.5f/255.0f) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_GR16_EQ 11 - " %s + (( dot(%s.rgb, comp24) >= (dot(%s.rgb, comp24) + (0.25f/255.0f))) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_BGR24_GT 12 - " %s + (abs(dot(%s.rgb, comp24) - dot(%s.rgb, comp24)) < (0.5f/255.0f) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_BGR24_EQ 13 - " %s + (max(sign(%s.rgb - %s.rgb - (0.25f/255.0f)), float3(0.0f, 0.0f, 0.0f)) * %s)",//#define TEVCMP_RGB8_GT 14 - " %s + ((float3(1.0f, 1.0f, 1.0f) - max(sign(abs(%s.rgb - %s.rgb) - (0.5f/255.0f)), float3(0.0f, 0.0f, 0.0f))) * %s)"//#define TEVCMP_RGB8_EQ 15 -}; - -//table with the alpha compare operations -static const char *TEVCMPAlphaOPTable[16] = -{ - "0.0f",//0 - "0.0f",//1 - "0.0f",//2 - "0.0f",//3 - "0.0f",//4 - "0.0f",//5 - "0.0f",//6 - "0.0f",//7 - " %s.a + ((%s.r >= (%s.r + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_R8_GT 8 - " %s.a + (abs(%s.r - %s.r) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_R8_EQ 9 - " %s.a + ((dot(%s.rgb, comp16) >= (dot(%s.rgb, comp16) + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_GR16_GT 10 - " %s.a + (abs(dot(%s.rgb, comp16) - dot(%s.rgb, comp16)) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_GR16_EQ 11 - " %s.a + ((dot(%s.rgb, comp24) >= (dot(%s.rgb, comp24) + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_BGR24_GT 12 - " %s.a + (abs(dot(%s.rgb, comp24) - dot(%s.rgb, comp24)) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_BGR24_EQ 13 - " %s.a + ((%s.a >= (%s.a + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_A8_GT 14 - " %s.a + (abs(%s.a - %s.a) < (0.5f/255.0f) ? %s.a : 0.0f)"//#define TEVCMP_A8_EQ 15 - -}; - - -static void WriteStage(char *&p, int n, API_TYPE ApiType) -{ - int texcoord = bpmem.tevorders[n/2].getTexCoord(n&1); - bool bHasTexCoord = (u32)texcoord < bpmem.genMode.numtexgens; - bool bHasIndStage = bpmem.tevind[n].IsActive() && bpmem.tevind[n].bt < bpmem.genMode.numindstages; - - // HACK to handle cases where the tex gen is not enabled - if (!bHasTexCoord) - texcoord = 0; - - if (bHasIndStage) - { - // perform the indirect op on the incoming regular coordinates using indtex%d as the offset coords - if (bpmem.tevind[n].bs != ITBA_OFF) - { - WRITE(p, "alphabump = indtex%d.%s %s;\n", - bpmem.tevind[n].bt, - tevIndAlphaSel[bpmem.tevind[n].bs], - tevIndAlphaScale[bpmem.tevind[n].fmt]); - } - // format - WRITE(p, "float3 indtevcrd%d = indtex%d * %s;\n", n, bpmem.tevind[n].bt, tevIndFmtScale[bpmem.tevind[n].fmt]); - - // bias - if (bpmem.tevind[n].bias != ITB_NONE ) - WRITE(p, "indtevcrd%d.%s += %s;\n", n, tevIndBiasField[bpmem.tevind[n].bias], tevIndBiasAdd[bpmem.tevind[n].fmt]); - - // multiply by offset matrix and scale - if (bpmem.tevind[n].mid != 0) - { - if (bpmem.tevind[n].mid <= 3) - { - int mtxidx = 2*(bpmem.tevind[n].mid-1); - WRITE(p, "float2 indtevtrans%d = float2(dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d), dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d));\n", - n, mtxidx, n, mtxidx+1, n); - } - else if (bpmem.tevind[n].mid <= 7 && bHasTexCoord) - { // s matrix - // TODO: Might become negative? - int mtxidx = 2*(bpmem.tevind[n].mid-5); - WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.xx;\n", n, mtxidx, texcoord, n); - } - else if (bpmem.tevind[n].mid <= 11 && bHasTexCoord) - { // t matrix - // TODO: Might become negative? - int mtxidx = 2*(bpmem.tevind[n].mid-9); - WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.yy;\n", n, mtxidx, texcoord, n); - } - else - WRITE(p, "float2 indtevtrans%d = 0;\n", n); - } - else - WRITE(p, "float2 indtevtrans%d = 0;\n", n); - - // --------- - // Wrapping - // --------- - - // wrap S - if (bpmem.tevind[n].sw == ITW_OFF) - WRITE(p, "wrappedcoord.x = uv%d.x;\n", texcoord); - else if (bpmem.tevind[n].sw == ITW_0) - WRITE(p, "wrappedcoord.x = 0.0f;\n"); - else - WRITE(p, "wrappedcoord.x = fmod( uv%d.x, %s );\n", texcoord, tevIndWrapStart[bpmem.tevind[n].sw]); - - // wrap T - if (bpmem.tevind[n].tw == ITW_OFF) - WRITE(p, "wrappedcoord.y = uv%d.y;\n", texcoord); - else if (bpmem.tevind[n].tw == ITW_0) - WRITE(p, "wrappedcoord.y = 0.0f;\n"); - else - WRITE(p, "wrappedcoord.y = fmod( uv%d.y, %s );\n", texcoord, tevIndWrapStart[bpmem.tevind[n].tw]); - - if (bpmem.tevind[n].fb_addprev) // add previous tevcoord - WRITE(p, "tevcoord.xy += wrappedcoord + indtevtrans%d;\n", n); - else - WRITE(p, "tevcoord.xy = wrappedcoord + indtevtrans%d;\n", n); - } - - TevStageCombiner::ColorCombiner &cc = bpmem.combiners[n].colorC; - TevStageCombiner::AlphaCombiner &ac = bpmem.combiners[n].alphaC; - - if(cc.a == TEVCOLORARG_RASA || cc.a == TEVCOLORARG_RASC - || cc.b == TEVCOLORARG_RASA || cc.b == TEVCOLORARG_RASC - || cc.c == TEVCOLORARG_RASA || cc.c == TEVCOLORARG_RASC - || cc.d == TEVCOLORARG_RASA || cc.d == TEVCOLORARG_RASC - || ac.a == TEVALPHAARG_RASA || ac.b == TEVALPHAARG_RASA - || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA) - { - char *rasswap = swapModeTable[bpmem.combiners[n].alphaC.rswap]; - WRITE(p, "rastemp = %s.%s;\n", tevRasTable[bpmem.tevorders[n / 2].getColorChan(n & 1)], rasswap); - WRITE(p, "crastemp = frac(rastemp * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - } - - - if (bpmem.tevorders[n/2].getEnable(n&1)) - { - if(!bHasIndStage) - { - // calc tevcord - if(bHasTexCoord) - WRITE(p, "tevcoord.xy = uv%d.xy;\n", texcoord); - else - WRITE(p, "tevcoord.xy = float2(0.0f, 0.0f);\n"); - } - - char *texswap = swapModeTable[bpmem.combiners[n].alphaC.tswap]; - int texmap = bpmem.tevorders[n/2].getTexMap(n&1); - SampleTexture(p, "textemp", "tevcoord", texswap, texmap, ApiType); - } - else - WRITE(p, "textemp = float4(1.0f, 1.0f, 1.0f, 1.0f);\n"); - - - if (cc.a == TEVCOLORARG_KONST || cc.b == TEVCOLORARG_KONST || cc.c == TEVCOLORARG_KONST || cc.d == TEVCOLORARG_KONST - || ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST || ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST) - { - int kc = bpmem.tevksel[n / 2].getKC(n & 1); - int ka = bpmem.tevksel[n / 2].getKA(n & 1); - WRITE(p, "konsttemp = float4(%s, %s);\n", tevKSelTableC[kc], tevKSelTableA[ka]); - if(kc > 7 || ka > 7) - { - WRITE(p, "ckonsttemp = frac(konsttemp * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - } - else - { - WRITE(p, "ckonsttemp = konsttemp;\n"); - } - } - - if(cc.a == TEVCOLORARG_CPREV || cc.a == TEVCOLORARG_APREV - || cc.b == TEVCOLORARG_CPREV || cc.b == TEVCOLORARG_APREV - || cc.c == TEVCOLORARG_CPREV || cc.c == TEVCOLORARG_APREV - || ac.a == TEVALPHAARG_APREV || ac.b == TEVALPHAARG_APREV || ac.c == TEVALPHAARG_APREV) - WRITE(p, "cprev = frac(prev * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - - - if(cc.a == TEVCOLORARG_C0 || cc.a == TEVCOLORARG_A0 - || cc.b == TEVCOLORARG_C0 || cc.b == TEVCOLORARG_A0 - || cc.c == TEVCOLORARG_C0 || cc.c == TEVCOLORARG_A0 - || ac.a == TEVALPHAARG_A0 || ac.b == TEVALPHAARG_A0 || ac.c == TEVALPHAARG_A0) - WRITE(p, "cc0 = frac(c0 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - - - if(cc.a == TEVCOLORARG_C1 || cc.a == TEVCOLORARG_A1 - || cc.b == TEVCOLORARG_C1 || cc.b == TEVCOLORARG_A1 - || cc.c == TEVCOLORARG_C1 || cc.c == TEVCOLORARG_A1 - || ac.a == TEVALPHAARG_A1 || ac.b == TEVALPHAARG_A1 || ac.c == TEVALPHAARG_A1) - WRITE(p, "cc1 = frac(c1 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - - - if(cc.a == TEVCOLORARG_C2 || cc.a == TEVCOLORARG_A2 - || cc.b == TEVCOLORARG_C2 || cc.b == TEVCOLORARG_A2 - || cc.c == TEVCOLORARG_C2 || cc.c == TEVCOLORARG_A2 - || ac.a == TEVALPHAARG_A2 || ac.b == TEVALPHAARG_A2 || ac.c == TEVALPHAARG_A2) - WRITE(p, "cc2 = frac(c2 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - - - if (cc.clamp) - WRITE(p, "%s = saturate(", tevCOutputTable[cc.dest]); - else - WRITE(p, "%s = ", tevCOutputTable[cc.dest]); - - // combine the color channel - if (cc.bias != TevBias_COMPARE) // if not compare - { - //normal color combiner goes here - if (cc.shift > TEVSCALE_1) - WRITE(p, "%s*(", tevScaleTable[cc.shift]); - - if(!(cc.d == TEVCOLORARG_ZERO && cc.op == TEVOP_ADD)) - WRITE(p, "%s%s", tevCInputTable[cc.d], tevOpTable[cc.op]); - - if (cc.a == cc.b) - WRITE(p, "%s", tevCInputTable[cc.a + 16]); - else if (cc.c == TEVCOLORARG_ZERO) - WRITE(p, "%s", tevCInputTable[cc.a + 16]); - else if (cc.c == TEVCOLORARG_ONE) - WRITE(p, "%s", tevCInputTable[cc.b + 16]); - else if (cc.a == TEVCOLORARG_ZERO) - WRITE(p, "%s*%s", tevCInputTable[cc.b + 16], tevCInputTable[cc.c + 16]); - else if (cc.b == TEVCOLORARG_ZERO) - WRITE(p, "%s*(float3(1.0f, 1.0f, 1.0f)-%s)", tevCInputTable[cc.a + 16], tevCInputTable[cc.c + 16]); - else - WRITE(p, "lerp(%s, %s, %s)", tevCInputTable[cc.a + 16], tevCInputTable[cc.b + 16], tevCInputTable[cc.c + 16]); - - WRITE(p, "%s", tevBiasTable[cc.bias]); - - if (cc.shift > TEVSCALE_1) - WRITE(p, ")"); - } - else - { - int cmp = (cc.shift<<1)|cc.op|8; // comparemode stored here - WRITE(p, TEVCMPColorOPTable[cmp],//lookup the function from the op table - tevCInputTable[cc.d], - tevCInputTable[cc.a + 16], - tevCInputTable[cc.b + 16], - tevCInputTable[cc.c + 16]); - } - if (cc.clamp) - WRITE(p, ")"); - WRITE(p,";\n"); - - // combine the alpha channel - if (ac.clamp) - WRITE(p, "%s = saturate(", tevAOutputTable[ac.dest]); - else - WRITE(p, "%s = ", tevAOutputTable[ac.dest]); - - if (ac.bias != TevBias_COMPARE) // if not compare - { - //normal alpha combiner goes here - if (ac.shift > TEVSCALE_1) - WRITE(p, "%s*(", tevScaleTable[ac.shift]); - - if(!(ac.d == TEVALPHAARG_ZERO && ac.op == TEVOP_ADD)) - WRITE(p, "%s.a%s", tevAInputTable[ac.d], tevOpTable[ac.op]); - - if (ac.a == ac.b) - WRITE(p, "%s.a", tevAInputTable[ac.a + 8]); - else if (ac.c == TEVALPHAARG_ZERO) - WRITE(p, "%s.a", tevAInputTable[ac.a + 8]); - else if (ac.a == TEVALPHAARG_ZERO) - WRITE(p, "%s.a*%s.a", tevAInputTable[ac.b + 8], tevAInputTable[ac.c + 8]); - else if (ac.b == TEVALPHAARG_ZERO) - WRITE(p, "%s.a*(1.0f-%s.a)", tevAInputTable[ac.a + 8], tevAInputTable[ac.c + 8]); - else - WRITE(p, "lerp(%s.a, %s.a, %s.a)", tevAInputTable[ac.a + 8], tevAInputTable[ac.b + 8], tevAInputTable[ac.c + 8]); - - WRITE(p, "%s",tevBiasTable[ac.bias]); - - if (ac.shift>0) - WRITE(p, ")"); - - } - else - { - //compare alpha combiner goes here - int cmp = (ac.shift<<1)|ac.op|8; // comparemode stored here - WRITE(p, TEVCMPAlphaOPTable[cmp], - tevAInputTable[ac.d], - tevAInputTable[ac.a + 8], - tevAInputTable[ac.b + 8], - tevAInputTable[ac.c + 8]); - } - if (ac.clamp) - WRITE(p, ")"); - WRITE(p, ";\n\n"); -} - -void SampleTexture(char *&p, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType) -{ - if (ApiType == API_D3D11) - WRITE(p, "%s=Tex%d.Sample(samp%d,%s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap,texmap, texcoords, texmap, texswap); - else - WRITE(p, "%s=tex2D(samp%d,%s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap, texcoords, texmap, texswap); -} - -static const char *tevAlphaFuncsTable[] = -{ - "(false)", //ALPHACMP_NEVER 0 - "(prev.a <= %s - (0.25f/255.0f))", //ALPHACMP_LESS 1 - "(abs( prev.a - %s ) < (0.5f/255.0f))", //ALPHACMP_EQUAL 2 - "(prev.a < %s + (0.25f/255.0f))", //ALPHACMP_LEQUAL 3 - "(prev.a >= %s + (0.25f/255.0f))", //ALPHACMP_GREATER 4 - "(abs( prev.a - %s ) >= (0.5f/255.0f))", //ALPHACMP_NEQUAL 5 - "(prev.a > %s - (0.25f/255.0f))", //ALPHACMP_GEQUAL 6 - "(true)" //ALPHACMP_ALWAYS 7 -}; - -static const char *tevAlphaFunclogicTable[] = -{ - " && ", // and - " || ", // or - " != ", // xor - " == " // xnor -}; -static int AlphaPreTest() -{ - u32 op = bpmem.alphaFunc.logic; - u32 comp[2] = {bpmem.alphaFunc.comp0, bpmem.alphaFunc.comp1}; - - // First kill all the simple cases - switch(op) - { - case 0: // AND - if (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) return true; - if (comp[0] == ALPHACMP_NEVER || comp[1] == ALPHACMP_NEVER) return false; - break; - case 1: // OR - if (comp[0] == ALPHACMP_ALWAYS || comp[1] == ALPHACMP_ALWAYS) return true; - if (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)return false; - break; - case 2: // XOR - if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS)) - return true; - if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)) - return false; - break; - case 3: // XNOR - if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS)) - return false; - if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)) - return true; - break; - default: PanicAlert("bad logic for alpha test? %08x", op); - } - return -1; -} - - -static bool WriteAlphaTest(char *&p, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode) -{ - static const char *alphaRef[2] = - { - I_ALPHA"[0].r", - I_ALPHA"[0].g" - }; - - int Pretest = AlphaPreTest(); - if(Pretest >= 0) - { - return Pretest != 0; - } - - // using discard then return works the same in cg and dx9 but not in dx11 - WRITE(p, "if(!( "); - - int compindex = bpmem.alphaFunc.comp0 % 8; - WRITE(p, tevAlphaFuncsTable[compindex],alphaRef[0]);//lookup the first component from the alpha function table - - WRITE(p, "%s", tevAlphaFunclogicTable[bpmem.alphaFunc.logic % 4]);//lookup the logic op - - compindex = bpmem.alphaFunc.comp1 % 8; - WRITE(p, tevAlphaFuncsTable[compindex],alphaRef[1]);//lookup the second component from the alpha function table - WRITE(p, ")){ocol0 = 0;%s%s discard;%s}\n", - dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "ocol1 = 0;" : "", - DepthTextureEnable ? "depth = 1.f;" : "", - (ApiType != API_D3D11) ? "return;" : ""); - return true; -} - -static const char *tevFogFuncsTable[] = -{ - "", //No Fog - "", //? - "", //Linear - "", //? - " fog = 1.0f - pow(2.0f, -8.0f * fog);\n", //exp - " fog = 1.0f - pow(2.0f, -8.0f * fog * fog);\n", //exp2 - " fog = pow(2.0f, -8.0f * (1.0f - fog));\n", //backward exp - " fog = 1.0f - fog;\n fog = pow(2.0f, -8.0f * fog * fog);\n" //backward exp2 -}; - -static void WriteFog(char *&p) -{ - if(bpmem.fog.c_proj_fsel.fsel == 0)return;//no Fog - - if (bpmem.fog.c_proj_fsel.proj == 0) - { - // perspective - // ze = A/(B - (Zs >> B_SHF) - WRITE (p, " float ze = "I_FOG"[1].x / ("I_FOG"[1].y - (zCoord / "I_FOG"[1].w));\n"); - } - else - { - // orthographic - // ze = a*Zs (here, no B_SHF) - WRITE (p, " float ze = "I_FOG"[1].x * zCoord;\n"); - } - - // x_adjust = sqrt((x-center)^2 + k^2)/k - // ze *= x_adjust - //this is complitly teorical as the real hard seems to use a table intead of calculate the values. - if(bpmem.fogRange.Base.Enabled) - { - WRITE (p, " float x_adjust = (2.0f * (clipPos.x / "I_FOG"[2].y)) - 1.0f - "I_FOG"[2].x;\n"); - WRITE (p, " x_adjust = sqrt(x_adjust * x_adjust + "I_FOG"[2].z * "I_FOG"[2].z) / "I_FOG"[2].z;\n"); - WRITE (p, " ze *= x_adjust;\n"); - } - - WRITE (p, " float fog = saturate(ze - "I_FOG"[1].z);\n"); - - if(bpmem.fog.c_proj_fsel.fsel > 3) - { - WRITE(p, "%s", tevFogFuncsTable[bpmem.fog.c_proj_fsel.fsel]); - } - else - { - if(bpmem.fog.c_proj_fsel.fsel != 2) - WARN_LOG(VIDEO, "Unknown Fog Type! %08x", bpmem.fog.c_proj_fsel.fsel); - } - - WRITE(p, " prev.rgb = lerp(prev.rgb,"I_FOG"[0].rgb,fog);\n"); - - + WRITE(p, "prev = frac(4.0f + prev * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + + // TODO: Why are we doing a second alpha pretest here? + if (!WriteAlphaTest(p, ApiType, dstAlphaMode)) + { + // alpha test will always fail, so restart the shader and just make it an empty function + p = pmainstart; + WRITE(p, "ocol0 = 0;\n"); + if(DepthTextureEnable) + WRITE(p, "depth = 1.f;\n"); + if(dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND) + WRITE(p, "ocol1 = 0;\n"); + WRITE(p, "discard;\n"); + if(ApiType != API_D3D11) + WRITE(p, "return;\n"); + } + else + { + if((bpmem.fog.c_proj_fsel.fsel != 0) || DepthTextureEnable) + { + + // the screen space depth value = far z + (clip z / clip w) * z range + WRITE(p, "float zCoord = "I_ZBIAS"[1].x + (clipPos.z / clipPos.w) * "I_ZBIAS"[1].y;\n"); + } + + if (DepthTextureEnable) + { + // use the texture input of the last texture stage (textemp), hopefully this has been read and is in correct format... + if (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) + { + if (bpmem.ztex2.op == ZTEXTURE_ADD) + WRITE(p, "zCoord = dot("I_ZBIAS"[0].xyzw, textemp.xyzw) + "I_ZBIAS"[1].w + zCoord;\n"); + else + WRITE(p, "zCoord = dot("I_ZBIAS"[0].xyzw, textemp.xyzw) + "I_ZBIAS"[1].w;\n"); + + // scale to make result from frac correct + WRITE(p, "zCoord = zCoord * (16777215.0f/16777216.0f);\n"); + WRITE(p, "zCoord = frac(zCoord);\n"); + WRITE(p, "zCoord = zCoord * (16777216.0f/16777215.0f);\n"); + } + WRITE(p, "depth = zCoord;\n"); + } + + if (dstAlphaMode == DSTALPHA_ALPHA_PASS) + WRITE(p, " ocol0 = float4(prev.rgb, "I_ALPHA"[0].a);\n"); + else + { + WriteFog(p); + WRITE(p, " ocol0 = prev;\n"); + } + + // On D3D11, use dual-source color blending to perform dst alpha in a + // single pass + if (dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND) + { + // Colors will be blended against the alpha from ocol1... + WRITE(p, " ocol1 = ocol0;\n"); + // ...and the alpha from ocol0 will be written to the framebuffer. + WRITE(p, " ocol0.a = "I_ALPHA"[0].a;\n"); + } + } + WRITE(p, "}\n"); + if (text[sizeof(text) - 1] != 0x7C) + PanicAlert("PixelShader generator - buffer too small, canary has been eaten!"); + + setlocale(LC_NUMERIC, ""); // restore locale + return text; +} + + + +//table with the color compare operations +static const char *TEVCMPColorOPTable[16] = +{ + "float3(0.0f, 0.0f, 0.0f)",//0 + "float3(0.0f, 0.0f, 0.0f)",//1 + "float3(0.0f, 0.0f, 0.0f)",//2 + "float3(0.0f, 0.0f, 0.0f)",//3 + "float3(0.0f, 0.0f, 0.0f)",//4 + "float3(0.0f, 0.0f, 0.0f)",//5 + "float3(0.0f, 0.0f, 0.0f)",//6 + "float3(0.0f, 0.0f, 0.0f)",//7 + " %s + ((%s.r >= %s.r + (0.25f/255.0f)) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_R8_GT 8 + " %s + ((abs(%s.r - %s.r) < (0.5f/255.0f)) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_R8_EQ 9 + " %s + (( dot(%s.rgb, comp16) >= (dot(%s.rgb, comp16) + (0.25f/255.0f))) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_GR16_GT 10 + " %s + (abs(dot(%s.rgb, comp16) - dot(%s.rgb, comp16)) < (0.5f/255.0f) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_GR16_EQ 11 + " %s + (( dot(%s.rgb, comp24) >= (dot(%s.rgb, comp24) + (0.25f/255.0f))) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_BGR24_GT 12 + " %s + (abs(dot(%s.rgb, comp24) - dot(%s.rgb, comp24)) < (0.5f/255.0f) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_BGR24_EQ 13 + " %s + (max(sign(%s.rgb - %s.rgb - (0.25f/255.0f)), float3(0.0f, 0.0f, 0.0f)) * %s)",//#define TEVCMP_RGB8_GT 14 + " %s + ((float3(1.0f, 1.0f, 1.0f) - max(sign(abs(%s.rgb - %s.rgb) - (0.5f/255.0f)), float3(0.0f, 0.0f, 0.0f))) * %s)"//#define TEVCMP_RGB8_EQ 15 +}; + +//table with the alpha compare operations +static const char *TEVCMPAlphaOPTable[16] = +{ + "0.0f",//0 + "0.0f",//1 + "0.0f",//2 + "0.0f",//3 + "0.0f",//4 + "0.0f",//5 + "0.0f",//6 + "0.0f",//7 + " %s.a + ((%s.r >= (%s.r + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_R8_GT 8 + " %s.a + (abs(%s.r - %s.r) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_R8_EQ 9 + " %s.a + ((dot(%s.rgb, comp16) >= (dot(%s.rgb, comp16) + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_GR16_GT 10 + " %s.a + (abs(dot(%s.rgb, comp16) - dot(%s.rgb, comp16)) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_GR16_EQ 11 + " %s.a + ((dot(%s.rgb, comp24) >= (dot(%s.rgb, comp24) + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_BGR24_GT 12 + " %s.a + (abs(dot(%s.rgb, comp24) - dot(%s.rgb, comp24)) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_BGR24_EQ 13 + " %s.a + ((%s.a >= (%s.a + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_A8_GT 14 + " %s.a + (abs(%s.a - %s.a) < (0.5f/255.0f) ? %s.a : 0.0f)"//#define TEVCMP_A8_EQ 15 + +}; + + +static void WriteStage(char *&p, int n, API_TYPE ApiType) +{ + int texcoord = bpmem.tevorders[n/2].getTexCoord(n&1); + bool bHasTexCoord = (u32)texcoord < bpmem.genMode.numtexgens; + bool bHasIndStage = bpmem.tevind[n].IsActive() && bpmem.tevind[n].bt < bpmem.genMode.numindstages; + + // HACK to handle cases where the tex gen is not enabled + if (!bHasTexCoord) + texcoord = 0; + + WRITE(p, "// TEV stage %d\n", n); + + if (bHasIndStage) + { + WRITE(p, "// indirect op\n", n); + // perform the indirect op on the incoming regular coordinates using indtex%d as the offset coords + if (bpmem.tevind[n].bs != ITBA_OFF) + { + WRITE(p, "alphabump = indtex%d.%s %s;\n", + bpmem.tevind[n].bt, + tevIndAlphaSel[bpmem.tevind[n].bs], + tevIndAlphaScale[bpmem.tevind[n].fmt]); + } + // format + WRITE(p, "float3 indtevcrd%d = indtex%d * %s;\n", n, bpmem.tevind[n].bt, tevIndFmtScale[bpmem.tevind[n].fmt]); + + // bias + if (bpmem.tevind[n].bias != ITB_NONE ) + WRITE(p, "indtevcrd%d.%s += %s;\n", n, tevIndBiasField[bpmem.tevind[n].bias], tevIndBiasAdd[bpmem.tevind[n].fmt]); + + // multiply by offset matrix and scale + if (bpmem.tevind[n].mid != 0) + { + if (bpmem.tevind[n].mid <= 3) + { + int mtxidx = 2*(bpmem.tevind[n].mid-1); + WRITE(p, "float2 indtevtrans%d = float2(dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d), dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d));\n", + n, mtxidx, n, mtxidx+1, n); + } + else if (bpmem.tevind[n].mid <= 7 && bHasTexCoord) + { // s matrix + // TODO: Might become negative? + int mtxidx = 2*(bpmem.tevind[n].mid-5); + WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.xx;\n", n, mtxidx, texcoord, n); + } + else if (bpmem.tevind[n].mid <= 11 && bHasTexCoord) + { // t matrix + // TODO: Might become negative? + int mtxidx = 2*(bpmem.tevind[n].mid-9); + WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.yy;\n", n, mtxidx, texcoord, n); + } + else + WRITE(p, "float2 indtevtrans%d = 0;\n", n); + } + else + WRITE(p, "float2 indtevtrans%d = 0;\n", n); + + // --------- + // Wrapping + // --------- + + // wrap S + if (bpmem.tevind[n].sw == ITW_OFF) + WRITE(p, "wrappedcoord.x = uv%d.x;\n", texcoord); + else if (bpmem.tevind[n].sw == ITW_0) + WRITE(p, "wrappedcoord.x = 0.0f;\n"); + else + WRITE(p, "wrappedcoord.x = fmod( uv%d.x, %s );\n", texcoord, tevIndWrapStart[bpmem.tevind[n].sw]); + + // wrap T + if (bpmem.tevind[n].tw == ITW_OFF) + WRITE(p, "wrappedcoord.y = uv%d.y;\n", texcoord); + else if (bpmem.tevind[n].tw == ITW_0) + WRITE(p, "wrappedcoord.y = 0.0f;\n"); + else + WRITE(p, "wrappedcoord.y = fmod( uv%d.y, %s );\n", texcoord, tevIndWrapStart[bpmem.tevind[n].tw]); + + if (bpmem.tevind[n].fb_addprev) // add previous tevcoord + WRITE(p, "tevcoord.xy += wrappedcoord + indtevtrans%d;\n", n); + else + WRITE(p, "tevcoord.xy = wrappedcoord + indtevtrans%d;\n", n); + } + + TevStageCombiner::ColorCombiner &cc = bpmem.combiners[n].colorC; + TevStageCombiner::AlphaCombiner &ac = bpmem.combiners[n].alphaC; + + // blah1 + if(cc.a == TEVCOLORARG_RASA || cc.a == TEVCOLORARG_RASC + || cc.b == TEVCOLORARG_RASA || cc.b == TEVCOLORARG_RASC + || cc.c == TEVCOLORARG_RASA || cc.c == TEVCOLORARG_RASC + || cc.d == TEVCOLORARG_RASA || cc.d == TEVCOLORARG_RASC + || ac.a == TEVALPHAARG_RASA || ac.b == TEVALPHAARG_RASA + || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA) + { + char *rasswap = swapModeTable[bpmem.combiners[n].alphaC.rswap]; + WRITE(p, "rastemp = %s.%s;\n", tevRasTable[bpmem.tevorders[n / 2].getColorChan(n & 1)], rasswap); + WRITE(p, "crastemp = frac(rastemp * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + } + + + if (bpmem.tevorders[n/2].getEnable(n&1)) + { + if(!bHasIndStage) + { + // calc tevcord + if(bHasTexCoord) + WRITE(p, "tevcoord.xy = uv%d.xy;\n", texcoord); + else + WRITE(p, "tevcoord.xy = float2(0.0f, 0.0f);\n"); + } + + char *texswap = swapModeTable[bpmem.combiners[n].alphaC.tswap]; + int texmap = bpmem.tevorders[n/2].getTexMap(n&1); + SampleTexture(p, "textemp", "tevcoord", texswap, texmap, ApiType); + } + else + WRITE(p, "textemp = float4(1.0f, 1.0f, 1.0f, 1.0f);\n"); + + + // blah2 + if (cc.a == TEVCOLORARG_KONST || cc.b == TEVCOLORARG_KONST || cc.c == TEVCOLORARG_KONST || cc.d == TEVCOLORARG_KONST + || ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST || ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST) + { + int kc = bpmem.tevksel[n / 2].getKC(n & 1); + int ka = bpmem.tevksel[n / 2].getKA(n & 1); + WRITE(p, "konsttemp = float4(%s, %s);\n", tevKSelTableC[kc], tevKSelTableA[ka]); + if(kc > 7 || ka > 7) + { + WRITE(p, "ckonsttemp = frac(konsttemp * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + } + else + { + WRITE(p, "ckonsttemp = konsttemp;\n"); + } + } + + if(cc.a == TEVCOLORARG_CPREV || cc.a == TEVCOLORARG_APREV + || cc.b == TEVCOLORARG_CPREV || cc.b == TEVCOLORARG_APREV + || cc.c == TEVCOLORARG_CPREV || cc.c == TEVCOLORARG_APREV + || ac.a == TEVALPHAARG_APREV || ac.b == TEVALPHAARG_APREV || ac.c == TEVALPHAARG_APREV) + WRITE(p, "cprev = frac(prev * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + + + if(cc.a == TEVCOLORARG_C0 || cc.a == TEVCOLORARG_A0 + || cc.b == TEVCOLORARG_C0 || cc.b == TEVCOLORARG_A0 + || cc.c == TEVCOLORARG_C0 || cc.c == TEVCOLORARG_A0 + || ac.a == TEVALPHAARG_A0 || ac.b == TEVALPHAARG_A0 || ac.c == TEVALPHAARG_A0) + WRITE(p, "cc0 = frac(c0 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + + + if(cc.a == TEVCOLORARG_C1 || cc.a == TEVCOLORARG_A1 + || cc.b == TEVCOLORARG_C1 || cc.b == TEVCOLORARG_A1 + || cc.c == TEVCOLORARG_C1 || cc.c == TEVCOLORARG_A1 + || ac.a == TEVALPHAARG_A1 || ac.b == TEVALPHAARG_A1 || ac.c == TEVALPHAARG_A1) + WRITE(p, "cc1 = frac(c1 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + + + if(cc.a == TEVCOLORARG_C2 || cc.a == TEVCOLORARG_A2 + || cc.b == TEVCOLORARG_C2 || cc.b == TEVCOLORARG_A2 + || cc.c == TEVCOLORARG_C2 || cc.c == TEVCOLORARG_A2 + || ac.a == TEVALPHAARG_A2 || ac.b == TEVALPHAARG_A2 || ac.c == TEVALPHAARG_A2) + WRITE(p, "cc2 = frac(c2 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + + + WRITE(p, "// color combine\n", n); + if (cc.clamp) + WRITE(p, "%s = saturate(", tevCOutputTable[cc.dest]); + else + WRITE(p, "%s = ", tevCOutputTable[cc.dest]); + + // combine the color channel + if (cc.bias != TevBias_COMPARE) // if not compare + { + //normal color combiner goes here + if (cc.shift > TEVSCALE_1) + WRITE(p, "%s*(", tevScaleTable[cc.shift]); + + if(!(cc.d == TEVCOLORARG_ZERO && cc.op == TEVOP_ADD)) + WRITE(p, "%s%s", tevCInputTable[cc.d], tevOpTable[cc.op]); + + if (cc.a == cc.b) + WRITE(p, "%s", tevCInputTable[cc.a + 16]); + else if (cc.c == TEVCOLORARG_ZERO) + WRITE(p, "%s", tevCInputTable[cc.a + 16]); + else if (cc.c == TEVCOLORARG_ONE) + WRITE(p, "%s", tevCInputTable[cc.b + 16]); + else if (cc.a == TEVCOLORARG_ZERO) + WRITE(p, "%s*%s", tevCInputTable[cc.b + 16], tevCInputTable[cc.c + 16]); + else if (cc.b == TEVCOLORARG_ZERO) + WRITE(p, "%s*(float3(1.0f, 1.0f, 1.0f)-%s)", tevCInputTable[cc.a + 16], tevCInputTable[cc.c + 16]); + else + WRITE(p, "lerp(%s, %s, %s)", tevCInputTable[cc.a + 16], tevCInputTable[cc.b + 16], tevCInputTable[cc.c + 16]); + + WRITE(p, "%s", tevBiasTable[cc.bias]); + + if (cc.shift > TEVSCALE_1) + WRITE(p, ")"); + } + else + { + int cmp = (cc.shift<<1)|cc.op|8; // comparemode stored here + WRITE(p, TEVCMPColorOPTable[cmp],//lookup the function from the op table + tevCInputTable[cc.d], + tevCInputTable[cc.a + 16], + tevCInputTable[cc.b + 16], + tevCInputTable[cc.c + 16]); + } + if (cc.clamp) + WRITE(p, ")"); + WRITE(p,";\n"); + + WRITE(p, "// alpha combine\n", n); + // combine the alpha channel + if (ac.clamp) + WRITE(p, "%s = saturate(", tevAOutputTable[ac.dest]); + else + WRITE(p, "%s = ", tevAOutputTable[ac.dest]); + + if (ac.bias != TevBias_COMPARE) // if not compare + { + //normal alpha combiner goes here + if (ac.shift > TEVSCALE_1) + WRITE(p, "%s*(", tevScaleTable[ac.shift]); + + if(!(ac.d == TEVALPHAARG_ZERO && ac.op == TEVOP_ADD)) + WRITE(p, "%s.a%s", tevAInputTable[ac.d], tevOpTable[ac.op]); + + if (ac.a == ac.b) + WRITE(p, "%s.a", tevAInputTable[ac.a + 8]); + else if (ac.c == TEVALPHAARG_ZERO) + WRITE(p, "%s.a", tevAInputTable[ac.a + 8]); + else if (ac.a == TEVALPHAARG_ZERO) + WRITE(p, "%s.a*%s.a", tevAInputTable[ac.b + 8], tevAInputTable[ac.c + 8]); + else if (ac.b == TEVALPHAARG_ZERO) + WRITE(p, "%s.a*(1.0f-%s.a)", tevAInputTable[ac.a + 8], tevAInputTable[ac.c + 8]); + else + WRITE(p, "lerp(%s.a, %s.a, %s.a)", tevAInputTable[ac.a + 8], tevAInputTable[ac.b + 8], tevAInputTable[ac.c + 8]); + + WRITE(p, "%s",tevBiasTable[ac.bias]); + + if (ac.shift>0) + WRITE(p, ")"); + + } + else + { + //compare alpha combiner goes here + int cmp = (ac.shift<<1)|ac.op|8; // comparemode stored here + WRITE(p, TEVCMPAlphaOPTable[cmp], + tevAInputTable[ac.d], + tevAInputTable[ac.a + 8], + tevAInputTable[ac.b + 8], + tevAInputTable[ac.c + 8]); + } + if (ac.clamp) + WRITE(p, ")"); + WRITE(p, ";\n\n"); + WRITE(p, "// TEV done\n", n); +} + +void SampleTexture(char *&p, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType) +{ + if (ApiType == API_D3D11) + WRITE(p, "%s=Tex%d.Sample(samp%d,%s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap,texmap, texcoords, texmap, texswap); + else + WRITE(p, "%s=tex2D(samp%d,%s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap, texcoords, texmap, texswap); +} + +static const char *tevAlphaFuncsTable[] = +{ + "(false)", //ALPHACMP_NEVER 0 + "(prev.a <= %s - (0.25f/255.0f))", //ALPHACMP_LESS 1 + "(abs( prev.a - %s ) < (0.5f/255.0f))", //ALPHACMP_EQUAL 2 + "(prev.a < %s + (0.25f/255.0f))", //ALPHACMP_LEQUAL 3 + "(prev.a >= %s + (0.25f/255.0f))", //ALPHACMP_GREATER 4 + "(abs( prev.a - %s ) >= (0.5f/255.0f))", //ALPHACMP_NEQUAL 5 + "(prev.a > %s - (0.25f/255.0f))", //ALPHACMP_GEQUAL 6 + "(true)" //ALPHACMP_ALWAYS 7 +}; + +static const char *tevAlphaFunclogicTable[] = +{ + " && ", // and + " || ", // or + " != ", // xor + " == " // xnor +}; +static int AlphaPreTest() +{ + u32 op = bpmem.alphaFunc.logic; + u32 comp[2] = {bpmem.alphaFunc.comp0, bpmem.alphaFunc.comp1}; + + // First kill all the simple cases + switch(op) + { + case 0: // AND + if (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) return true; + if (comp[0] == ALPHACMP_NEVER || comp[1] == ALPHACMP_NEVER) return false; + break; + case 1: // OR + if (comp[0] == ALPHACMP_ALWAYS || comp[1] == ALPHACMP_ALWAYS) return true; + if (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)return false; + break; + case 2: // XOR + if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS)) + return true; + if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)) + return false; + break; + case 3: // XNOR + if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS)) + return false; + if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)) + return true; + break; + default: PanicAlert("bad logic for alpha test? %08x", op); + } + return -1; +} + + +static bool WriteAlphaTest(char *&p, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode) +{ + static const char *alphaRef[2] = + { + I_ALPHA"[0].r", + I_ALPHA"[0].g" + }; + + int Pretest = AlphaPreTest(); + if(Pretest >= 0) + { + return Pretest != 0; + } + + // using discard then return works the same in cg and dx9 but not in dx11 + WRITE(p, "if(!( "); + + int compindex = bpmem.alphaFunc.comp0 % 8; + WRITE(p, tevAlphaFuncsTable[compindex],alphaRef[0]);//lookup the first component from the alpha function table + + WRITE(p, "%s", tevAlphaFunclogicTable[bpmem.alphaFunc.logic % 4]);//lookup the logic op + + compindex = bpmem.alphaFunc.comp1 % 8; + WRITE(p, tevAlphaFuncsTable[compindex],alphaRef[1]);//lookup the second component from the alpha function table + WRITE(p, ")){ocol0 = 0;%s%s discard;%s}\n", + dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "ocol1 = 0;" : "", + DepthTextureEnable ? "depth = 1.f;" : "", + (ApiType != API_D3D11) ? "return;" : ""); + return true; +} + +static const char *tevFogFuncsTable[] = +{ + "", //No Fog + "", //? + "", //Linear + "", //? + " fog = 1.0f - pow(2.0f, -8.0f * fog);\n", //exp + " fog = 1.0f - pow(2.0f, -8.0f * fog * fog);\n", //exp2 + " fog = pow(2.0f, -8.0f * (1.0f - fog));\n", //backward exp + " fog = 1.0f - fog;\n fog = pow(2.0f, -8.0f * fog * fog);\n" //backward exp2 +}; + +static void WriteFog(char *&p) +{ + if(bpmem.fog.c_proj_fsel.fsel == 0)return;//no Fog + + if (bpmem.fog.c_proj_fsel.proj == 0) + { + // perspective + // ze = A/(B - (Zs >> B_SHF) + WRITE (p, " float ze = "I_FOG"[1].x / ("I_FOG"[1].y - (zCoord / "I_FOG"[1].w));\n"); + } + else + { + // orthographic + // ze = a*Zs (here, no B_SHF) + WRITE (p, " float ze = "I_FOG"[1].x * zCoord;\n"); + } + + // x_adjust = sqrt((x-center)^2 + k^2)/k + // ze *= x_adjust + //this is complitly teorical as the real hard seems to use a table intead of calculate the values. + if(bpmem.fogRange.Base.Enabled) + { + WRITE (p, " float x_adjust = (2.0f * (clipPos.x / "I_FOG"[2].y)) - 1.0f - "I_FOG"[2].x;\n"); + WRITE (p, " x_adjust = sqrt(x_adjust * x_adjust + "I_FOG"[2].z * "I_FOG"[2].z) / "I_FOG"[2].z;\n"); + WRITE (p, " ze *= x_adjust;\n"); + } + + WRITE (p, " float fog = saturate(ze - "I_FOG"[1].z);\n"); + + if(bpmem.fog.c_proj_fsel.fsel > 3) + { + WRITE(p, "%s", tevFogFuncsTable[bpmem.fog.c_proj_fsel.fsel]); + } + else + { + if(bpmem.fog.c_proj_fsel.fsel != 2) + WARN_LOG(VIDEO, "Unknown Fog Type! %08x", bpmem.fog.c_proj_fsel.fsel); + } + + WRITE(p, " prev.rgb = lerp(prev.rgb,"I_FOG"[0].rgb,fog);\n"); + + } \ No newline at end of file diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.h b/Source/Core/VideoCommon/Src/PixelShaderGen.h index b495703a13..0ebdd0e42f 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.h +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.h @@ -44,7 +44,7 @@ #define C_PLIGHTS (C_FOG + 3) #define C_PMATERIALS (C_PLIGHTS + 40) #define C_PENVCONST_END (C_PMATERIALS + 4) -#define PIXELSHADERUID_MAX_VALUES (5 + 32 + 6 + 11 + 2) +#define PIXELSHADERUID_MAX_VALUES 67 // DO NOT make anything in this class virtual. class PIXELSHADERUID @@ -71,7 +71,7 @@ public: int GetNumValues() const { - return tevstages + indstages + 4; + return tevstages; } bool operator <(const PIXELSHADERUID& _Right) const -- cgit v1.2.3 From 4137f287fd4d8778967bb8a82eafe5450687e2b7 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Wed, 31 Aug 2011 19:45:28 +0200 Subject: Fix a critical bug which caused shaders to be redundantly recompiled when disabling per-pixel depth. As a nice side effect, the number of redundant shader compilations is now next to zero ;) --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 6d0cc64964..ff82438305 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -124,6 +124,7 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) { // Courtesy of PreAlphaTest, we're done already ;) // TODO: There's a comment including bpmem.genmode.numindstages.. shouldnt really bother about that though. + uid->tevstages = 1; return; } -- cgit v1.2.3 From bcb8d11c1b9f0493a29b4bc4ecf40ee75594e9df Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Wed, 31 Aug 2011 20:46:03 +0200 Subject: Reduced the number of redundant vertex shader compilations (possibly to zero). That one was almost too easy ;P --- Source/Core/VideoCommon/Src/VertexShaderGen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp index 747b95c0da..7a1d5eb555 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp @@ -37,7 +37,7 @@ void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components) (xfregs.numChan.numColorChans << 27) | (xfregs.dualTexTrans.enabled << 29); - for (int i = 0; i < 2; ++i) { + for (int i = 0; i < xfregs.numChan.numColorChans; ++i) { uid->values[1+i] = xfregs.color[i].enablelighting ? (u32)xfregs.color[i].hex : (u32)xfregs.color[i].matsource; -- cgit v1.2.3 From 95517a9741175f15124dc11e132620a23bec7a19 Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Mon, 5 Sep 2011 09:43:23 -0700 Subject: vs2010: Disable LTCG for realz --- Source/Core/VideoCommon/VideoCommon.vcxproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index cf6d8c28a6..dd29d29ccb 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -45,7 +45,7 @@ StaticLibrary false Unicode - true + false StaticLibrary @@ -56,7 +56,6 @@ StaticLibrary false Unicode - true StaticLibrary -- cgit v1.2.3 From b867c21fea6401c0f9886129621bb0dbae962a4a Mon Sep 17 00:00:00 2001 From: Shawn Hoffman Date: Mon, 5 Sep 2011 12:19:11 -0700 Subject: apply to dx9 and dx11 backends as well --- Source/Core/VideoCommon/Src/RenderBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/RenderBase.h b/Source/Core/VideoCommon/Src/RenderBase.h index 17132ebb8c..7e5c0dc8fe 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.h +++ b/Source/Core/VideoCommon/Src/RenderBase.h @@ -54,7 +54,7 @@ public: virtual void SetColorMask() = 0; virtual void SetBlendMode(bool forceUpdate) = 0; - virtual bool SetScissorRect() = 0; + virtual void SetScissorRect() = 0; virtual void SetGenerationMode() = 0; virtual void SetDepthMode() = 0; virtual void SetLogicOpMode() = 0; -- cgit v1.2.3 From 17fcd406fcf68059ee5c0e940d42e9bcdcd32842 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 5 Sep 2011 22:04:28 +0200 Subject: Merge some scissor rect related code to VideoCommon. --- Source/Core/VideoCommon/Src/BPFunctions.cpp | 19 +++++++++++++++++-- Source/Core/VideoCommon/Src/BPFunctions.h | 2 +- Source/Core/VideoCommon/Src/BPStructs.cpp | 2 +- Source/Core/VideoCommon/Src/RenderBase.h | 14 +------------- Source/Core/VideoCommon/Src/VideoCommon.h | 3 +-- 5 files changed, 21 insertions(+), 19 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/BPFunctions.cpp b/Source/Core/VideoCommon/Src/BPFunctions.cpp index f323f03142..4f7315e832 100644 --- a/Source/Core/VideoCommon/Src/BPFunctions.cpp +++ b/Source/Core/VideoCommon/Src/BPFunctions.cpp @@ -44,9 +44,24 @@ void SetGenerationMode(const BPCmd &bp) g_renderer->SetGenerationMode(); } -void SetScissor(const BPCmd &bp) +void SetScissor() { - g_renderer->SetScissorRect(); + const int xoff = bpmem.scissorOffset.x * 2 - 342; + const int yoff = bpmem.scissorOffset.y * 2 - 342; + + EFBRectangle rc (bpmem.scissorTL.x - xoff - 342, bpmem.scissorTL.y - yoff - 342, + bpmem.scissorBR.x - xoff - 341, bpmem.scissorBR.y - yoff - 341); + + if (rc.left < 0) rc.left = 0; + if (rc.top < 0) rc.top = 0; + if (rc.right > EFB_WIDTH) rc.right = EFB_WIDTH; + if (rc.bottom > EFB_HEIGHT) rc.bottom = EFB_HEIGHT; + + if (rc.left > rc.right) rc.right = rc.left; + if (rc.top > rc.bottom) rc.bottom = rc.top; + + TargetRectangle trc = g_renderer->ConvertEFBRectangle(rc); + g_renderer->SetScissorRect(trc); } void SetLineWidth(const BPCmd &bp) diff --git a/Source/Core/VideoCommon/Src/BPFunctions.h b/Source/Core/VideoCommon/Src/BPFunctions.h index d104b12751..2b86523687 100644 --- a/Source/Core/VideoCommon/Src/BPFunctions.h +++ b/Source/Core/VideoCommon/Src/BPFunctions.h @@ -38,7 +38,7 @@ enum void FlushPipeline(); void SetGenerationMode(const BPCmd &bp); -void SetScissor(const BPCmd &bp); +void SetScissor(); void SetLineWidth(const BPCmd &bp); void SetDepthMode(const BPCmd &bp); void SetBlendMode(const BPCmd &bp); diff --git a/Source/Core/VideoCommon/Src/BPStructs.cpp b/Source/Core/VideoCommon/Src/BPStructs.cpp index 42b5b2212a..cee68327f5 100644 --- a/Source/Core/VideoCommon/Src/BPStructs.cpp +++ b/Source/Core/VideoCommon/Src/BPStructs.cpp @@ -162,7 +162,7 @@ void BPWritten(const BPCmd& bp) case BPMEM_SCISSORTL: // Scissor Rectable Top, Left case BPMEM_SCISSORBR: // Scissor Rectable Bottom, Right case BPMEM_SCISSOROFFSET: // Scissor Offset - SetScissor(bp); + SetScissor(); break; case BPMEM_LINEPTWIDTH: // Line Width SetLineWidth(bp); diff --git a/Source/Core/VideoCommon/Src/RenderBase.h b/Source/Core/VideoCommon/Src/RenderBase.h index 7e5c0dc8fe..b2c2c7ecfb 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.h +++ b/Source/Core/VideoCommon/Src/RenderBase.h @@ -54,7 +54,7 @@ public: virtual void SetColorMask() = 0; virtual void SetBlendMode(bool forceUpdate) = 0; - virtual void SetScissorRect() = 0; + virtual void SetScissorRect(const TargetRectangle& rc) = 0; virtual void SetGenerationMode() = 0; virtual void SetDepthMode() = 0; virtual void SetLogicOpMode() = 0; @@ -175,16 +175,4 @@ extern Renderer *g_renderer; void UpdateViewport(Matrix44& vpCorrection); -template -void GetScissorRect(MathUtil::Rectangle &rect) -{ - const int xoff = bpmem.scissorOffset.x * 2 - 342; - const int yoff = bpmem.scissorOffset.y * 2 - 342; - - rect.left = (R)(bpmem.scissorTL.x - xoff - 342); - rect.top = (R)(bpmem.scissorTL.y - yoff - 342); - rect.right = (R)(bpmem.scissorBR.x - xoff - 341); - rect.bottom = (R)(bpmem.scissorBR.y - yoff - 341); -} - #endif // _COMMON_RENDERBASE_H_ diff --git a/Source/Core/VideoCommon/Src/VideoCommon.h b/Source/Core/VideoCommon/Src/VideoCommon.h index 030c1f0b32..80c0937884 100644 --- a/Source/Core/VideoCommon/Src/VideoCommon.h +++ b/Source/Core/VideoCommon/Src/VideoCommon.h @@ -70,8 +70,7 @@ void HandleGLError(); // This structure should only be used to represent a rectangle in EFB // coordinates, where the origin is at the upper left and the frame dimensions // are 640 x 528. -struct EFBRectangle : public MathUtil::Rectangle -{}; +typedef MathUtil::Rectangle EFBRectangle; // This structure should only be used to represent a rectangle in standard target // coordinates, where the origin is at the lower left and the frame dimensions -- cgit v1.2.3 From df4e337ac99b9fdb72bbc244bd437032c6109a3b Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sat, 6 Aug 2011 21:35:16 +0000 Subject: Fix various pixel shader compilation errors caused by the Direct3D shader compiler going nuts due to uninitialized (and unused) shader variables. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@7693 8ced0084-cf51-0410-be5f-012b33b47a6e --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 530ecd54bb..6c709084e4 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -523,10 +523,12 @@ const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType WRITE(p, " float4 c0 = "I_COLORS"[1], c1 = "I_COLORS"[2], c2 = "I_COLORS"[3], prev = float4(0.0f, 0.0f, 0.0f, 0.0f), textemp = float4(0.0f, 0.0f, 0.0f, 0.0f), rastemp = float4(0.0f, 0.0f, 0.0f, 0.0f), konsttemp = float4(0.0f, 0.0f, 0.0f, 0.0f);\n" " float3 comp16 = float3(1.0f, 255.0f, 0.0f), comp24 = float3(1.0f, 255.0f, 255.0f*255.0f);\n" - " float4 alphabump=0;\n" - " float3 tevcoord;\n" - " float2 wrappedcoord, tempcoord;\n" - " float4 cc0, cc1, cc2, cprev,crastemp,ckonsttemp;\n\n"); + " float4 alphabump=float4(0.0f,0.0f,0.0f,0.0f);\n" + " float3 tevcoord=float3(0.0f, 0.0f, 0.0f);\n" + " float2 wrappedcoord=float2(0.0f,0.0f), tempcoord=float2(0.0f,0.0f);\n" + " float4 cc0=float4(0.0f,0.0f,0.0f,0.0f), cc1=float4(0.0f,0.0f,0.0f,0.0f);\n" + " float4 cc2=float4(0.0f,0.0f,0.0f,0.0f), cprev=float4(0.0f,0.0f,0.0f,0.0f);\n" + " float4 crastemp=float4(0.0f,0.0f,0.0f,0.0f),ckonsttemp=float4(0.0f,0.0f,0.0f,0.0f);\n\n"); if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) { -- cgit v1.2.3 From 231c13d6cec11c1326ab21f567ab6a58a521043d Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sun, 4 Sep 2011 00:47:45 +0200 Subject: Added safe vertex shader UIDs for debugging purposes. --- Source/Core/VideoCommon/Src/VertexShaderGen.cpp | 21 +++++++++++++++++++++ Source/Core/VideoCommon/Src/VertexShaderGen.h | 21 +++++++++++++-------- 2 files changed, 34 insertions(+), 8 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp index 7a1d5eb555..d15cef0c70 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp @@ -69,6 +69,27 @@ void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components) } } +void GetSafeVertexShaderId(VERTEXSHADERUIDSAFE *uid, u32 components) +{ + // Just store all used registers here without caring whether we need all bits or less. + u32* ptr = uid->values; + *ptr++ = components; + *ptr++ = xfregs.numTexGen.hex; + *ptr++ = xfregs.numChan.hex; + *ptr++ = xfregs.dualTexTrans.hex; + + for (int i = 0; i < 2; ++i) { + *ptr++ = xfregs.color[i].hex; + *ptr++ = xfregs.alpha[i].hex; + } + *ptr++ = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; + for (unsigned int i = 0; i < 8; ++i) { + *ptr++ = xfregs.texMtxInfo[i].hex; + *ptr++ = xfregs.postMtxInfo[i].hex; + } + _assert_((ptr - uid->values) == uid->GetNumValues()); +} + static char text[16384]; #define WRITE p+=sprintf diff --git a/Source/Core/VideoCommon/Src/VertexShaderGen.h b/Source/Core/VideoCommon/Src/VertexShaderGen.h index d7bf362213..50e9c5fc3b 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderGen.h +++ b/Source/Core/VideoCommon/Src/VertexShaderGen.h @@ -48,17 +48,19 @@ #define C_DEPTHPARAMS (C_POSTTRANSFORMMATRICES + 64) #define C_VENVCONST_END (C_DEPTHPARAMS + 4) -class VERTEXSHADERUID +template +class _VERTEXSHADERUID { +#define NUM_VSUID_VALUES_SAFE 25 public: - u32 values[9]; + u32 values[safe ? NUM_VSUID_VALUES_SAFE : 9]; - VERTEXSHADERUID() + _VERTEXSHADERUID() { memset(values, 0, sizeof(values)); } - VERTEXSHADERUID(const VERTEXSHADERUID& r) + _VERTEXSHADERUID(const _VERTEXSHADERUID& r) { for (size_t i = 0; i < sizeof(values) / sizeof(u32); ++i) values[i] = r.values[i]; @@ -66,10 +68,11 @@ public: int GetNumValues() const { - return (((values[0] >> 23) & 0xf) * 3 + 3) / 4 + 3; // numTexGens*3/4+1 + if (safe) return NUM_VSUID_VALUES_SAFE; + else return (((values[0] >> 23) & 0xf) * 3 + 3) / 4 + 3; // numTexGens*3/4+1 } - bool operator <(const VERTEXSHADERUID& _Right) const + bool operator <(const _VERTEXSHADERUID& _Right) const { if (values[0] < _Right.values[0]) return true; @@ -86,7 +89,7 @@ public: return false; } - bool operator ==(const VERTEXSHADERUID& _Right) const + bool operator ==(const _VERTEXSHADERUID& _Right) const { if (values[0] != _Right.values[0]) return false; @@ -99,13 +102,15 @@ public: return true; } }; - +typedef _VERTEXSHADERUID VERTEXSHADERUID; +typedef _VERTEXSHADERUID VERTEXSHADERUIDSAFE; // components is included in the uid. char* GenerateVSOutputStruct(char* p, u32 components, API_TYPE api_type); const char *GenerateVertexShaderCode(u32 components, API_TYPE api_type); void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components); +void GetSafeVertexShaderId(VERTEXSHADERUIDSAFE *uid, u32 components); extern VERTEXSHADERUID last_vertex_shader_uid; -- cgit v1.2.3 From 4702de591eb69e815398b21c5b4321b1ac133552 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sun, 4 Sep 2011 02:10:07 +0200 Subject: Added safe pixel shader UIDs for debugging purposes. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 54 ++++++++++++++++++++++---- Source/Core/VideoCommon/Src/PixelShaderGen.h | 24 +++++++----- Source/Core/VideoCommon/Src/VertexShaderGen.h | 2 +- 3 files changed, 63 insertions(+), 17 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index ff82438305..9913ea7c5a 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -164,7 +164,7 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) } u32* ptr = &uid->values[2]; - for (unsigned int i = 0; i < bpmem.genMode.numtevstages+1; ++i) + for (int i = 0; i < bpmem.genMode.numtevstages+1; ++i) { StageHash(i, ptr); ptr += 4; // max: ptr = &uid->values[66] @@ -315,6 +315,46 @@ void _GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) } +void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) +{ + u32* ptr = uid->values; + *ptr++ = dstAlphaMode; + *ptr++ = bpmem.genMode.hex; + *ptr++ = bpmem.ztex2.hex; + *ptr++ = bpmem.zcontrol.hex; + *ptr++ = bpmem.zmode.hex; + *ptr++ = g_ActiveConfig.bEnablePerPixelDepth; + *ptr++ = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; + *ptr++ = xfregs.numTexGen.hex; + + if (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + { + // TODO: Include register states for lighting shader + } + + for (unsigned int i = 0; i < 8; ++i) + *ptr++ = xfregs.texMtxInfo[i].hex; + + for (unsigned int i = 0; i < 16; ++i) + *ptr++ = bpmem.tevind[i].hex; + + *ptr++ = bpmem.tevindref.hex; + + for (int i = 0; i < bpmem.genMode.numtevstages+1; ++i) + { + // TODO ... + StageHash(i, ptr); + ptr += 4; // max: ptr = &uid->values[66] + } + + *ptr++ = bpmem.fog.c_proj_fsel.hex; + + *ptr++ = bpmem.fogRange.Base.hex; + + _assert_((ptr - uid->values) == uid->GetNumValues()); +} + + // old tev->pixelshader notes // // color for this stage (alpha, color) is given by bpmem.tevorders[0].colorchan0 @@ -539,8 +579,8 @@ const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType char *p = text; WRITE(p, "//Pixel Shader for TEV stages\n"); - WRITE(p, "//%i TEV stages, %i texgens, %i IND stages\n", - numStages, numTexgen, bpmem.genMode.numindstages); + WRITE(p, "//%i TEV stages, %i texgens, XXX IND stages\n", + numStages, numTexgen/*, bpmem.genMode.numindstages*/); int nIndirectStagesUsed = 0; if (bpmem.genMode.numindstages > 0) @@ -876,7 +916,7 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) if (bHasIndStage) { - WRITE(p, "// indirect op\n", n); + WRITE(p, "// indirect op\n"); // perform the indirect op on the incoming regular coordinates using indtex%d as the offset coords if (bpmem.tevind[n].bs != ITBA_OFF) { @@ -1026,7 +1066,7 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) WRITE(p, "cc2 = frac(c2 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - WRITE(p, "// color combine\n", n); + WRITE(p, "// color combine\n"); if (cc.clamp) WRITE(p, "%s = saturate(", tevCOutputTable[cc.dest]); else @@ -1073,7 +1113,7 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) WRITE(p, ")"); WRITE(p,";\n"); - WRITE(p, "// alpha combine\n", n); + WRITE(p, "// alpha combine\n"); // combine the alpha channel if (ac.clamp) WRITE(p, "%s = saturate(", tevAOutputTable[ac.dest]); @@ -1119,7 +1159,7 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) if (ac.clamp) WRITE(p, ")"); WRITE(p, ";\n\n"); - WRITE(p, "// TEV done\n", n); + WRITE(p, "// TEV done\n"); } void SampleTexture(char *&p, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType) diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.h b/Source/Core/VideoCommon/Src/PixelShaderGen.h index 0ebdd0e42f..6ed39e7e74 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.h +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.h @@ -45,36 +45,39 @@ #define C_PMATERIALS (C_PLIGHTS + 40) #define C_PENVCONST_END (C_PMATERIALS + 4) #define PIXELSHADERUID_MAX_VALUES 67 +#define PIXELSHADERUID_MAX_VALUES_SAFE 100 // DO NOT make anything in this class virtual. -class PIXELSHADERUID +template +class _PIXELSHADERUID { public: - u32 values[PIXELSHADERUID_MAX_VALUES]; + u32 values[safe ? PIXELSHADERUID_MAX_VALUES_SAFE : PIXELSHADERUID_MAX_VALUES]; u16 tevstages, indstages; - PIXELSHADERUID() + _PIXELSHADERUID() { - memset(values, 0, PIXELSHADERUID_MAX_VALUES * 4); + memset(values, 0, sizeof(values)); tevstages = indstages = 0; } - PIXELSHADERUID(const PIXELSHADERUID& r) + _PIXELSHADERUID(const _PIXELSHADERUID& r) { tevstages = r.tevstages; indstages = r.indstages; int N = GetNumValues(); - _assert_(N <= PIXELSHADERUID_MAX_VALUES); + _assert_(N <= GetNumValues()); for (int i = 0; i < N; ++i) values[i] = r.values[i]; } int GetNumValues() const { - return tevstages; + if (safe) return (sizeof(values) / sizeof(u32)); + else return tevstages; } - bool operator <(const PIXELSHADERUID& _Right) const + bool operator <(const _PIXELSHADERUID& _Right) const { if (values[0] < _Right.values[0]) return true; @@ -91,7 +94,7 @@ public: return false; } - bool operator ==(const PIXELSHADERUID& _Right) const + bool operator ==(const _PIXELSHADERUID& _Right) const { if (values[0] != _Right.values[0]) return false; @@ -104,6 +107,8 @@ public: return true; } }; +typedef _PIXELSHADERUID PIXELSHADERUID; +typedef _PIXELSHADERUID PIXELSHADERUIDSAFE; // Different ways to achieve rendering with destination alpha enum DSTALPHA_MODE @@ -115,6 +120,7 @@ enum DSTALPHA_MODE const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType, u32 components); void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode); +void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode); extern PIXELSHADERUID last_pixel_shader_uid; diff --git a/Source/Core/VideoCommon/Src/VertexShaderGen.h b/Source/Core/VideoCommon/Src/VertexShaderGen.h index 50e9c5fc3b..1ab696acc2 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderGen.h +++ b/Source/Core/VideoCommon/Src/VertexShaderGen.h @@ -57,7 +57,7 @@ public: _VERTEXSHADERUID() { - memset(values, 0, sizeof(values)); + memset(values, 0, sizeof(values)); } _VERTEXSHADERUID(const _VERTEXSHADERUID& r) -- cgit v1.2.3 From 3939f9595a87393482df617465888f4ed46b6d94 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Wed, 7 Sep 2011 20:20:29 +0200 Subject: Add runtime checks to make sure we aren't overoptimizing the pixel shader cache. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 49 ++++++++++++++------------ Source/Core/VideoCommon/Src/PixelShaderGen.h | 2 ++ 2 files changed, 28 insertions(+), 23 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 9913ea7c5a..8bdfc3db28 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -172,13 +172,13 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) if (alphaPreTest == 0 || alphaPreTest == 2) { - ptr[0] |= bpmem.fog.c_proj_fsel.fsel; // 3 + ptr[0] |= bpmem.fog.c_proj_fsel.fsel << 8; // 3 if (DepthTextureEnable) { - ptr[0] |= bpmem.ztex2.op << 3; // 2 - ptr[0] |= bpmem.zcontrol.zcomploc << 5; // 1 - ptr[0] |= bpmem.zmode.testenable << 6; // 1 - ptr[0] |= bpmem.zmode.updateenable << 7; // 1 + ptr[0] |= bpmem.ztex2.op << 11; // 2 + ptr[0] |= bpmem.zcontrol.zcomploc << 13; // 1 + ptr[0] |= bpmem.zmode.testenable << 14; // 1 + ptr[0] |= bpmem.zmode.updateenable << 15; // 1 } } @@ -186,8 +186,8 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) { if (bpmem.fog.c_proj_fsel.fsel != 0) { - ptr[0] |= bpmem.fog.c_proj_fsel.proj << 8; // 1 - ptr[0] |= bpmem.fogRange.Base.Enabled << 9; // 1 + ptr[0] |= bpmem.fog.c_proj_fsel.proj << 16; // 1 + ptr[0] |= bpmem.fogRange.Base.Enabled << 17; // 1 } } uid->tevstages = (ptr+1) - uid->values; @@ -318,14 +318,14 @@ void _GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) { u32* ptr = uid->values; - *ptr++ = dstAlphaMode; - *ptr++ = bpmem.genMode.hex; - *ptr++ = bpmem.ztex2.hex; - *ptr++ = bpmem.zcontrol.hex; - *ptr++ = bpmem.zmode.hex; - *ptr++ = g_ActiveConfig.bEnablePerPixelDepth; - *ptr++ = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; - *ptr++ = xfregs.numTexGen.hex; + *ptr++ = dstAlphaMode; // 0 + *ptr++ = bpmem.genMode.hex; // 1 + *ptr++ = bpmem.ztex2.hex; // 2 + *ptr++ = bpmem.zcontrol.hex; // 3 + *ptr++ = bpmem.zmode.hex; // 4 + *ptr++ = g_ActiveConfig.bEnablePerPixelDepth; // 5 + *ptr++ = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; // 6 + *ptr++ = xfregs.numTexGen.hex; // 7 if (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) { @@ -333,25 +333,28 @@ void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) } for (unsigned int i = 0; i < 8; ++i) - *ptr++ = xfregs.texMtxInfo[i].hex; + *ptr++ = xfregs.texMtxInfo[i].hex; // 8-15 for (unsigned int i = 0; i < 16; ++i) - *ptr++ = bpmem.tevind[i].hex; + *ptr++ = bpmem.tevind[i].hex; // 16-31 - *ptr++ = bpmem.tevindref.hex; + *ptr++ = bpmem.tevindref.hex; // 32 - for (int i = 0; i < bpmem.genMode.numtevstages+1; ++i) + for (int i = 0; i < bpmem.genMode.numtevstages+1; ++i) // up to 16 times { // TODO ... StageHash(i, ptr); - ptr += 4; // max: ptr = &uid->values[66] + ptr += 4; // max: ptr = &uid->values[33+63] } - *ptr++ = bpmem.fog.c_proj_fsel.hex; + ptr = &uid->values[97]; + + *ptr++ = bpmem.alphaFunc.hex; // 97 - *ptr++ = bpmem.fogRange.Base.hex; + *ptr++ = bpmem.fog.c_proj_fsel.hex; // 98 + *ptr++ = bpmem.fogRange.Base.hex; // 99 - _assert_((ptr - uid->values) == uid->GetNumValues()); + _assert_((ptr - uid->values) <= uid->GetNumValues()); } diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.h b/Source/Core/VideoCommon/Src/PixelShaderGen.h index 6ed39e7e74..c66635e12b 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.h +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.h @@ -119,6 +119,8 @@ enum DSTALPHA_MODE }; const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType, u32 components); + +// TODO: Wtf, those need components as well! -.- void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode); void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode); -- cgit v1.2.3 From 98b62d836290ff0ea46f73a6f6bdb0d3c91df923 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Wed, 7 Sep 2011 21:12:17 +0200 Subject: Track alpha blending paramaters in the pixel shader UID. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 8bdfc3db28..46f139f105 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -170,6 +170,10 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) ptr += 4; // max: ptr = &uid->values[66] } + ptr[0] |= bpmem.alphaFunc.comp0; // 3 + ptr[0] |= bpmem.alphaFunc.comp1 << 3; // 3 + ptr[0] |= bpmem.alphaFunc.logic << 6; // 2 + if (alphaPreTest == 0 || alphaPreTest == 2) { ptr[0] |= bpmem.fog.c_proj_fsel.fsel << 8; // 3 -- cgit v1.2.3 From 6c7bda6851b39d358f9b951739156e07345ccfa4 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Thu, 8 Sep 2011 02:09:44 +0200 Subject: Various fixes and cleanups. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 152 +++---------------------- Source/Core/VideoCommon/Src/PixelShaderGen.h | 33 +++--- 2 files changed, 32 insertions(+), 153 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 46f139f105..464c16fa58 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -100,14 +100,16 @@ static void StageHash(int stage, u32* out) } } +// Mash together all the inputs that contribute to the code of a generated pixel shader into +// a unique identifier, basically containing all the bits. Yup, it's a lot .... +// It would likely be a lot more efficient to build this incrementally as the attributes +// are set... void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) { uid->values[0] |= bpmem.genMode.numtevstages; // 4 uid->values[0] |= bpmem.genMode.numtexgens << 4; // 4 uid->values[0] |= dstAlphaMode << 8; // 2 - uid->tevstages = bpmem.genMode.numtevstages; - bool DepthTextureEnable = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) || g_ActiveConfig.bEnablePerPixelDepth; uid->values[0] |= DepthTextureEnable << 10; // 1 @@ -124,7 +126,7 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) { // Courtesy of PreAlphaTest, we're done already ;) // TODO: There's a comment including bpmem.genmode.numindstages.. shouldnt really bother about that though. - uid->tevstages = 1; + uid->num_values = 1; return; } @@ -141,8 +143,6 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) uid->values[1] |= xfregs.texMtxInfo[i].projection << (i - 14); // 1 } - uid->indstages = bpmem.genMode.numindstages; - uid->values[1] = bpmem.genMode.numindstages << 2; // 3 u32 indirectStagesUsed = 0; for (unsigned int i = 0; i < bpmem.genMode.numindstages; ++i) @@ -194,129 +194,7 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) ptr[0] |= bpmem.fogRange.Base.Enabled << 17; // 1 } } - uid->tevstages = (ptr+1) - uid->values; -} - -// Mash together all the inputs that contribute to the code of a generated pixel shader into -// a unique identifier, basically containing all the bits. Yup, it's a lot .... -// It would likely be a lot more efficient to build this incrementally as the attributes -// are set... -void _GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) -{ - u32 numstages = bpmem.genMode.numtevstages + 1; - u32 projtexcoords = 0; - for (u32 i = 0; i < numstages; i++) - { - if (bpmem.tevorders[i/2].getEnable(i & 1)) - { - int texcoord = bpmem.tevorders[i / 2].getTexCoord(i & 1); - if (xfregs.texMtxInfo[i].projection) - projtexcoords |= 1 << texcoord; - } - } - uid->values[0] = (u32)bpmem.genMode.numtevstages | - ((u32)bpmem.genMode.numindstages << 4) | - ((u32)bpmem.genMode.numtexgens << 7) | - ((u32)dstAlphaMode << 11) | - ((u32)((bpmem.alphaFunc.hex >> 16) & 0xff) << 13) | - (projtexcoords << 21) | - ((u32)bpmem.ztex2.op << 29); - - // swap table - for (int i = 0; i < 8; i += 2) - ((u8*)&uid->values[1])[i / 2] = (bpmem.tevksel[i].hex & 0xf) | ((bpmem.tevksel[i + 1].hex & 0xf) << 4); - - u32 enableZTexture = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) || g_ActiveConfig.bEnablePerPixelDepth ? 1 : 0; - - uid->values[2] = (u32)bpmem.fog.c_proj_fsel.fsel | - ((u32)bpmem.fog.c_proj_fsel.proj << 3) | - ((u32)enableZTexture << 4) | ((u32)bpmem.fogRange.Base.Enabled << 5); - - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - { - for (int i = 0; i < 2; ++i) { - uid->values[3 + i] = xfregs.color[i].enablelighting ? - (u32)xfregs.color[i].hex : - (u32)xfregs.color[i].matsource; - uid->values[3 + i] |= (xfregs.alpha[i].enablelighting ? - (u32)xfregs.alpha[i].hex : - (u32)xfregs.alpha[i].matsource) << 15; - } - } - uid->values[4] |= (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) << 31; - - int hdr = 5; - u32 *pcurvalue = &uid->values[hdr]; - for (u32 i = 0; i < numstages; ++i) - { - TevStageCombiner::ColorCombiner &cc = bpmem.combiners[i].colorC; - TevStageCombiner::AlphaCombiner &ac = bpmem.combiners[i].alphaC; - - u32 val0 = cc.hex & 0xffffff; - u32 val1 = ac.hex & 0xffffff; - val0 |= bpmem.tevksel[i / 2].getKC(i & 1) << 24; - val1 |= bpmem.tevksel[i / 2].getKA(i & 1) << 24; - pcurvalue[0] = val0; - pcurvalue[1] = val1; - pcurvalue += 2; - } - - for (u32 i = 0; i < numstages / 2; ++i) - { - u32 val0, val1; - if (bpmem.tevorders[i].hex & 0x40) - val0 = bpmem.tevorders[i].hex & 0x3ff; - else - val0 = bpmem.tevorders[i].hex & 0x380; - if (bpmem.tevorders[i].hex & 0x40000) - val1 = (bpmem.tevorders[i].hex & 0x3ff000) >> 12; - else - val1 = (bpmem.tevorders[i].hex & 0x380000) >> 12; - - switch (i % 3) { - case 0: pcurvalue[0] = val0|(val1<<10); break; - case 1: pcurvalue[0] |= val0<<20; pcurvalue[1] = val1; pcurvalue++; break; - case 2: pcurvalue[1] |= (val0<<10)|(val1<<20); pcurvalue++; break; - default: PanicAlert("Unknown case for Tev Stages / 2: %08x", (i % 3)); - } - } - - if (numstages & 1) { // odd - u32 val0; - if (bpmem.tevorders[bpmem.genMode.numtevstages/2].hex & 0x40) - val0 = bpmem.tevorders[bpmem.genMode.numtevstages/2].hex & 0x3ff; - else - val0 = bpmem.tevorders[bpmem.genMode.numtevstages/2].hex & 0x380; - - switch (bpmem.genMode.numtevstages % 3) - { - case 0: pcurvalue[0] = val0; break; - case 1: pcurvalue[0] |= val0 << 20; break; - case 2: pcurvalue[1] |= val0 << 10; pcurvalue++; break; - default: PanicAlert("Unknown case for Tev Stages: %08x", bpmem.genMode.numtevstages % 3); - } - } - - if ((bpmem.genMode.numtevstages % 3) != 2) - ++pcurvalue; - - uid->tevstages = (u32)(pcurvalue - &uid->values[0] - hdr); - - for (u32 i = 0; i < bpmem.genMode.numindstages; ++i) - { - u32 val = bpmem.tevind[i].hex & 0x1fffff; // 21 bits - switch (i % 3) - { - case 0: pcurvalue[0] = val; break; - case 1: pcurvalue[0] |= val << 21; pcurvalue[1] = val >> 11; ++pcurvalue; break; - case 2: pcurvalue[0] |= val << 10; ++pcurvalue; break; - default: PanicAlert("Unknown case for Ind Stages: %08x", (i % 3)); - } - } - - // yeah, well .... - uid->indstages = (u32)(pcurvalue - &uid->values[0] - (hdr - 1) - uid->tevstages); - + uid->num_values = (ptr+1) - uid->values; } void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) @@ -346,19 +224,21 @@ void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) for (int i = 0; i < bpmem.genMode.numtevstages+1; ++i) // up to 16 times { - // TODO ... - StageHash(i, ptr); - ptr += 4; // max: ptr = &uid->values[33+63] + *ptr++ = bpmem.combiners[i].colorC.hex; // 33+5*i + *ptr++ = bpmem.combiners[i].alphaC.hex; // 34+5*i + *ptr++ = bpmem.tevind[i].hex; // 35+5*i + *ptr++ = bpmem.tevksel[i/2].hex; // 36+5*i + *ptr++ = bpmem.tevorders[i/2].hex; // 37+5*i } - ptr = &uid->values[97]; + ptr = &uid->values[113]; - *ptr++ = bpmem.alphaFunc.hex; // 97 + *ptr++ = bpmem.alphaFunc.hex; // 113 - *ptr++ = bpmem.fog.c_proj_fsel.hex; // 98 - *ptr++ = bpmem.fogRange.Base.hex; // 99 + *ptr++ = bpmem.fog.c_proj_fsel.hex; // 114 + *ptr++ = bpmem.fogRange.Base.hex; // 115 - _assert_((ptr - uid->values) <= uid->GetNumValues()); + _assert_((ptr - uid->values) == uid->GetNumValues()); } diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.h b/Source/Core/VideoCommon/Src/PixelShaderGen.h index c66635e12b..d20f43c711 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.h +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.h @@ -45,7 +45,7 @@ #define C_PMATERIALS (C_PLIGHTS + 40) #define C_PENVCONST_END (C_PMATERIALS + 4) #define PIXELSHADERUID_MAX_VALUES 67 -#define PIXELSHADERUID_MAX_VALUES_SAFE 100 +#define PIXELSHADERUID_MAX_VALUES_SAFE 115 // DO NOT make anything in this class virtual. template @@ -53,38 +53,37 @@ class _PIXELSHADERUID { public: u32 values[safe ? PIXELSHADERUID_MAX_VALUES_SAFE : PIXELSHADERUID_MAX_VALUES]; - u16 tevstages, indstages; + u16 num_values; _PIXELSHADERUID() { memset(values, 0, sizeof(values)); - tevstages = indstages = 0; + + if (safe) num_values = sizeof(values) / sizeof(values[0]); + else num_values = 0; } _PIXELSHADERUID(const _PIXELSHADERUID& r) { - tevstages = r.tevstages; - indstages = r.indstages; - int N = GetNumValues(); - _assert_(N <= GetNumValues()); - for (int i = 0; i < N; ++i) - values[i] = r.values[i]; + num_values = r.num_values; + if (safe) memcpy(values, r.values, PIXELSHADERUID_MAX_VALUES_SAFE); + else memcpy(values, r.values, r.GetNumValues() * sizeof(values[0])); } int GetNumValues() const { if (safe) return (sizeof(values) / sizeof(u32)); - else return tevstages; + else return num_values; } bool operator <(const _PIXELSHADERUID& _Right) const { - if (values[0] < _Right.values[0]) + int N = GetNumValues(); + if (N < _Right.GetNumValues()) return true; - else if (values[0] > _Right.values[0]) + else if (N > _Right.GetNumValues()) return false; - int N = GetNumValues(); - for (int i = 1; i < N; ++i) + for (int i = 0; i < N; ++i) { if (values[i] < _Right.values[i]) return true; @@ -96,10 +95,10 @@ public: bool operator ==(const _PIXELSHADERUID& _Right) const { - if (values[0] != _Right.values[0]) - return false; int N = GetNumValues(); - for (int i = 1; i < N; ++i) + if (N != _Right.GetNumValues()) + return false; + for (int i = 0; i < N; ++i) { if (values[i] != _Right.values[i]) return false; -- cgit v1.2.3 From b28348066e083d532d5ab62c93ce0f1dc5c217ed Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Fri, 9 Sep 2011 00:32:04 +0200 Subject: Improve the shader UID debugging stuff and merge it to VideoCommon, effectively enabling it in D3D9 and D3D11 as well. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 45 +- Source/Core/VideoCommon/Src/PixelShaderGen.h | 3 + Source/Core/VideoCommon/Src/VertexShaderGen.cpp | 968 ++++++++++++------------ Source/Core/VideoCommon/Src/VertexShaderGen.h | 4 + 4 files changed, 548 insertions(+), 472 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 8aa3a0a56e..25007be93b 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -241,6 +241,39 @@ void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) _assert_((ptr - uid->values) == uid->GetNumValues()); } +void ValidatePixelShaderIDs(API_TYPE api, PIXELSHADERUIDSAFE old_id, const std::string& old_code, DSTALPHA_MODE dstAlphaMode, u32 components) +{ + PIXELSHADERUIDSAFE new_id; + GetSafePixelShaderId(&new_id, dstAlphaMode); + + if (!(old_id == new_id)) + { + std::string new_code(GeneratePixelShaderCode(dstAlphaMode, api, components)); + if (old_code != new_code) + { + _assert_(old_id.GetNumValues() == new_id.GetNumValues()); + + char msg[8192]; + char* ptr = msg; + ptr += sprintf(ptr, "Pixel shader IDs matched but unique IDs did not!\nUnique IDs (old <-> new):\n"); + const int N = new_id.GetNumValues(); + for (int i = 0; i < N/2; ++i) + ptr += sprintf(ptr, "%02d, %08X %08X | %08X %08X\n", 2*i, old_id.values[2*i], old_id.values[2*i+1], + new_id.values[2*i], new_id.values[2*i+1]); + if (N % 2) + ptr += sprintf(ptr, "%02d, %08X | %08X\n", N-1, old_id.values[N-1], new_id.values[N-1]); + + static int num_failures = 0; + char szTemp[MAX_PATH]; + sprintf(szTemp, "%spsuid_mismatch_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++); + std::ofstream file(szTemp); + file << msg; + file.close(); + + PanicAlert("Unique pixel shader ID mismatch!\n\nReport this to the devs, along with the contents of %s.", szTemp); + } + } +} // old tev->pixelshader notes // @@ -594,12 +627,12 @@ const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType WRITE(p, " float4 c0 = "I_COLORS"[1], c1 = "I_COLORS"[2], c2 = "I_COLORS"[3], prev = float4(0.0f, 0.0f, 0.0f, 0.0f), textemp = float4(0.0f, 0.0f, 0.0f, 0.0f), rastemp = float4(0.0f, 0.0f, 0.0f, 0.0f), konsttemp = float4(0.0f, 0.0f, 0.0f, 0.0f);\n" " float3 comp16 = float3(1.0f, 255.0f, 0.0f), comp24 = float3(1.0f, 255.0f, 255.0f*255.0f);\n" - " float4 alphabump=float4(0.0f,0.0f,0.0f,0.0f);\n" - " float3 tevcoord=float3(0.0f, 0.0f, 0.0f);\n" - " float2 wrappedcoord=float2(0.0f,0.0f), tempcoord=float2(0.0f,0.0f);\n" - " float4 cc0=float4(0.0f,0.0f,0.0f,0.0f), cc1=float4(0.0f,0.0f,0.0f,0.0f);\n" - " float4 cc2=float4(0.0f,0.0f,0.0f,0.0f), cprev=float4(0.0f,0.0f,0.0f,0.0f);\n" - " float4 crastemp=float4(0.0f,0.0f,0.0f,0.0f),ckonsttemp=float4(0.0f,0.0f,0.0f,0.0f);\n\n"); + " float4 alphabump=float4(0.0f,0.0f,0.0f,0.0f);\n" + " float3 tevcoord=float3(0.0f, 0.0f, 0.0f);\n" + " float2 wrappedcoord=float2(0.0f,0.0f), tempcoord=float2(0.0f,0.0f);\n" + " float4 cc0=float4(0.0f,0.0f,0.0f,0.0f), cc1=float4(0.0f,0.0f,0.0f,0.0f);\n" + " float4 cc2=float4(0.0f,0.0f,0.0f,0.0f), cprev=float4(0.0f,0.0f,0.0f,0.0f);\n" + " float4 crastemp=float4(0.0f,0.0f,0.0f,0.0f),ckonsttemp=float4(0.0f,0.0f,0.0f,0.0f);\n\n"); if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) { diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.h b/Source/Core/VideoCommon/Src/PixelShaderGen.h index d20f43c711..8031fdfbb2 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.h +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.h @@ -123,6 +123,9 @@ const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode); void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode); +// Used to make sure that our optimized pixel shader IDs don't lose any possible shader code changes +void ValidatePixelShaderIDs(API_TYPE api, PIXELSHADERUIDSAFE old_id, const std::string& old_code, DSTALPHA_MODE dstAlphaMode, u32 components); + extern PIXELSHADERUID last_pixel_shader_uid; #endif // GCOGL_PIXELSHADER_H diff --git a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp index d15cef0c70..902f645bec 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp @@ -1,466 +1,502 @@ -// Copyright (C) 2003 Dolphin Project. - -// This program 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, version 2.0. - -// This program 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 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include - -#include "NativeVertexFormat.h" - -#include "BPMemory.h" -#include "CPMemory.h" -#include "LightingShaderGen.h" -#include "VertexShaderGen.h" -#include "VideoConfig.h" - -VERTEXSHADERUID last_vertex_shader_uid; - -// Mash together all the inputs that contribute to the code of a generated vertex shader into -// a unique identifier, basically containing all the bits. Yup, it's a lot .... -void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components) -{ - uid->values[0] = components | - (xfregs.numTexGen.numTexGens << 23) | - (xfregs.numChan.numColorChans << 27) | - (xfregs.dualTexTrans.enabled << 29); - - for (int i = 0; i < xfregs.numChan.numColorChans; ++i) { - uid->values[1+i] = xfregs.color[i].enablelighting ? - (u32)xfregs.color[i].hex : - (u32)xfregs.color[i].matsource; - uid->values[1+i] |= (xfregs.alpha[i].enablelighting ? - (u32)xfregs.alpha[i].hex : - (u32)xfregs.alpha[i].matsource) << 15; - } - uid->values[2] |= (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) << 31; - u32 *pcurvalue = &uid->values[3]; - for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) { - TexMtxInfo tinfo = xfregs.texMtxInfo[i]; - if (tinfo.texgentype != XF_TEXGEN_EMBOSS_MAP) - tinfo.hex &= 0x7ff; - if (tinfo.texgentype != XF_TEXGEN_REGULAR) - tinfo.projection = 0; - - u32 val = ((tinfo.hex >> 1) & 0x1ffff); - if (xfregs.dualTexTrans.enabled && tinfo.texgentype == XF_TEXGEN_REGULAR) { - // rewrite normalization and post index - val |= ((u32)xfregs.postMtxInfo[i].index << 17) | ((u32)xfregs.postMtxInfo[i].normalize << 23); - } - - switch (i & 3) { - case 0: pcurvalue[0] |= val; break; - case 1: pcurvalue[0] |= val << 24; pcurvalue[1] = val >> 8; ++pcurvalue; break; - case 2: pcurvalue[0] |= val << 16; pcurvalue[1] = val >> 16; ++pcurvalue; break; - case 3: pcurvalue[0] |= val << 8; ++pcurvalue; break; - } - } -} - -void GetSafeVertexShaderId(VERTEXSHADERUIDSAFE *uid, u32 components) -{ - // Just store all used registers here without caring whether we need all bits or less. - u32* ptr = uid->values; - *ptr++ = components; - *ptr++ = xfregs.numTexGen.hex; - *ptr++ = xfregs.numChan.hex; - *ptr++ = xfregs.dualTexTrans.hex; - - for (int i = 0; i < 2; ++i) { - *ptr++ = xfregs.color[i].hex; - *ptr++ = xfregs.alpha[i].hex; - } - *ptr++ = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; - for (unsigned int i = 0; i < 8; ++i) { - *ptr++ = xfregs.texMtxInfo[i].hex; - *ptr++ = xfregs.postMtxInfo[i].hex; - } - _assert_((ptr - uid->values) == uid->GetNumValues()); -} - -static char text[16384]; - -#define WRITE p+=sprintf - -char* GenerateVSOutputStruct(char* p, u32 components, API_TYPE api_type) -{ - WRITE(p, "struct VS_OUTPUT {\n"); - WRITE(p, " float4 pos : POSITION;\n"); - WRITE(p, " float4 colors_0 : COLOR0;\n"); - WRITE(p, " float4 colors_1 : COLOR1;\n"); - - if (xfregs.numTexGen.numTexGens < 7) { - for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) - WRITE(p, " float3 tex%d : TEXCOORD%d;\n", i, i); - WRITE(p, " float4 clipPos : TEXCOORD%d;\n", xfregs.numTexGen.numTexGens); - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - WRITE(p, " float4 Normal : TEXCOORD%d;\n", xfregs.numTexGen.numTexGens + 1); - } else { - // clip position is in w of first 4 texcoords - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - { - for (int i = 0; i < 8; ++i) - WRITE(p, " float4 tex%d : TEXCOORD%d;\n", i, i); - } - else - { - for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) - WRITE(p, " float%d tex%d : TEXCOORD%d;\n", i < 4 ? 4 : 3 , i, i); - } - } - WRITE(p, "};\n"); - - return p; -} - -const char *GenerateVertexShaderCode(u32 components, API_TYPE api_type) -{ - setlocale(LC_NUMERIC, "C"); // Reset locale for compilation - text[sizeof(text) - 1] = 0x7C; // canary - - _assert_(bpmem.genMode.numtexgens == xfregs.numTexGen.numTexGens); - _assert_(bpmem.genMode.numcolchans == xfregs.numChan.numColorChans); - - bool is_d3d = (api_type & API_D3D9 || api_type == API_D3D11); - u32 lightMask = 0; - if (xfregs.numChan.numColorChans > 0) - lightMask |= xfregs.color[0].GetFullLightMask() | xfregs.alpha[0].GetFullLightMask(); - if (xfregs.numChan.numColorChans > 1) - lightMask |= xfregs.color[1].GetFullLightMask() | xfregs.alpha[1].GetFullLightMask(); - - char *p = text; - WRITE(p, "//Vertex Shader: comp:%x, \n", components); - WRITE(p, "typedef struct { float4 T0, T1, T2; float4 N0, N1, N2; } s_"I_POSNORMALMATRIX";\n" - "typedef struct { float4 t; } FLT4;\n" - "typedef struct { FLT4 T[24]; } s_"I_TEXMATRICES";\n" - "typedef struct { FLT4 T[64]; } s_"I_TRANSFORMMATRICES";\n" - "typedef struct { FLT4 T[32]; } s_"I_NORMALMATRICES";\n" - "typedef struct { FLT4 T[64]; } s_"I_POSTTRANSFORMMATRICES";\n" - "typedef struct { float4 col; float4 cosatt; float4 distatt; float4 pos; float4 dir; } Light;\n" - "typedef struct { Light lights[8]; } s_"I_LIGHTS";\n" - "typedef struct { float4 C0, C1, C2, C3; } s_"I_MATERIALS";\n" - "typedef struct { float4 T0, T1, T2, T3; } s_"I_PROJECTION";\n" - ); - - p = GenerateVSOutputStruct(p, components, api_type); - - // uniforms - - WRITE(p, "uniform s_"I_TRANSFORMMATRICES" "I_TRANSFORMMATRICES" : register(c%d);\n", C_TRANSFORMMATRICES); - WRITE(p, "uniform s_"I_TEXMATRICES" "I_TEXMATRICES" : register(c%d);\n", C_TEXMATRICES); // also using tex matrices - WRITE(p, "uniform s_"I_NORMALMATRICES" "I_NORMALMATRICES" : register(c%d);\n", C_NORMALMATRICES); - WRITE(p, "uniform s_"I_POSNORMALMATRIX" "I_POSNORMALMATRIX" : register(c%d);\n", C_POSNORMALMATRIX); - WRITE(p, "uniform s_"I_POSTTRANSFORMMATRICES" "I_POSTTRANSFORMMATRICES" : register(c%d);\n", C_POSTTRANSFORMMATRICES); - WRITE(p, "uniform s_"I_LIGHTS" "I_LIGHTS" : register(c%d);\n", C_LIGHTS); - WRITE(p, "uniform s_"I_MATERIALS" "I_MATERIALS" : register(c%d);\n", C_MATERIALS); - WRITE(p, "uniform s_"I_PROJECTION" "I_PROJECTION" : register(c%d);\n", C_PROJECTION); - WRITE(p, "uniform float4 "I_DEPTHPARAMS" : register(c%d);\n", C_DEPTHPARAMS); - - WRITE(p, "VS_OUTPUT main(\n"); - - // inputs - if (components & VB_HAS_NRM0) - WRITE(p, " float3 rawnorm0 : NORMAL0,\n"); - if (components & VB_HAS_NRM1) { - if (is_d3d) - WRITE(p, " float3 rawnorm1 : NORMAL1,\n"); - else - WRITE(p, " float3 rawnorm1 : ATTR%d,\n", SHADER_NORM1_ATTRIB); - } - if (components & VB_HAS_NRM2) { - if (is_d3d) - WRITE(p, " float3 rawnorm2 : NORMAL2,\n"); - else - WRITE(p, " float3 rawnorm2 : ATTR%d,\n", SHADER_NORM2_ATTRIB); - } - if (components & VB_HAS_COL0) - WRITE(p, " float4 color0 : COLOR0,\n"); - if (components & VB_HAS_COL1) - WRITE(p, " float4 color1 : COLOR1,\n"); - for (int i = 0; i < 8; ++i) { - u32 hastexmtx = (components & (VB_HAS_TEXMTXIDX0<= 32 ? (posmtx-32) : posmtx;\n"); - WRITE(p, "float3 N0 = "I_NORMALMATRICES".T[normidx].t.xyz, N1 = "I_NORMALMATRICES".T[normidx+1].t.xyz, N2 = "I_NORMALMATRICES".T[normidx+2].t.xyz;\n"); - } - - if (components & VB_HAS_NRM0) - WRITE(p, "float3 _norm0 = normalize(float3(dot(N0, rawnorm0), dot(N1, rawnorm0), dot(N2, rawnorm0)));\n"); - if (components & VB_HAS_NRM1) - WRITE(p, "float3 _norm1 = float3(dot(N0, rawnorm1), dot(N1, rawnorm1), dot(N2, rawnorm1));\n"); - if (components & VB_HAS_NRM2) - WRITE(p, "float3 _norm2 = float3(dot(N0, rawnorm2), dot(N1, rawnorm2), dot(N2, rawnorm2));\n"); - } - else - { - WRITE(p, "float4 pos = float4(dot("I_POSNORMALMATRIX".T0, rawpos), dot("I_POSNORMALMATRIX".T1, rawpos), dot("I_POSNORMALMATRIX".T2, rawpos), 1.0f);\n"); - if (components & VB_HAS_NRM0) - WRITE(p, "float3 _norm0 = normalize(float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm0)));\n"); - if (components & VB_HAS_NRM1) - WRITE(p, "float3 _norm1 = float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm1));\n"); - if (components & VB_HAS_NRM2) - WRITE(p, "float3 _norm2 = float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm2));\n"); - } - - if (!(components & VB_HAS_NRM0)) - WRITE(p, "float3 _norm0 = float3(0.0f, 0.0f, 0.0f);\n"); - - - - WRITE(p, "o.pos = float4(dot("I_PROJECTION".T0, pos), dot("I_PROJECTION".T1, pos), dot("I_PROJECTION".T2, pos), dot("I_PROJECTION".T3, pos));\n"); - - WRITE(p, "float4 mat, lacc;\n" - "float3 ldir, h;\n" - "float dist, dist2, attn;\n"); - - if(xfregs.numChan.numColorChans == 0) - { - if (components & VB_HAS_COL0) - WRITE(p, "o.colors_0 = color0;\n"); - else - WRITE(p, "o.colors_0 = float4(1.0f, 1.0f, 1.0f, 1.0f);\n"); - } - - p = GenerateLightingShader(p, components, I_MATERIALS, I_LIGHTS, "color", "o.colors_"); - - if(xfregs.numChan.numColorChans < 2) - { - if (components & VB_HAS_COL1) - WRITE(p, "o.colors_1 = color1;\n"); - else - WRITE(p, "o.colors_1 = o.colors_0;\n"); - } - // special case if only pos and tex coord 0 and tex coord input is AB11 - // donko - this has caused problems in some games. removed for now. - bool texGenSpecialCase = false; - /*bool texGenSpecialCase = - ((g_VtxDesc.Hex & 0x60600L) == g_VtxDesc.Hex) && // only pos and tex coord 0 - (g_VtxDesc.Tex0Coord != NOT_PRESENT) && - (xfregs.texcoords[0].texmtxinfo.inputform == XF_TEXINPUT_AB11); - */ - - // transform texcoords - WRITE(p, "float4 coord = float4(0.0f, 0.0f, 1.0f, 1.0f);\n"); - for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) { - TexMtxInfo& texinfo = xfregs.texMtxInfo[i]; - - WRITE(p, "{\n"); - WRITE(p, "coord = float4(0.0f, 0.0f, 1.0f, 1.0f);\n"); - switch (texinfo.sourcerow) { - case XF_SRCGEOM_INROW: - _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); - WRITE(p, "coord = rawpos;\n"); // pos.w is 1 - break; - case XF_SRCNORMAL_INROW: - if (components & VB_HAS_NRM0) { - _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); - WRITE(p, "coord = float4(rawnorm0.xyz, 1.0f);\n"); - } - break; - case XF_SRCCOLORS_INROW: - _assert_( texinfo.texgentype == XF_TEXGEN_COLOR_STRGBC0 || texinfo.texgentype == XF_TEXGEN_COLOR_STRGBC1 ); - break; - case XF_SRCBINORMAL_T_INROW: - if (components & VB_HAS_NRM1) { - _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); - WRITE(p, "coord = float4(rawnorm1.xyz, 1.0f);\n"); - } - break; - case XF_SRCBINORMAL_B_INROW: - if (components & VB_HAS_NRM2) { - _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); - WRITE(p, "coord = float4(rawnorm2.xyz, 1.0f);\n"); - } - break; - default: - _assert_(texinfo.sourcerow <= XF_SRCTEX7_INROW); - if (components & (VB_HAS_UV0<<(texinfo.sourcerow - XF_SRCTEX0_INROW)) ) - WRITE(p, "coord = float4(tex%d.x, tex%d.y, 1.0f, 1.0f);\n", texinfo.sourcerow - XF_SRCTEX0_INROW, texinfo.sourcerow - XF_SRCTEX0_INROW); - break; - } - - // first transformation - switch (texinfo.texgentype) { - case XF_TEXGEN_EMBOSS_MAP: // calculate tex coords into bump map - - if (components & (VB_HAS_NRM1|VB_HAS_NRM2)) { - // transform the light dir into tangent space - WRITE(p, "ldir = normalize("I_LIGHTS".lights[%d].pos.xyz - pos.xyz);\n", texinfo.embosslightshift); - WRITE(p, "o.tex%d.xyz = o.tex%d.xyz + float3(dot(ldir, _norm1), dot(ldir, _norm2), 0.0f);\n", i, texinfo.embosssourceshift); - } - else - { - _assert_(0); // should have normals - WRITE(p, "o.tex%d.xyz = o.tex%d.xyz;\n", i, texinfo.embosssourceshift); - } - - break; - case XF_TEXGEN_COLOR_STRGBC0: - _assert_(texinfo.sourcerow == XF_SRCCOLORS_INROW); - WRITE(p, "o.tex%d.xyz = float3(o.colors_0.x, o.colors_0.y, 1);\n", i); - break; - case XF_TEXGEN_COLOR_STRGBC1: - _assert_(texinfo.sourcerow == XF_SRCCOLORS_INROW); - WRITE(p, "o.tex%d.xyz = float3(o.colors_1.x, o.colors_1.y, 1);\n", i); - break; - case XF_TEXGEN_REGULAR: - default: - if (components & (VB_HAS_TEXMTXIDX0< +#include + +#include "NativeVertexFormat.h" + +#include "BPMemory.h" +#include "CPMemory.h" +#include "LightingShaderGen.h" +#include "VertexShaderGen.h" +#include "VideoConfig.h" + +VERTEXSHADERUID last_vertex_shader_uid; + +// Mash together all the inputs that contribute to the code of a generated vertex shader into +// a unique identifier, basically containing all the bits. Yup, it's a lot .... +void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components) +{ + uid->values[0] = components | + (xfregs.numTexGen.numTexGens << 23) | + (xfregs.numChan.numColorChans << 27) | + (xfregs.dualTexTrans.enabled << 29); + + for (int i = 0; i < xfregs.numChan.numColorChans; ++i) { + uid->values[1+i] = xfregs.color[i].enablelighting ? + (u32)xfregs.color[i].hex : + (u32)xfregs.color[i].matsource; + uid->values[1+i] |= (xfregs.alpha[i].enablelighting ? + (u32)xfregs.alpha[i].hex : + (u32)xfregs.alpha[i].matsource) << 15; + } + uid->values[2] |= (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) << 31; + u32 *pcurvalue = &uid->values[3]; + for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) { + TexMtxInfo tinfo = xfregs.texMtxInfo[i]; + if (tinfo.texgentype != XF_TEXGEN_EMBOSS_MAP) + tinfo.hex &= 0x7ff; + if (tinfo.texgentype != XF_TEXGEN_REGULAR) + tinfo.projection = 0; + + u32 val = ((tinfo.hex >> 1) & 0x1ffff); + if (xfregs.dualTexTrans.enabled && tinfo.texgentype == XF_TEXGEN_REGULAR) { + // rewrite normalization and post index + val |= ((u32)xfregs.postMtxInfo[i].index << 17) | ((u32)xfregs.postMtxInfo[i].normalize << 23); + } + + switch (i & 3) { + case 0: pcurvalue[0] |= val; break; + case 1: pcurvalue[0] |= val << 24; pcurvalue[1] = val >> 8; ++pcurvalue; break; + case 2: pcurvalue[0] |= val << 16; pcurvalue[1] = val >> 16; ++pcurvalue; break; + case 3: pcurvalue[0] |= val << 8; ++pcurvalue; break; + } + } +} + +void GetSafeVertexShaderId(VERTEXSHADERUIDSAFE *uid, u32 components) +{ + // Just store all used registers here without caring whether we need all bits or less. + u32* ptr = uid->values; + *ptr++ = components; + *ptr++ = xfregs.numTexGen.hex; + *ptr++ = xfregs.numChan.hex; + *ptr++ = xfregs.dualTexTrans.hex; + + for (int i = 0; i < 2; ++i) { + *ptr++ = xfregs.color[i].hex; + *ptr++ = xfregs.alpha[i].hex; + } + *ptr++ = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; + for (unsigned int i = 0; i < 8; ++i) { + *ptr++ = xfregs.texMtxInfo[i].hex; + *ptr++ = xfregs.postMtxInfo[i].hex; + } + _assert_((ptr - uid->values) == uid->GetNumValues()); +} + + +void ValidateVertexShaderIDs(API_TYPE api, VERTEXSHADERUIDSAFE old_id, const std::string& old_code, u32 components) +{ + VERTEXSHADERUIDSAFE new_id; + GetSafeVertexShaderId(&new_id, components); + + if (!(old_id == new_id)) + { + std::string new_code(GenerateVertexShaderCode(components, api)); + if (old_code != new_code) + { + _assert_(old_id.GetNumValues() == new_id.GetNumValues()); + + char msg[8192]; + char* ptr = msg; + ptr += sprintf(ptr, "Vertex shader IDs matched but unique IDs did not!\nUnique IDs (old <-> new):\n"); + const int N = new_id.GetNumValues(); + for (int i = 0; i < N/2; ++i) + ptr += sprintf(ptr, "%02d, %08X %08X | %08X %08X\n", 2*i, old_id.values[2*i], old_id.values[2*i+1], + new_id.values[2*i], new_id.values[2*i+1]); + if (N % 2) + ptr += sprintf(ptr, "%02d, %08X | %08X\n", N-1, old_id.values[N-1], new_id.values[N-1]); + + static int num_failures = 0; + char szTemp[MAX_PATH]; + sprintf(szTemp, "%svsuid_mismatch_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++); + std::ofstream file(szTemp); + file << msg; + file.close(); + + PanicAlert("Unique pixel shader ID mismatch!\n\nReport this to the devs, along with the contents of %s.", szTemp); + } + } +} + + +static char text[16384]; + +#define WRITE p+=sprintf + +char* GenerateVSOutputStruct(char* p, u32 components, API_TYPE api_type) +{ + WRITE(p, "struct VS_OUTPUT {\n"); + WRITE(p, " float4 pos : POSITION;\n"); + WRITE(p, " float4 colors_0 : COLOR0;\n"); + WRITE(p, " float4 colors_1 : COLOR1;\n"); + + if (xfregs.numTexGen.numTexGens < 7) { + for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) + WRITE(p, " float3 tex%d : TEXCOORD%d;\n", i, i); + WRITE(p, " float4 clipPos : TEXCOORD%d;\n", xfregs.numTexGen.numTexGens); + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + WRITE(p, " float4 Normal : TEXCOORD%d;\n", xfregs.numTexGen.numTexGens + 1); + } else { + // clip position is in w of first 4 texcoords + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + { + for (int i = 0; i < 8; ++i) + WRITE(p, " float4 tex%d : TEXCOORD%d;\n", i, i); + } + else + { + for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) + WRITE(p, " float%d tex%d : TEXCOORD%d;\n", i < 4 ? 4 : 3 , i, i); + } + } + WRITE(p, "};\n"); + + return p; +} + +const char *GenerateVertexShaderCode(u32 components, API_TYPE api_type) +{ + setlocale(LC_NUMERIC, "C"); // Reset locale for compilation + text[sizeof(text) - 1] = 0x7C; // canary + + _assert_(bpmem.genMode.numtexgens == xfregs.numTexGen.numTexGens); + _assert_(bpmem.genMode.numcolchans == xfregs.numChan.numColorChans); + + bool is_d3d = (api_type & API_D3D9 || api_type == API_D3D11); + u32 lightMask = 0; + if (xfregs.numChan.numColorChans > 0) + lightMask |= xfregs.color[0].GetFullLightMask() | xfregs.alpha[0].GetFullLightMask(); + if (xfregs.numChan.numColorChans > 1) + lightMask |= xfregs.color[1].GetFullLightMask() | xfregs.alpha[1].GetFullLightMask(); + + char *p = text; + WRITE(p, "//Vertex Shader: comp:%x, \n", components); + WRITE(p, "typedef struct { float4 T0, T1, T2; float4 N0, N1, N2; } s_"I_POSNORMALMATRIX";\n" + "typedef struct { float4 t; } FLT4;\n" + "typedef struct { FLT4 T[24]; } s_"I_TEXMATRICES";\n" + "typedef struct { FLT4 T[64]; } s_"I_TRANSFORMMATRICES";\n" + "typedef struct { FLT4 T[32]; } s_"I_NORMALMATRICES";\n" + "typedef struct { FLT4 T[64]; } s_"I_POSTTRANSFORMMATRICES";\n" + "typedef struct { float4 col; float4 cosatt; float4 distatt; float4 pos; float4 dir; } Light;\n" + "typedef struct { Light lights[8]; } s_"I_LIGHTS";\n" + "typedef struct { float4 C0, C1, C2, C3; } s_"I_MATERIALS";\n" + "typedef struct { float4 T0, T1, T2, T3; } s_"I_PROJECTION";\n" + ); + + p = GenerateVSOutputStruct(p, components, api_type); + + // uniforms + + WRITE(p, "uniform s_"I_TRANSFORMMATRICES" "I_TRANSFORMMATRICES" : register(c%d);\n", C_TRANSFORMMATRICES); + WRITE(p, "uniform s_"I_TEXMATRICES" "I_TEXMATRICES" : register(c%d);\n", C_TEXMATRICES); // also using tex matrices + WRITE(p, "uniform s_"I_NORMALMATRICES" "I_NORMALMATRICES" : register(c%d);\n", C_NORMALMATRICES); + WRITE(p, "uniform s_"I_POSNORMALMATRIX" "I_POSNORMALMATRIX" : register(c%d);\n", C_POSNORMALMATRIX); + WRITE(p, "uniform s_"I_POSTTRANSFORMMATRICES" "I_POSTTRANSFORMMATRICES" : register(c%d);\n", C_POSTTRANSFORMMATRICES); + WRITE(p, "uniform s_"I_LIGHTS" "I_LIGHTS" : register(c%d);\n", C_LIGHTS); + WRITE(p, "uniform s_"I_MATERIALS" "I_MATERIALS" : register(c%d);\n", C_MATERIALS); + WRITE(p, "uniform s_"I_PROJECTION" "I_PROJECTION" : register(c%d);\n", C_PROJECTION); + WRITE(p, "uniform float4 "I_DEPTHPARAMS" : register(c%d);\n", C_DEPTHPARAMS); + + WRITE(p, "VS_OUTPUT main(\n"); + + // inputs + if (components & VB_HAS_NRM0) + WRITE(p, " float3 rawnorm0 : NORMAL0,\n"); + if (components & VB_HAS_NRM1) { + if (is_d3d) + WRITE(p, " float3 rawnorm1 : NORMAL1,\n"); + else + WRITE(p, " float3 rawnorm1 : ATTR%d,\n", SHADER_NORM1_ATTRIB); + } + if (components & VB_HAS_NRM2) { + if (is_d3d) + WRITE(p, " float3 rawnorm2 : NORMAL2,\n"); + else + WRITE(p, " float3 rawnorm2 : ATTR%d,\n", SHADER_NORM2_ATTRIB); + } + if (components & VB_HAS_COL0) + WRITE(p, " float4 color0 : COLOR0,\n"); + if (components & VB_HAS_COL1) + WRITE(p, " float4 color1 : COLOR1,\n"); + for (int i = 0; i < 8; ++i) { + u32 hastexmtx = (components & (VB_HAS_TEXMTXIDX0<= 32 ? (posmtx-32) : posmtx;\n"); + WRITE(p, "float3 N0 = "I_NORMALMATRICES".T[normidx].t.xyz, N1 = "I_NORMALMATRICES".T[normidx+1].t.xyz, N2 = "I_NORMALMATRICES".T[normidx+2].t.xyz;\n"); + } + + if (components & VB_HAS_NRM0) + WRITE(p, "float3 _norm0 = normalize(float3(dot(N0, rawnorm0), dot(N1, rawnorm0), dot(N2, rawnorm0)));\n"); + if (components & VB_HAS_NRM1) + WRITE(p, "float3 _norm1 = float3(dot(N0, rawnorm1), dot(N1, rawnorm1), dot(N2, rawnorm1));\n"); + if (components & VB_HAS_NRM2) + WRITE(p, "float3 _norm2 = float3(dot(N0, rawnorm2), dot(N1, rawnorm2), dot(N2, rawnorm2));\n"); + } + else + { + WRITE(p, "float4 pos = float4(dot("I_POSNORMALMATRIX".T0, rawpos), dot("I_POSNORMALMATRIX".T1, rawpos), dot("I_POSNORMALMATRIX".T2, rawpos), 1.0f);\n"); + if (components & VB_HAS_NRM0) + WRITE(p, "float3 _norm0 = normalize(float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm0)));\n"); + if (components & VB_HAS_NRM1) + WRITE(p, "float3 _norm1 = float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm1));\n"); + if (components & VB_HAS_NRM2) + WRITE(p, "float3 _norm2 = float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm2));\n"); + } + + if (!(components & VB_HAS_NRM0)) + WRITE(p, "float3 _norm0 = float3(0.0f, 0.0f, 0.0f);\n"); + + + + WRITE(p, "o.pos = float4(dot("I_PROJECTION".T0, pos), dot("I_PROJECTION".T1, pos), dot("I_PROJECTION".T2, pos), dot("I_PROJECTION".T3, pos));\n"); + + WRITE(p, "float4 mat, lacc;\n" + "float3 ldir, h;\n" + "float dist, dist2, attn;\n"); + + if(xfregs.numChan.numColorChans == 0) + { + if (components & VB_HAS_COL0) + WRITE(p, "o.colors_0 = color0;\n"); + else + WRITE(p, "o.colors_0 = float4(1.0f, 1.0f, 1.0f, 1.0f);\n"); + } + + p = GenerateLightingShader(p, components, I_MATERIALS, I_LIGHTS, "color", "o.colors_"); + + if(xfregs.numChan.numColorChans < 2) + { + if (components & VB_HAS_COL1) + WRITE(p, "o.colors_1 = color1;\n"); + else + WRITE(p, "o.colors_1 = o.colors_0;\n"); + } + // special case if only pos and tex coord 0 and tex coord input is AB11 + // donko - this has caused problems in some games. removed for now. + bool texGenSpecialCase = false; + /*bool texGenSpecialCase = + ((g_VtxDesc.Hex & 0x60600L) == g_VtxDesc.Hex) && // only pos and tex coord 0 + (g_VtxDesc.Tex0Coord != NOT_PRESENT) && + (xfregs.texcoords[0].texmtxinfo.inputform == XF_TEXINPUT_AB11); + */ + + // transform texcoords + WRITE(p, "float4 coord = float4(0.0f, 0.0f, 1.0f, 1.0f);\n"); + for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) { + TexMtxInfo& texinfo = xfregs.texMtxInfo[i]; + + WRITE(p, "{\n"); + WRITE(p, "coord = float4(0.0f, 0.0f, 1.0f, 1.0f);\n"); + switch (texinfo.sourcerow) { + case XF_SRCGEOM_INROW: + _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); + WRITE(p, "coord = rawpos;\n"); // pos.w is 1 + break; + case XF_SRCNORMAL_INROW: + if (components & VB_HAS_NRM0) { + _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); + WRITE(p, "coord = float4(rawnorm0.xyz, 1.0f);\n"); + } + break; + case XF_SRCCOLORS_INROW: + _assert_( texinfo.texgentype == XF_TEXGEN_COLOR_STRGBC0 || texinfo.texgentype == XF_TEXGEN_COLOR_STRGBC1 ); + break; + case XF_SRCBINORMAL_T_INROW: + if (components & VB_HAS_NRM1) { + _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); + WRITE(p, "coord = float4(rawnorm1.xyz, 1.0f);\n"); + } + break; + case XF_SRCBINORMAL_B_INROW: + if (components & VB_HAS_NRM2) { + _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); + WRITE(p, "coord = float4(rawnorm2.xyz, 1.0f);\n"); + } + break; + default: + _assert_(texinfo.sourcerow <= XF_SRCTEX7_INROW); + if (components & (VB_HAS_UV0<<(texinfo.sourcerow - XF_SRCTEX0_INROW)) ) + WRITE(p, "coord = float4(tex%d.x, tex%d.y, 1.0f, 1.0f);\n", texinfo.sourcerow - XF_SRCTEX0_INROW, texinfo.sourcerow - XF_SRCTEX0_INROW); + break; + } + + // first transformation + switch (texinfo.texgentype) { + case XF_TEXGEN_EMBOSS_MAP: // calculate tex coords into bump map + + if (components & (VB_HAS_NRM1|VB_HAS_NRM2)) { + // transform the light dir into tangent space + WRITE(p, "ldir = normalize("I_LIGHTS".lights[%d].pos.xyz - pos.xyz);\n", texinfo.embosslightshift); + WRITE(p, "o.tex%d.xyz = o.tex%d.xyz + float3(dot(ldir, _norm1), dot(ldir, _norm2), 0.0f);\n", i, texinfo.embosssourceshift); + } + else + { + _assert_(0); // should have normals + WRITE(p, "o.tex%d.xyz = o.tex%d.xyz;\n", i, texinfo.embosssourceshift); + } + + break; + case XF_TEXGEN_COLOR_STRGBC0: + _assert_(texinfo.sourcerow == XF_SRCCOLORS_INROW); + WRITE(p, "o.tex%d.xyz = float3(o.colors_0.x, o.colors_0.y, 1);\n", i); + break; + case XF_TEXGEN_COLOR_STRGBC1: + _assert_(texinfo.sourcerow == XF_SRCCOLORS_INROW); + WRITE(p, "o.tex%d.xyz = float3(o.colors_1.x, o.colors_1.y, 1);\n", i); + break; + case XF_TEXGEN_REGULAR: + default: + if (components & (VB_HAS_TEXMTXIDX0< VERTEXSHADERUIDSAFE; // components is included in the uid. char* GenerateVSOutputStruct(char* p, u32 components, API_TYPE api_type); const char *GenerateVertexShaderCode(u32 components, API_TYPE api_type); + void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components); void GetSafeVertexShaderId(VERTEXSHADERUIDSAFE *uid, u32 components); +// Used to make sure that our optimized vertex shader IDs don't lose any possible shader code changes +void ValidateVertexShaderIDs(API_TYPE api, VERTEXSHADERUIDSAFE old_id, const std::string& old_code, u32 components); + extern VERTEXSHADERUID last_vertex_shader_uid; #endif // GCOGL_VERTEXSHADER_H -- cgit v1.2.3 From 5c14a24ce115a85d9edfdd11bbf6d78ccd9eddaa Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Fri, 9 Sep 2011 21:34:46 +0200 Subject: Make shader ID validation optional by adding a gfx setting called "EnableShaderDebugging". Setting this to True will enable additional checks if the shader cache misses any relevant register changes. --- Source/Core/VideoCommon/Src/VideoConfig.cpp | 4 ++++ Source/Core/VideoCommon/Src/VideoConfig.h | 3 +++ 2 files changed, 7 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/VideoConfig.cpp b/Source/Core/VideoCommon/Src/VideoConfig.cpp index 8bad7b005a..2c7788c9cc 100644 --- a/Source/Core/VideoCommon/Src/VideoConfig.cpp +++ b/Source/Core/VideoCommon/Src/VideoConfig.cpp @@ -96,6 +96,8 @@ void VideoConfig::Load(const char *ini_file) iniFile.Get("Settings", "EnableOpenCL", &bEnableOpenCL, false); iniFile.Get("Settings", "OMPDecoder", &bOMPDecoder, false); + iniFile.Get("Settings", "EnableShaderDebugging", &bEnableShaderDebugging, false); + iniFile.Get("Enhancements", "ForceFiltering", &bForceFiltering, 0); iniFile.Get("Enhancements", "MaxAnisotropy", &iMaxAnisotropy, 0); // NOTE - this is x in (1 << x) iniFile.Get("Enhancements", "PostProcessingShader", &sPostProcessingShader, ""); @@ -231,6 +233,8 @@ void VideoConfig::Save(const char *ini_file) iniFile.Set("Settings", "EnableOpenCL", bEnableOpenCL); iniFile.Set("Settings", "OMPDecoder", bOMPDecoder); + iniFile.Set("Settings", "EnableShaderDebugging", bEnableShaderDebugging); + iniFile.Set("Enhancements", "ForceFiltering", bForceFiltering); iniFile.Set("Enhancements", "MaxAnisotropy", iMaxAnisotropy); iniFile.Set("Enhancements", "PostProcessingShader", sPostProcessingShader); diff --git a/Source/Core/VideoCommon/Src/VideoConfig.h b/Source/Core/VideoCommon/Src/VideoConfig.h index 92a8b67741..83ec08784d 100644 --- a/Source/Core/VideoCommon/Src/VideoConfig.h +++ b/Source/Core/VideoCommon/Src/VideoConfig.h @@ -147,6 +147,9 @@ struct VideoConfig // D3D only config, mostly to be merged into the above int iAdapter; + // Debugging + bool bEnableShaderDebugging; + // Static config per API // TODO: Move this out of VideoConfig struct -- cgit v1.2.3 From a021dd7b79062d20552852d32387cf6b36ab06e6 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Fri, 9 Sep 2011 21:45:11 +0200 Subject: Small fix to the previous commit. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 3 +++ Source/Core/VideoCommon/Src/VertexShaderGen.cpp | 3 +++ 2 files changed, 6 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 25007be93b..1ea7479990 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -243,6 +243,9 @@ void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) void ValidatePixelShaderIDs(API_TYPE api, PIXELSHADERUIDSAFE old_id, const std::string& old_code, DSTALPHA_MODE dstAlphaMode, u32 components) { + if (!g_ActiveConfig.bEnableShaderDebugging) + return; + PIXELSHADERUIDSAFE new_id; GetSafePixelShaderId(&new_id, dstAlphaMode); diff --git a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp index 902f645bec..f3dab89173 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp @@ -93,6 +93,9 @@ void GetSafeVertexShaderId(VERTEXSHADERUIDSAFE *uid, u32 components) void ValidateVertexShaderIDs(API_TYPE api, VERTEXSHADERUIDSAFE old_id, const std::string& old_code, u32 components) { + if (!g_ActiveConfig.bEnableShaderDebugging) + return; + VERTEXSHADERUIDSAFE new_id; GetSafeVertexShaderId(&new_id, components); -- cgit v1.2.3 From 5d075ce507c180dc89b9731eaa2f52e729caf8f6 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sat, 10 Sep 2011 03:10:28 +0200 Subject: - D3D9: pass the correct API type to ValidatePixelShaderIDs - don't load shader cache from disk in d3d9/11 if shader debugging is enabled (we won't have any info about the source shader code otherwise, etc) - dump shader source codes on safe UIDs mismatch Thanks to LordMark and [SS] for reporting those to me ;) --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 2 ++ Source/Core/VideoCommon/Src/VertexShaderGen.cpp | 2 ++ 2 files changed, 4 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 1ea7479990..7937c695ce 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -271,6 +271,8 @@ void ValidatePixelShaderIDs(API_TYPE api, PIXELSHADERUIDSAFE old_id, const std:: sprintf(szTemp, "%spsuid_mismatch_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++); std::ofstream file(szTemp); file << msg; + file << "\n\nOld shader code:\n" << old_code; + file << "\n\nNew shader code:\n" << new_code; file.close(); PanicAlert("Unique pixel shader ID mismatch!\n\nReport this to the devs, along with the contents of %s.", szTemp); diff --git a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp index f3dab89173..5c6c12887a 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp @@ -121,6 +121,8 @@ void ValidateVertexShaderIDs(API_TYPE api, VERTEXSHADERUIDSAFE old_id, const std sprintf(szTemp, "%svsuid_mismatch_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++); std::ofstream file(szTemp); file << msg; + file << "\n\nOld shader code:\n" << old_code; + file << "\n\nNew shader code:\n" << new_code; file.close(); PanicAlert("Unique pixel shader ID mismatch!\n\nReport this to the devs, along with the contents of %s.", szTemp); -- cgit v1.2.3 From 8c691767da71d041580d362b3b822d50e85d46da Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sat, 27 Aug 2011 20:42:11 +0200 Subject: Various changes which improve FreeBSD support. Patches by martymac, all credits go to him ;) --- Source/Core/VideoCommon/Src/AVIDump.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/AVIDump.cpp b/Source/Core/VideoCommon/Src/AVIDump.cpp index cfd34e5cdb..a5a6098489 100644 --- a/Source/Core/VideoCommon/Src/AVIDump.cpp +++ b/Source/Core/VideoCommon/Src/AVIDump.cpp @@ -15,6 +15,10 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ +#if defined(__FreeBSD__) +#define __STDC_CONSTANT_MACROS 1 +#endif + #include "AVIDump.h" #include "HW/VideoInterface.h" //for TargetRefreshRate #include "VideoConfig.h" -- cgit v1.2.3 From bd4a5b5ef692bce25dcfc60e13df3f08edc84bf8 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Thu, 8 Sep 2011 15:39:03 +0200 Subject: Implement frame dumping in D3D11. Fixes issue 4831. --- Source/Core/VideoCommon/Src/RenderBase.cpp | 2 +- Source/Core/VideoCommon/Src/RenderBase.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/RenderBase.cpp b/Source/Core/VideoCommon/Src/RenderBase.cpp index d89e582860..06f4c81ab6 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.cpp +++ b/Source/Core/VideoCommon/Src/RenderBase.cpp @@ -52,7 +52,7 @@ int OSDChoice, OSDTime; Renderer *g_renderer = NULL; -bool s_bLastFrameDumped = false; +bool bLastFrameDumped = false; std::mutex Renderer::s_criticalScreenshot; std::string Renderer::s_sScreenshotName; diff --git a/Source/Core/VideoCommon/Src/RenderBase.h b/Source/Core/VideoCommon/Src/RenderBase.h index b2c2c7ecfb..48f096851f 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.h +++ b/Source/Core/VideoCommon/Src/RenderBase.h @@ -41,7 +41,7 @@ extern int frameCount; extern int OSDChoice, OSDTime; -extern bool s_bLastFrameDumped; +extern bool bLastFrameDumped; // Renderer really isn't a very good name for this class - it's more like "Misc". // The long term goal is to get rid of this class and replace it with others that make -- cgit v1.2.3 From c710ea33f97b5b64eef1c56c2f67a666146d41f6 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Thu, 8 Sep 2011 17:09:24 +0200 Subject: Merge some frame dumping code to VideoCommon, fixes a memory leak in D3D9 and OpenGL if emulation is stopped while dumping frames. Breaks D3D11 frame dumping for some weird reason (memory corruption or whatever?). --- Source/Core/VideoCommon/Src/RenderBase.cpp | 17 +++++++++++++++-- Source/Core/VideoCommon/Src/RenderBase.h | 13 ++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/RenderBase.cpp b/Source/Core/VideoCommon/Src/RenderBase.cpp index 06f4c81ab6..fa6ba018c7 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.cpp +++ b/Source/Core/VideoCommon/Src/RenderBase.cpp @@ -42,6 +42,7 @@ #include "Host.h" #include "XFMemory.h" #include "FifoPlayer/FifoRecorder.h" +#include "AVIDump.h" #include #include @@ -52,7 +53,6 @@ int OSDChoice, OSDTime; Renderer *g_renderer = NULL; -bool bLastFrameDumped = false; std::mutex Renderer::s_criticalScreenshot; std::string Renderer::s_sScreenshotName; @@ -81,15 +81,28 @@ bool Renderer::s_EnableDLCachingAfterRecording; unsigned int Renderer::prev_efb_format = (unsigned int)-1; -Renderer::Renderer() +Renderer::Renderer() : frame_data(NULL), bLastFrameDumped(false) { UpdateActiveConfig(); + +#if defined _WIN32 || defined HAVE_LIBAV + bAVIDumping = false; +#endif } Renderer::~Renderer() { // invalidate previous efb format prev_efb_format = (unsigned int)-1; + +#if defined _WIN32 || defined HAVE_LIBAV + if (g_ActiveConfig.bDumpFrames && bLastFrameDumped && bAVIDumping) + AVIDump::Stop(); +#else + if (f_pFrameDump.IsOpen()) + f_pFrameDump.Close(); +#endif + delete[] frame_data; } void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc, float Gamma) diff --git a/Source/Core/VideoCommon/Src/RenderBase.h b/Source/Core/VideoCommon/Src/RenderBase.h index 48f096851f..e8d4c55a20 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.h +++ b/Source/Core/VideoCommon/Src/RenderBase.h @@ -132,9 +132,6 @@ public: protected: - static std::mutex s_criticalScreenshot; - static std::string s_sScreenshotName; - static void CalculateTargetScale(int x, int y, int &scaledX, int &scaledY); static bool CalculateTargetSize(int multiplier = 1); static void CalculateXYScale(const TargetRectangle& dst_rect); @@ -143,6 +140,16 @@ protected: static void RecordVideoMemory(); static volatile bool s_bScreenshot; + static std::mutex s_criticalScreenshot; + static std::string s_sScreenshotName; + +#if defined _WIN32 || defined HAVE_LIBAV + bool bAVIDumping; +#else + File::IOFile pFrameDump; +#endif + char* frame_data; + bool bLastFrameDumped; // The framebuffer size static int s_target_width; -- cgit v1.2.3 From f041eee23b51b11f198b993b81820597af13a30b Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Fri, 9 Sep 2011 16:30:29 +0200 Subject: Compile fix. --- Source/Core/VideoCommon/Src/RenderBase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/RenderBase.cpp b/Source/Core/VideoCommon/Src/RenderBase.cpp index fa6ba018c7..3b06bbb1c0 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.cpp +++ b/Source/Core/VideoCommon/Src/RenderBase.cpp @@ -99,8 +99,8 @@ Renderer::~Renderer() if (g_ActiveConfig.bDumpFrames && bLastFrameDumped && bAVIDumping) AVIDump::Stop(); #else - if (f_pFrameDump.IsOpen()) - f_pFrameDump.Close(); + if (pFrameDump.IsOpen()) + pFrameDump.Close(); #endif delete[] frame_data; } -- cgit v1.2.3 From ca7e8a9e88aeb2271ed04af4ff3994286c00a90c Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Thu, 29 Sep 2011 21:21:09 +0200 Subject: Fix pixel lighting. --- Source/Core/VideoCommon/Src/LightingShaderGen.cpp | 15 +++++++++++++++ Source/Core/VideoCommon/Src/LightingShaderGen.h | 3 +++ Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 18 +++++++++++------- Source/Core/VideoCommon/Src/PixelShaderGen.h | 4 ++-- Source/Core/VideoCommon/Src/VertexShaderGen.cpp | 14 +++++--------- 5 files changed, 36 insertions(+), 18 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/LightingShaderGen.cpp b/Source/Core/VideoCommon/Src/LightingShaderGen.cpp index 6824fbd2fc..a506acadfc 100644 --- a/Source/Core/VideoCommon/Src/LightingShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/LightingShaderGen.cpp @@ -21,6 +21,21 @@ #define WRITE p+=sprintf +int GetLightingShaderId(u32* out) +{ + for (int i = 0; i < xfregs.numChan.numColorChans; ++i) + { + out[i] = xfregs.color[i].enablelighting ? + (u32)xfregs.color[i].hex : + (u32)xfregs.color[i].matsource; + out[i] |= (xfregs.alpha[i].enablelighting ? + (u32)xfregs.alpha[i].hex : + (u32)xfregs.alpha[i].matsource) << 15; + } + _assert_(xfregs.numChan.numColorChans <= 2); + return xfregs.numChan.numColorChans; +} + // coloralpha - 1 if color, 2 if alpha char *GenerateLightShader(char *p, int index, const LitChannel& chan, const char* lightsName, int coloralpha) { diff --git a/Source/Core/VideoCommon/Src/LightingShaderGen.h b/Source/Core/VideoCommon/Src/LightingShaderGen.h index 21da8ca01a..e72623a4d2 100644 --- a/Source/Core/VideoCommon/Src/LightingShaderGen.h +++ b/Source/Core/VideoCommon/Src/LightingShaderGen.h @@ -18,6 +18,9 @@ #ifndef _LIGHTINGSHADERGEN_H_ #define _LIGHTINGSHADERGEN_H_ +#include "CommonTypes.h" + +int GetLightingShaderId(u32* out); char *GenerateLightingShader(char *p, int components, const char* materialsName, const char* lightsName, const char* inColorName, const char* dest); #endif // _LIGHTINGSHADERGEN_H_ diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 7937c695ce..52a4477390 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -125,16 +125,11 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) if (alphaPreTest == 1 || (alphaPreTest && !DepthTextureEnable && dstAlphaMode == DSTALPHA_ALPHA_PASS)) { // Courtesy of PreAlphaTest, we're done already ;) - // TODO: There's a comment including bpmem.genmode.numindstages.. shouldnt really bother about that though. + // NOTE: The comment header of generated shaders depends on the value of bpmem.genmode.numindstages.. shouldnt really bother about that though. uid->num_values = 1; return; } - if (enablePL) - { - // TODO: Include register states for lighting shader - } - for (unsigned int i = 0; i < bpmem.genMode.numtexgens; ++i) { if (18+i < 32) @@ -194,7 +189,12 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) ptr[0] |= bpmem.fogRange.Base.Enabled << 17; // 1 } } - uid->num_values = (ptr+1) - uid->values; + + ++ptr; + if (enablePL) + ptr += GetLightingShaderId(ptr); + + uid->num_values = ptr - uid->values; } void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) @@ -212,6 +212,10 @@ void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) if (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) { // TODO: Include register states for lighting shader + *ptr++ = xfregs.color[0].hex; + *ptr++ = xfregs.alpha[0].hex; + *ptr++ = xfregs.color[1].hex; + *ptr++ = xfregs.alpha[1].hex; } for (unsigned int i = 0; i < 8; ++i) diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.h b/Source/Core/VideoCommon/Src/PixelShaderGen.h index 8031fdfbb2..b24b656784 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.h +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.h @@ -44,8 +44,8 @@ #define C_PLIGHTS (C_FOG + 3) #define C_PMATERIALS (C_PLIGHTS + 40) #define C_PENVCONST_END (C_PMATERIALS + 4) -#define PIXELSHADERUID_MAX_VALUES 67 -#define PIXELSHADERUID_MAX_VALUES_SAFE 115 +#define PIXELSHADERUID_MAX_VALUES 69 +#define PIXELSHADERUID_MAX_VALUES_SAFE 117 // DO NOT make anything in this class virtual. template diff --git a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp index 5c6c12887a..a492d5a201 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp @@ -37,14 +37,9 @@ void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components) (xfregs.numChan.numColorChans << 27) | (xfregs.dualTexTrans.enabled << 29); - for (int i = 0; i < xfregs.numChan.numColorChans; ++i) { - uid->values[1+i] = xfregs.color[i].enablelighting ? - (u32)xfregs.color[i].hex : - (u32)xfregs.color[i].matsource; - uid->values[1+i] |= (xfregs.alpha[i].enablelighting ? - (u32)xfregs.alpha[i].hex : - (u32)xfregs.alpha[i].matsource) << 15; - } + // TODO: If pixel lighting is enabled, do we even have to bother about storing lighting related registers here? + GetLightingShaderId(&uid->values[1]); + uid->values[2] |= (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) << 31; u32 *pcurvalue = &uid->values[3]; for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) { @@ -306,7 +301,8 @@ const char *GenerateVertexShaderCode(u32 components, API_TYPE api_type) else WRITE(p, "o.colors_0 = float4(1.0f, 1.0f, 1.0f, 1.0f);\n"); } - + + // TODO: This probably isn't necessary if pixel lighting is enabled. p = GenerateLightingShader(p, components, I_MATERIALS, I_LIGHTS, "color", "o.colors_"); if(xfregs.numChan.numColorChans < 2) -- cgit v1.2.3 From ddfe219293e3af9e04f7020f0557736e1762370b Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Thu, 29 Sep 2011 21:52:13 +0200 Subject: Fixup line endings. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 2482 +++++++++++------------ Source/Core/VideoCommon/Src/VertexShaderGen.cpp | 1006 ++++----- 2 files changed, 1744 insertions(+), 1744 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 52a4477390..f52c6b4190 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -1,1242 +1,1242 @@ -// Copyright (C) 2003 Dolphin Project. - -// This program 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, version 2.0. - -// This program 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 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include -#include -#include - -#include "LightingShaderGen.h" -#include "PixelShaderGen.h" -#include "XFMemory.h" // for texture projection mode -#include "BPMemory.h" -#include "VideoConfig.h" -#include "NativeVertexFormat.h" - -PIXELSHADERUID last_pixel_shader_uid; - -static int AlphaPreTest(); - -static void StageHash(int stage, u32* out) -{ - out[0] |= bpmem.combiners[stage].colorC.hex & 0xFFFFFF; // 24 - u32 alphaC = bpmem.combiners[stage].alphaC.hex & 0xFFFFF0; // 24, strip out tswap and rswap for now - out[0] |= (alphaC&0xF0) << 24; // 8 - out[1] |= alphaC >> 8; // 16 - - // reserve 3 bits for bpmem.tevorders[stage/2].getTexMap - out[1] |= bpmem.tevorders[stage/2].getTexCoord(stage&1) << 19; // 3 - out[1] |= bpmem.tevorders[stage/2].getEnable(stage&1) << 22; // 1 - // reserve 3 bits for bpmem.tevorders[stage/2].getColorChan - - bool bHasIndStage = bpmem.tevind[stage].IsActive() && bpmem.tevind[stage].bt < bpmem.genMode.numindstages; - out[2] |= bHasIndStage << 2; // 1 - - bool needstexcoord = false; - - if (bHasIndStage) - { - out[2] |= (bpmem.tevind[stage].hex & 0x17FFFF) << 3; // 21, TODO: needs an explanation - needstexcoord = true; - } - - - TevStageCombiner::ColorCombiner& cc = bpmem.combiners[stage].colorC; - TevStageCombiner::AlphaCombiner& ac = bpmem.combiners[stage].alphaC; - - if(cc.a == TEVCOLORARG_RASA || cc.a == TEVCOLORARG_RASC - || cc.b == TEVCOLORARG_RASA || cc.b == TEVCOLORARG_RASC - || cc.c == TEVCOLORARG_RASA || cc.c == TEVCOLORARG_RASC - || cc.d == TEVCOLORARG_RASA || cc.d == TEVCOLORARG_RASC - || ac.a == TEVALPHAARG_RASA || ac.b == TEVALPHAARG_RASA - || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA) - { - out[0] |= bpmem.combiners[stage].alphaC.rswap; - out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2].swap1 << 24; // 2 - out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2].swap2 << 26; // 2 - out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2+1].swap1 << 28; // 2 - out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2+1].swap2 << 30; // 2 - out[1] |= (bpmem.tevorders[stage/2].getColorChan(stage&1)&1) << 23; - out[2] |= (bpmem.tevorders[stage/2].getColorChan(stage&1)&0x6) >> 1; - } - - out[3] |= bpmem.tevorders[stage/2].getEnable(stage&1); - if (bpmem.tevorders[stage/2].getEnable(stage&1)) - { - if (bHasIndStage) needstexcoord = true; - - out[0] |= bpmem.combiners[stage].alphaC.tswap; - out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2].swap1 << 1; // 2 - out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2].swap2 << 3; // 2 - out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2+1].swap1 << 5; // 2 - out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2+1].swap2 << 7; // 2 - out[1] |= bpmem.tevorders[stage/2].getTexMap(stage&1) << 16; - } - - if (cc.a == TEVCOLORARG_KONST || cc.b == TEVCOLORARG_KONST || cc.c == TEVCOLORARG_KONST || cc.d == TEVCOLORARG_KONST - || ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST || ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST) - { - out[3] |= bpmem.tevksel[stage/2].getKC(stage&1) << 9; // 5 - out[3] |= bpmem.tevksel[stage/2].getKA(stage&1) << 14; // 5 - } - - if (needstexcoord) - { - out[1] |= bpmem.tevorders[stage/2].getTexCoord(stage&1) << 16; - } -} - -// Mash together all the inputs that contribute to the code of a generated pixel shader into -// a unique identifier, basically containing all the bits. Yup, it's a lot .... -// It would likely be a lot more efficient to build this incrementally as the attributes -// are set... -void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) -{ - uid->values[0] |= bpmem.genMode.numtevstages; // 4 - uid->values[0] |= bpmem.genMode.numtexgens << 4; // 4 - uid->values[0] |= dstAlphaMode << 8; // 2 - - bool DepthTextureEnable = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) || g_ActiveConfig.bEnablePerPixelDepth; - - uid->values[0] |= DepthTextureEnable << 10; // 1 - - bool enablePL = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; - uid->values[0] |= enablePL << 11; // 1 - - if (!enablePL) uid->values[0] |= xfregs.numTexGen.numTexGens << 12; // 4 - u32 alphaPreTest = AlphaPreTest()+1; - - uid->values[0] |= alphaPreTest << 16; // 2 - - if (alphaPreTest == 1 || (alphaPreTest && !DepthTextureEnable && dstAlphaMode == DSTALPHA_ALPHA_PASS)) - { - // Courtesy of PreAlphaTest, we're done already ;) - // NOTE: The comment header of generated shaders depends on the value of bpmem.genmode.numindstages.. shouldnt really bother about that though. - uid->num_values = 1; - return; - } - - for (unsigned int i = 0; i < bpmem.genMode.numtexgens; ++i) - { - if (18+i < 32) - uid->values[0] |= xfregs.texMtxInfo[i].projection << (18+i); // 1 - else - uid->values[1] |= xfregs.texMtxInfo[i].projection << (i - 14); // 1 - } - - uid->values[1] = bpmem.genMode.numindstages << 2; // 3 - u32 indirectStagesUsed = 0; - for (unsigned int i = 0; i < bpmem.genMode.numindstages; ++i) - if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) - indirectStagesUsed |= (1 << bpmem.tevind[i].bt); - - assert(indirectStagesUsed == (indirectStagesUsed & 0xF)); - - uid->values[1] |= indirectStagesUsed << 5; // 4; - - for (unsigned int i = 0; i < bpmem.genMode.numindstages; ++i) - { - if (indirectStagesUsed & (1 << i)) - { - uid->values[1] |= (bpmem.tevindref.getTexCoord(i) < bpmem.genMode.numtexgens) << (9 + 3*i); // 1 - if (bpmem.tevindref.getTexCoord(i) < bpmem.genMode.numtexgens) - uid->values[1] |= bpmem.tevindref.getTexCoord(i) << (10 + 3*i); // 2 - } - } - - u32* ptr = &uid->values[2]; - for (int i = 0; i < bpmem.genMode.numtevstages+1; ++i) - { - StageHash(i, ptr); - ptr += 4; // max: ptr = &uid->values[66] - } - - ptr[0] |= bpmem.alphaFunc.comp0; // 3 - ptr[0] |= bpmem.alphaFunc.comp1 << 3; // 3 - ptr[0] |= bpmem.alphaFunc.logic << 6; // 2 - - if (alphaPreTest == 0 || alphaPreTest == 2) - { - ptr[0] |= bpmem.fog.c_proj_fsel.fsel << 8; // 3 - if (DepthTextureEnable) - { - ptr[0] |= bpmem.ztex2.op << 11; // 2 - ptr[0] |= bpmem.zcontrol.zcomploc << 13; // 1 - ptr[0] |= bpmem.zmode.testenable << 14; // 1 - ptr[0] |= bpmem.zmode.updateenable << 15; // 1 - } - } - - if (dstAlphaMode != DSTALPHA_ALPHA_PASS) - { - if (bpmem.fog.c_proj_fsel.fsel != 0) - { - ptr[0] |= bpmem.fog.c_proj_fsel.proj << 16; // 1 - ptr[0] |= bpmem.fogRange.Base.Enabled << 17; // 1 - } - } - - ++ptr; - if (enablePL) - ptr += GetLightingShaderId(ptr); - - uid->num_values = ptr - uid->values; -} - -void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) -{ - u32* ptr = uid->values; - *ptr++ = dstAlphaMode; // 0 - *ptr++ = bpmem.genMode.hex; // 1 - *ptr++ = bpmem.ztex2.hex; // 2 - *ptr++ = bpmem.zcontrol.hex; // 3 - *ptr++ = bpmem.zmode.hex; // 4 - *ptr++ = g_ActiveConfig.bEnablePerPixelDepth; // 5 - *ptr++ = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; // 6 - *ptr++ = xfregs.numTexGen.hex; // 7 - - if (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - { - // TODO: Include register states for lighting shader - *ptr++ = xfregs.color[0].hex; - *ptr++ = xfregs.alpha[0].hex; - *ptr++ = xfregs.color[1].hex; - *ptr++ = xfregs.alpha[1].hex; - } - - for (unsigned int i = 0; i < 8; ++i) - *ptr++ = xfregs.texMtxInfo[i].hex; // 8-15 - - for (unsigned int i = 0; i < 16; ++i) - *ptr++ = bpmem.tevind[i].hex; // 16-31 - - *ptr++ = bpmem.tevindref.hex; // 32 - - for (int i = 0; i < bpmem.genMode.numtevstages+1; ++i) // up to 16 times - { - *ptr++ = bpmem.combiners[i].colorC.hex; // 33+5*i - *ptr++ = bpmem.combiners[i].alphaC.hex; // 34+5*i - *ptr++ = bpmem.tevind[i].hex; // 35+5*i - *ptr++ = bpmem.tevksel[i/2].hex; // 36+5*i - *ptr++ = bpmem.tevorders[i/2].hex; // 37+5*i - } - - ptr = &uid->values[113]; - - *ptr++ = bpmem.alphaFunc.hex; // 113 - - *ptr++ = bpmem.fog.c_proj_fsel.hex; // 114 - *ptr++ = bpmem.fogRange.Base.hex; // 115 - - _assert_((ptr - uid->values) == uid->GetNumValues()); -} - -void ValidatePixelShaderIDs(API_TYPE api, PIXELSHADERUIDSAFE old_id, const std::string& old_code, DSTALPHA_MODE dstAlphaMode, u32 components) -{ - if (!g_ActiveConfig.bEnableShaderDebugging) - return; - - PIXELSHADERUIDSAFE new_id; - GetSafePixelShaderId(&new_id, dstAlphaMode); - - if (!(old_id == new_id)) - { - std::string new_code(GeneratePixelShaderCode(dstAlphaMode, api, components)); - if (old_code != new_code) - { - _assert_(old_id.GetNumValues() == new_id.GetNumValues()); - - char msg[8192]; - char* ptr = msg; - ptr += sprintf(ptr, "Pixel shader IDs matched but unique IDs did not!\nUnique IDs (old <-> new):\n"); - const int N = new_id.GetNumValues(); - for (int i = 0; i < N/2; ++i) - ptr += sprintf(ptr, "%02d, %08X %08X | %08X %08X\n", 2*i, old_id.values[2*i], old_id.values[2*i+1], - new_id.values[2*i], new_id.values[2*i+1]); - if (N % 2) - ptr += sprintf(ptr, "%02d, %08X | %08X\n", N-1, old_id.values[N-1], new_id.values[N-1]); - - static int num_failures = 0; - char szTemp[MAX_PATH]; - sprintf(szTemp, "%spsuid_mismatch_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++); - std::ofstream file(szTemp); - file << msg; - file << "\n\nOld shader code:\n" << old_code; - file << "\n\nNew shader code:\n" << new_code; - file.close(); - - PanicAlert("Unique pixel shader ID mismatch!\n\nReport this to the devs, along with the contents of %s.", szTemp); - } - } -} - -// old tev->pixelshader notes -// -// color for this stage (alpha, color) is given by bpmem.tevorders[0].colorchan0 -// konstant for this stage (alpha, color) is given by bpmem.tevksel -// inputs are given by bpmem.combiners[0].colorC.a/b/c/d << could be current chan color -// according to GXTevColorArg table above -// output is given by .outreg -// tevtemp is set according to swapmodetables and - -static void WriteStage(char *&p, int n, API_TYPE ApiType); -static void SampleTexture(char *&p, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType); -// static void WriteAlphaCompare(char *&p, int num, int comp); -static bool WriteAlphaTest(char *&p, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode); -static void WriteFog(char *&p); - -static const char *tevKSelTableC[] = // KCSEL -{ - "1.0f,1.0f,1.0f", // 1 = 0x00 - "0.875f,0.875f,0.875f", // 7_8 = 0x01 - "0.75f,0.75f,0.75f", // 3_4 = 0x02 - "0.625f,0.625f,0.625f", // 5_8 = 0x03 - "0.5f,0.5f,0.5f", // 1_2 = 0x04 - "0.375f,0.375f,0.375f", // 3_8 = 0x05 - "0.25f,0.25f,0.25f", // 1_4 = 0x06 - "0.125f,0.125f,0.125f", // 1_8 = 0x07 - "ERROR", // 0x08 - "ERROR", // 0x09 - "ERROR", // 0x0a - "ERROR", // 0x0b - I_KCOLORS"[0].rgb", // K0 = 0x0C - I_KCOLORS"[1].rgb", // K1 = 0x0D - I_KCOLORS"[2].rgb", // K2 = 0x0E - I_KCOLORS"[3].rgb", // K3 = 0x0F - I_KCOLORS"[0].rrr", // K0_R = 0x10 - I_KCOLORS"[1].rrr", // K1_R = 0x11 - I_KCOLORS"[2].rrr", // K2_R = 0x12 - I_KCOLORS"[3].rrr", // K3_R = 0x13 - I_KCOLORS"[0].ggg", // K0_G = 0x14 - I_KCOLORS"[1].ggg", // K1_G = 0x15 - I_KCOLORS"[2].ggg", // K2_G = 0x16 - I_KCOLORS"[3].ggg", // K3_G = 0x17 - I_KCOLORS"[0].bbb", // K0_B = 0x18 - I_KCOLORS"[1].bbb", // K1_B = 0x19 - I_KCOLORS"[2].bbb", // K2_B = 0x1A - I_KCOLORS"[3].bbb", // K3_B = 0x1B - I_KCOLORS"[0].aaa", // K0_A = 0x1C - I_KCOLORS"[1].aaa", // K1_A = 0x1D - I_KCOLORS"[2].aaa", // K2_A = 0x1E - I_KCOLORS"[3].aaa", // K3_A = 0x1F -}; - -static const char *tevKSelTableA[] = // KASEL -{ - "1.0f", // 1 = 0x00 - "0.875f",// 7_8 = 0x01 - "0.75f", // 3_4 = 0x02 - "0.625f",// 5_8 = 0x03 - "0.5f", // 1_2 = 0x04 - "0.375f",// 3_8 = 0x05 - "0.25f", // 1_4 = 0x06 - "0.125f",// 1_8 = 0x07 - "ERROR", // 0x08 - "ERROR", // 0x09 - "ERROR", // 0x0a - "ERROR", // 0x0b - "ERROR", // 0x0c - "ERROR", // 0x0d - "ERROR", // 0x0e - "ERROR", // 0x0f - I_KCOLORS"[0].r", // K0_R = 0x10 - I_KCOLORS"[1].r", // K1_R = 0x11 - I_KCOLORS"[2].r", // K2_R = 0x12 - I_KCOLORS"[3].r", // K3_R = 0x13 - I_KCOLORS"[0].g", // K0_G = 0x14 - I_KCOLORS"[1].g", // K1_G = 0x15 - I_KCOLORS"[2].g", // K2_G = 0x16 - I_KCOLORS"[3].g", // K3_G = 0x17 - I_KCOLORS"[0].b", // K0_B = 0x18 - I_KCOLORS"[1].b", // K1_B = 0x19 - I_KCOLORS"[2].b", // K2_B = 0x1A - I_KCOLORS"[3].b", // K3_B = 0x1B - I_KCOLORS"[0].a", // K0_A = 0x1C - I_KCOLORS"[1].a", // K1_A = 0x1D - I_KCOLORS"[2].a", // K2_A = 0x1E - I_KCOLORS"[3].a", // K3_A = 0x1F -}; - -static const char *tevScaleTable[] = // CS -{ - "1.0f", // SCALE_1 - "2.0f", // SCALE_2 - "4.0f", // SCALE_4 - "0.5f", // DIVIDE_2 -}; - -static const char *tevBiasTable[] = // TB -{ - "", // ZERO, - "+0.5f", // ADDHALF, - "-0.5f", // SUBHALF, - "", -}; - -static const char *tevOpTable[] = { // TEV - "+", // TEVOP_ADD = 0, - "-", // TEVOP_SUB = 1, -}; - -static const char *tevCInputTable[] = // CC -{ - "(prev.rgb)", // CPREV, - "(prev.aaa)", // APREV, - "(c0.rgb)", // C0, - "(c0.aaa)", // A0, - "(c1.rgb)", // C1, - "(c1.aaa)", // A1, - "(c2.rgb)", // C2, - "(c2.aaa)", // A2, - "(textemp.rgb)", // TEXC, - "(textemp.aaa)", // TEXA, - "(rastemp.rgb)", // RASC, - "(rastemp.aaa)", // RASA, - "float3(1.0f, 1.0f, 1.0f)", // ONE - "float3(0.5f, 0.5f, 0.5f)", // HALF - "(konsttemp.rgb)", //"konsttemp.rgb", // KONST - "float3(0.0f, 0.0f, 0.0f)", // ZERO - ///aded extra values to map clamped values - "(cprev.rgb)", // CPREV, - "(cprev.aaa)", // APREV, - "(cc0.rgb)", // C0, - "(cc0.aaa)", // A0, - "(cc1.rgb)", // C1, - "(cc1.aaa)", // A1, - "(cc2.rgb)", // C2, - "(cc2.aaa)", // A2, - "(textemp.rgb)", // TEXC, - "(textemp.aaa)", // TEXA, - "(crastemp.rgb)", // RASC, - "(crastemp.aaa)", // RASA, - "float3(1.0f, 1.0f, 1.0f)", // ONE - "float3(0.5f, 0.5f, 0.5f)", // HALF - "(ckonsttemp.rgb)", //"konsttemp.rgb", // KONST - "float3(0.0f, 0.0f, 0.0f)", // ZERO - "PADERROR", "PADERROR", "PADERROR", "PADERROR" -}; - -static const char *tevAInputTable[] = // CA -{ - "prev", // APREV, - "c0", // A0, - "c1", // A1, - "c2", // A2, - "textemp", // TEXA, - "rastemp", // RASA, - "konsttemp", // KONST, (hw1 had quarter) - "float4(0.0f, 0.0f, 0.0f, 0.0f)", // ZERO - ///aded extra values to map clamped values - "cprev", // APREV, - "cc0", // A0, - "cc1", // A1, - "cc2", // A2, - "textemp", // TEXA, - "crastemp", // RASA, - "ckonsttemp", // KONST, (hw1 had quarter) - "float4(0.0f, 0.0f, 0.0f, 0.0f)", // ZERO - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", -}; - -static const char *tevRasTable[] = -{ - "colors_0", - "colors_1", - "ERROR", //2 - "ERROR", //3 - "ERROR", //4 - "alphabump", // use bump alpha - "(alphabump*(255.0f/248.0f))", //normalized - "float4(0.0f, 0.0f, 0.0f, 0.0f)", // zero -}; - -//static const char *tevTexFunc[] = { "tex2D", "texRECT" }; - -static const char *tevCOutputTable[] = { "prev.rgb", "c0.rgb", "c1.rgb", "c2.rgb" }; -static const char *tevAOutputTable[] = { "prev.a", "c0.a", "c1.a", "c2.a" }; -static const char *tevIndAlphaSel[] = {"", "x", "y", "z"}; -//static const char *tevIndAlphaScale[] = {"", "*32", "*16", "*8"}; -static const char *tevIndAlphaScale[] = {"*(248.0f/255.0f)", "*(224.0f/255.0f)", "*(240.0f/255.0f)", "*(248.0f/255.0f)"}; -static const char *tevIndBiasField[] = {"", "x", "y", "xy", "z", "xz", "yz", "xyz"}; // indexed by bias -static const char *tevIndBiasAdd[] = {"-128.0f", "1.0f", "1.0f", "1.0f" }; // indexed by fmt -static const char *tevIndWrapStart[] = {"0.0f", "256.0f", "128.0f", "64.0f", "32.0f", "16.0f", "0.001f" }; -static const char *tevIndFmtScale[] = {"255.0f", "31.0f", "15.0f", "7.0f" }; - -#define WRITE p+=sprintf - -static char swapModeTable[4][5]; - -static char text[16384]; -static bool DepthTextureEnable; - -static void BuildSwapModeTable() -{ - static const char *swapColors = "rgba"; - for (int i = 0; i < 4; i++) - { - swapModeTable[i][0] = swapColors[bpmem.tevksel[i*2].swap1]; - swapModeTable[i][1] = swapColors[bpmem.tevksel[i*2].swap2]; - swapModeTable[i][2] = swapColors[bpmem.tevksel[i*2+1].swap1]; - swapModeTable[i][3] = swapColors[bpmem.tevksel[i*2+1].swap2]; - swapModeTable[i][4] = 0; - } -} - -const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType, u32 components) -{ - setlocale(LC_NUMERIC, "C"); // Reset locale for compilation - text[sizeof(text) - 1] = 0x7C; // canary - - BuildSwapModeTable(); // Needed for WriteStage - int numStages = bpmem.genMode.numtevstages + 1; - int numTexgen = bpmem.genMode.numtexgens; - - char *p = text; - WRITE(p, "//Pixel Shader for TEV stages\n"); - WRITE(p, "//%i TEV stages, %i texgens, XXX IND stages\n", - numStages, numTexgen/*, bpmem.genMode.numindstages*/); - - int nIndirectStagesUsed = 0; - if (bpmem.genMode.numindstages > 0) - { - for (int i = 0; i < numStages; ++i) - { - if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) - nIndirectStagesUsed |= 1 << bpmem.tevind[i].bt; - } - } - DepthTextureEnable = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) || g_ActiveConfig.bEnablePerPixelDepth ; - // Declare samplers - - if(ApiType != API_D3D11) - { - WRITE(p, "uniform sampler2D "); - } - else - { - WRITE(p, "sampler "); - } - - bool bfirst = true; - for (int i = 0; i < 8; ++i) - { - WRITE(p, "%s samp%d : register(s%d)", bfirst?"":",", i, i); - bfirst = false; - } - WRITE(p, ";\n"); - if(ApiType == API_D3D11) - { - WRITE(p, "Texture2D "); - bfirst = true; - for (int i = 0; i < 8; ++i) - { - WRITE(p, "%s Tex%d : register(t%d)", bfirst?"":",", i, i); - bfirst = false; - } - WRITE(p, ";\n"); - } - - WRITE(p, "\n"); - - WRITE(p, "uniform float4 "I_COLORS"[4] : register(c%d);\n", C_COLORS); - WRITE(p, "uniform float4 "I_KCOLORS"[4] : register(c%d);\n", C_KCOLORS); - WRITE(p, "uniform float4 "I_ALPHA"[1] : register(c%d);\n", C_ALPHA); - WRITE(p, "uniform float4 "I_TEXDIMS"[8] : register(c%d);\n", C_TEXDIMS); - WRITE(p, "uniform float4 "I_ZBIAS"[2] : register(c%d);\n", C_ZBIAS); - WRITE(p, "uniform float4 "I_INDTEXSCALE"[2] : register(c%d);\n", C_INDTEXSCALE); - WRITE(p, "uniform float4 "I_INDTEXMTX"[6] : register(c%d);\n", C_INDTEXMTX); - WRITE(p, "uniform float4 "I_FOG"[3] : register(c%d);\n", C_FOG); - - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - { - WRITE(p,"typedef struct { float4 col; float4 cosatt; float4 distatt; float4 pos; float4 dir; } Light;\n"); - WRITE(p,"typedef struct { Light lights[8]; } s_"I_PLIGHTS";\n"); - WRITE(p, "uniform s_"I_PLIGHTS" "I_PLIGHTS" : register(c%d);\n", C_PLIGHTS); - WRITE(p, "typedef struct { float4 C0, C1, C2, C3; } s_"I_PMATERIALS";\n"); - WRITE(p, "uniform s_"I_PMATERIALS" "I_PMATERIALS" : register(c%d);\n", C_PMATERIALS); - } - - WRITE(p, "void main(\n"); - if(ApiType != API_D3D11) - { - WRITE(p, " out float4 ocol0 : COLOR0,%s%s\n in float4 rawpos : %s,\n", - dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "\n out float4 ocol1 : COLOR1," : "", - DepthTextureEnable ? "\n out float depth : DEPTH," : "", - ApiType & API_OPENGL ? "WPOS" : ApiType & API_D3D9_SM20 ? "POSITION" : "VPOS"); - } - else - { - WRITE(p, " out float4 ocol0 : SV_Target0,%s%s\n in float4 rawpos : SV_Position,\n", - dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "\n out float4 ocol1 : SV_Target1," : "", - DepthTextureEnable ? "\n out float depth : SV_Depth," : ""); - } - - WRITE(p, " in float4 colors_0 : COLOR0,\n"); - WRITE(p, " in float4 colors_1 : COLOR1"); - - // compute window position if needed because binding semantic WPOS is not widely supported - if (numTexgen < 7) - { - for (int i = 0; i < numTexgen; ++i) - WRITE(p, ",\n in float3 uv%d : TEXCOORD%d", i, i); - WRITE(p, ",\n in float4 clipPos : TEXCOORD%d", numTexgen); - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - WRITE(p, ",\n in float4 Normal : TEXCOORD%d", numTexgen + 1); - } - else - { - // wpos is in w of first 4 texcoords - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - { - for (int i = 0; i < 8; ++i) - WRITE(p, ",\n in float4 uv%d : TEXCOORD%d", i, i); - } - else - { - for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) - WRITE(p, ",\n in float%d uv%d : TEXCOORD%d", i < 4 ? 4 : 3 , i, i); - } - } - WRITE(p, " ) {\n"); - - char* pmainstart = p; - int Pretest = AlphaPreTest(); - if (dstAlphaMode == DSTALPHA_ALPHA_PASS && !DepthTextureEnable && Pretest >= 0) - { - if (!Pretest) - { - // alpha test will always fail, so restart the shader and just make it an empty function - WRITE(p, "ocol0 = 0;\n"); - WRITE(p, "discard;\n"); - if(ApiType != API_D3D11) - WRITE(p, "return;\n"); - } - else - { - WRITE(p, " ocol0 = "I_ALPHA"[0].aaaa;\n"); - } - WRITE(p, "}\n"); - return text; - } - - WRITE(p, " float4 c0 = "I_COLORS"[1], c1 = "I_COLORS"[2], c2 = "I_COLORS"[3], prev = float4(0.0f, 0.0f, 0.0f, 0.0f), textemp = float4(0.0f, 0.0f, 0.0f, 0.0f), rastemp = float4(0.0f, 0.0f, 0.0f, 0.0f), konsttemp = float4(0.0f, 0.0f, 0.0f, 0.0f);\n" - " float3 comp16 = float3(1.0f, 255.0f, 0.0f), comp24 = float3(1.0f, 255.0f, 255.0f*255.0f);\n" - " float4 alphabump=float4(0.0f,0.0f,0.0f,0.0f);\n" - " float3 tevcoord=float3(0.0f, 0.0f, 0.0f);\n" - " float2 wrappedcoord=float2(0.0f,0.0f), tempcoord=float2(0.0f,0.0f);\n" - " float4 cc0=float4(0.0f,0.0f,0.0f,0.0f), cc1=float4(0.0f,0.0f,0.0f,0.0f);\n" - " float4 cc2=float4(0.0f,0.0f,0.0f,0.0f), cprev=float4(0.0f,0.0f,0.0f,0.0f);\n" - " float4 crastemp=float4(0.0f,0.0f,0.0f,0.0f),ckonsttemp=float4(0.0f,0.0f,0.0f,0.0f);\n\n"); - - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - { - if (xfregs.numTexGen.numTexGens < 7) - { - WRITE(p,"float3 _norm0 = normalize(Normal.xyz);\n\n"); - WRITE(p,"float3 pos = float3(clipPos.x,clipPos.y,Normal.w);\n"); - } - else - { - WRITE(p," float3 _norm0 = normalize(float3(uv4.w,uv5.w,uv6.w));\n\n"); - WRITE(p,"float3 pos = float3(uv0.w,uv1.w,uv7.w);\n"); - } - - - WRITE(p, "float4 mat, lacc;\n" - "float3 ldir, h;\n" - "float dist, dist2, attn;\n"); - - p = GenerateLightingShader(p, components, I_PMATERIALS, I_PLIGHTS, "colors_", "colors_"); - } - - if (numTexgen < 7) - WRITE(p, "clipPos = float4(rawpos.x, rawpos.y, clipPos.z, clipPos.w);\n"); - else - WRITE(p, "float4 clipPos = float4(rawpos.x, rawpos.y, uv2.w, uv3.w);\n"); - - // HACK to handle cases where the tex gen is not enabled - if (numTexgen == 0) - { - WRITE(p, "float3 uv0 = float3(0.0f, 0.0f, 0.0f);\n"); - } - else - { - for (int i = 0; i < numTexgen; ++i) - { - // optional perspective divides - if (xfregs.texMtxInfo[i].projection == XF_TEXPROJ_STQ) - { - WRITE(p, "if (uv%d.z)", i); - WRITE(p, " uv%d.xy = uv%d.xy / uv%d.z;\n", i, i, i); - } - - WRITE(p, "uv%d.xy = uv%d.xy * "I_TEXDIMS"[%d].zw;\n", i, i, i); - } - } - - // indirect texture map lookup - for(u32 i = 0; i < bpmem.genMode.numindstages; ++i) - { - if (nIndirectStagesUsed & (1<= %s.r + (0.25f/255.0f)) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_R8_GT 8 - " %s + ((abs(%s.r - %s.r) < (0.5f/255.0f)) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_R8_EQ 9 - " %s + (( dot(%s.rgb, comp16) >= (dot(%s.rgb, comp16) + (0.25f/255.0f))) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_GR16_GT 10 - " %s + (abs(dot(%s.rgb, comp16) - dot(%s.rgb, comp16)) < (0.5f/255.0f) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_GR16_EQ 11 - " %s + (( dot(%s.rgb, comp24) >= (dot(%s.rgb, comp24) + (0.25f/255.0f))) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_BGR24_GT 12 - " %s + (abs(dot(%s.rgb, comp24) - dot(%s.rgb, comp24)) < (0.5f/255.0f) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_BGR24_EQ 13 - " %s + (max(sign(%s.rgb - %s.rgb - (0.25f/255.0f)), float3(0.0f, 0.0f, 0.0f)) * %s)",//#define TEVCMP_RGB8_GT 14 - " %s + ((float3(1.0f, 1.0f, 1.0f) - max(sign(abs(%s.rgb - %s.rgb) - (0.5f/255.0f)), float3(0.0f, 0.0f, 0.0f))) * %s)"//#define TEVCMP_RGB8_EQ 15 -}; - -//table with the alpha compare operations -static const char *TEVCMPAlphaOPTable[16] = -{ - "0.0f",//0 - "0.0f",//1 - "0.0f",//2 - "0.0f",//3 - "0.0f",//4 - "0.0f",//5 - "0.0f",//6 - "0.0f",//7 - " %s.a + ((%s.r >= (%s.r + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_R8_GT 8 - " %s.a + (abs(%s.r - %s.r) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_R8_EQ 9 - " %s.a + ((dot(%s.rgb, comp16) >= (dot(%s.rgb, comp16) + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_GR16_GT 10 - " %s.a + (abs(dot(%s.rgb, comp16) - dot(%s.rgb, comp16)) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_GR16_EQ 11 - " %s.a + ((dot(%s.rgb, comp24) >= (dot(%s.rgb, comp24) + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_BGR24_GT 12 - " %s.a + (abs(dot(%s.rgb, comp24) - dot(%s.rgb, comp24)) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_BGR24_EQ 13 - " %s.a + ((%s.a >= (%s.a + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_A8_GT 14 - " %s.a + (abs(%s.a - %s.a) < (0.5f/255.0f) ? %s.a : 0.0f)"//#define TEVCMP_A8_EQ 15 - -}; - - -static void WriteStage(char *&p, int n, API_TYPE ApiType) -{ - int texcoord = bpmem.tevorders[n/2].getTexCoord(n&1); - bool bHasTexCoord = (u32)texcoord < bpmem.genMode.numtexgens; - bool bHasIndStage = bpmem.tevind[n].IsActive() && bpmem.tevind[n].bt < bpmem.genMode.numindstages; - - // HACK to handle cases where the tex gen is not enabled - if (!bHasTexCoord) - texcoord = 0; - - WRITE(p, "// TEV stage %d\n", n); - - if (bHasIndStage) - { - WRITE(p, "// indirect op\n"); - // perform the indirect op on the incoming regular coordinates using indtex%d as the offset coords - if (bpmem.tevind[n].bs != ITBA_OFF) - { - WRITE(p, "alphabump = indtex%d.%s %s;\n", - bpmem.tevind[n].bt, - tevIndAlphaSel[bpmem.tevind[n].bs], - tevIndAlphaScale[bpmem.tevind[n].fmt]); - } - // format - WRITE(p, "float3 indtevcrd%d = indtex%d * %s;\n", n, bpmem.tevind[n].bt, tevIndFmtScale[bpmem.tevind[n].fmt]); - - // bias - if (bpmem.tevind[n].bias != ITB_NONE ) - WRITE(p, "indtevcrd%d.%s += %s;\n", n, tevIndBiasField[bpmem.tevind[n].bias], tevIndBiasAdd[bpmem.tevind[n].fmt]); - - // multiply by offset matrix and scale - if (bpmem.tevind[n].mid != 0) - { - if (bpmem.tevind[n].mid <= 3) - { - int mtxidx = 2*(bpmem.tevind[n].mid-1); - WRITE(p, "float2 indtevtrans%d = float2(dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d), dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d));\n", - n, mtxidx, n, mtxidx+1, n); - } - else if (bpmem.tevind[n].mid <= 7 && bHasTexCoord) - { // s matrix - // TODO: Might become negative? - int mtxidx = 2*(bpmem.tevind[n].mid-5); - WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.xx;\n", n, mtxidx, texcoord, n); - } - else if (bpmem.tevind[n].mid <= 11 && bHasTexCoord) - { // t matrix - // TODO: Might become negative? - int mtxidx = 2*(bpmem.tevind[n].mid-9); - WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.yy;\n", n, mtxidx, texcoord, n); - } - else - WRITE(p, "float2 indtevtrans%d = 0;\n", n); - } - else - WRITE(p, "float2 indtevtrans%d = 0;\n", n); - - // --------- - // Wrapping - // --------- - - // wrap S - if (bpmem.tevind[n].sw == ITW_OFF) - WRITE(p, "wrappedcoord.x = uv%d.x;\n", texcoord); - else if (bpmem.tevind[n].sw == ITW_0) - WRITE(p, "wrappedcoord.x = 0.0f;\n"); - else - WRITE(p, "wrappedcoord.x = fmod( uv%d.x, %s );\n", texcoord, tevIndWrapStart[bpmem.tevind[n].sw]); - - // wrap T - if (bpmem.tevind[n].tw == ITW_OFF) - WRITE(p, "wrappedcoord.y = uv%d.y;\n", texcoord); - else if (bpmem.tevind[n].tw == ITW_0) - WRITE(p, "wrappedcoord.y = 0.0f;\n"); - else - WRITE(p, "wrappedcoord.y = fmod( uv%d.y, %s );\n", texcoord, tevIndWrapStart[bpmem.tevind[n].tw]); - - if (bpmem.tevind[n].fb_addprev) // add previous tevcoord - WRITE(p, "tevcoord.xy += wrappedcoord + indtevtrans%d;\n", n); - else - WRITE(p, "tevcoord.xy = wrappedcoord + indtevtrans%d;\n", n); - } - - TevStageCombiner::ColorCombiner &cc = bpmem.combiners[n].colorC; - TevStageCombiner::AlphaCombiner &ac = bpmem.combiners[n].alphaC; - - // blah1 - if(cc.a == TEVCOLORARG_RASA || cc.a == TEVCOLORARG_RASC - || cc.b == TEVCOLORARG_RASA || cc.b == TEVCOLORARG_RASC - || cc.c == TEVCOLORARG_RASA || cc.c == TEVCOLORARG_RASC - || cc.d == TEVCOLORARG_RASA || cc.d == TEVCOLORARG_RASC - || ac.a == TEVALPHAARG_RASA || ac.b == TEVALPHAARG_RASA - || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA) - { - char *rasswap = swapModeTable[bpmem.combiners[n].alphaC.rswap]; - WRITE(p, "rastemp = %s.%s;\n", tevRasTable[bpmem.tevorders[n / 2].getColorChan(n & 1)], rasswap); - WRITE(p, "crastemp = frac(rastemp * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - } - - - if (bpmem.tevorders[n/2].getEnable(n&1)) - { - if(!bHasIndStage) - { - // calc tevcord - if(bHasTexCoord) - WRITE(p, "tevcoord.xy = uv%d.xy;\n", texcoord); - else - WRITE(p, "tevcoord.xy = float2(0.0f, 0.0f);\n"); - } - - char *texswap = swapModeTable[bpmem.combiners[n].alphaC.tswap]; - int texmap = bpmem.tevorders[n/2].getTexMap(n&1); - SampleTexture(p, "textemp", "tevcoord", texswap, texmap, ApiType); - } - else - WRITE(p, "textemp = float4(1.0f, 1.0f, 1.0f, 1.0f);\n"); - - - // blah2 - if (cc.a == TEVCOLORARG_KONST || cc.b == TEVCOLORARG_KONST || cc.c == TEVCOLORARG_KONST || cc.d == TEVCOLORARG_KONST - || ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST || ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST) - { - int kc = bpmem.tevksel[n / 2].getKC(n & 1); - int ka = bpmem.tevksel[n / 2].getKA(n & 1); - WRITE(p, "konsttemp = float4(%s, %s);\n", tevKSelTableC[kc], tevKSelTableA[ka]); - if(kc > 7 || ka > 7) - { - WRITE(p, "ckonsttemp = frac(konsttemp * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - } - else - { - WRITE(p, "ckonsttemp = konsttemp;\n"); - } - } - - if(cc.a == TEVCOLORARG_CPREV || cc.a == TEVCOLORARG_APREV - || cc.b == TEVCOLORARG_CPREV || cc.b == TEVCOLORARG_APREV - || cc.c == TEVCOLORARG_CPREV || cc.c == TEVCOLORARG_APREV - || ac.a == TEVALPHAARG_APREV || ac.b == TEVALPHAARG_APREV || ac.c == TEVALPHAARG_APREV) - WRITE(p, "cprev = frac(prev * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - - - if(cc.a == TEVCOLORARG_C0 || cc.a == TEVCOLORARG_A0 - || cc.b == TEVCOLORARG_C0 || cc.b == TEVCOLORARG_A0 - || cc.c == TEVCOLORARG_C0 || cc.c == TEVCOLORARG_A0 - || ac.a == TEVALPHAARG_A0 || ac.b == TEVALPHAARG_A0 || ac.c == TEVALPHAARG_A0) - WRITE(p, "cc0 = frac(c0 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - - - if(cc.a == TEVCOLORARG_C1 || cc.a == TEVCOLORARG_A1 - || cc.b == TEVCOLORARG_C1 || cc.b == TEVCOLORARG_A1 - || cc.c == TEVCOLORARG_C1 || cc.c == TEVCOLORARG_A1 - || ac.a == TEVALPHAARG_A1 || ac.b == TEVALPHAARG_A1 || ac.c == TEVALPHAARG_A1) - WRITE(p, "cc1 = frac(c1 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - - - if(cc.a == TEVCOLORARG_C2 || cc.a == TEVCOLORARG_A2 - || cc.b == TEVCOLORARG_C2 || cc.b == TEVCOLORARG_A2 - || cc.c == TEVCOLORARG_C2 || cc.c == TEVCOLORARG_A2 - || ac.a == TEVALPHAARG_A2 || ac.b == TEVALPHAARG_A2 || ac.c == TEVALPHAARG_A2) - WRITE(p, "cc2 = frac(c2 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); - - - WRITE(p, "// color combine\n"); - if (cc.clamp) - WRITE(p, "%s = saturate(", tevCOutputTable[cc.dest]); - else - WRITE(p, "%s = ", tevCOutputTable[cc.dest]); - - // combine the color channel - if (cc.bias != TevBias_COMPARE) // if not compare - { - //normal color combiner goes here - if (cc.shift > TEVSCALE_1) - WRITE(p, "%s*(", tevScaleTable[cc.shift]); - - if(!(cc.d == TEVCOLORARG_ZERO && cc.op == TEVOP_ADD)) - WRITE(p, "%s%s", tevCInputTable[cc.d], tevOpTable[cc.op]); - - if (cc.a == cc.b) - WRITE(p, "%s", tevCInputTable[cc.a + 16]); - else if (cc.c == TEVCOLORARG_ZERO) - WRITE(p, "%s", tevCInputTable[cc.a + 16]); - else if (cc.c == TEVCOLORARG_ONE) - WRITE(p, "%s", tevCInputTable[cc.b + 16]); - else if (cc.a == TEVCOLORARG_ZERO) - WRITE(p, "%s*%s", tevCInputTable[cc.b + 16], tevCInputTable[cc.c + 16]); - else if (cc.b == TEVCOLORARG_ZERO) - WRITE(p, "%s*(float3(1.0f, 1.0f, 1.0f)-%s)", tevCInputTable[cc.a + 16], tevCInputTable[cc.c + 16]); - else - WRITE(p, "lerp(%s, %s, %s)", tevCInputTable[cc.a + 16], tevCInputTable[cc.b + 16], tevCInputTable[cc.c + 16]); - - WRITE(p, "%s", tevBiasTable[cc.bias]); - - if (cc.shift > TEVSCALE_1) - WRITE(p, ")"); - } - else - { - int cmp = (cc.shift<<1)|cc.op|8; // comparemode stored here - WRITE(p, TEVCMPColorOPTable[cmp],//lookup the function from the op table - tevCInputTable[cc.d], - tevCInputTable[cc.a + 16], - tevCInputTable[cc.b + 16], - tevCInputTable[cc.c + 16]); - } - if (cc.clamp) - WRITE(p, ")"); - WRITE(p,";\n"); - - WRITE(p, "// alpha combine\n"); - // combine the alpha channel - if (ac.clamp) - WRITE(p, "%s = saturate(", tevAOutputTable[ac.dest]); - else - WRITE(p, "%s = ", tevAOutputTable[ac.dest]); - - if (ac.bias != TevBias_COMPARE) // if not compare - { - //normal alpha combiner goes here - if (ac.shift > TEVSCALE_1) - WRITE(p, "%s*(", tevScaleTable[ac.shift]); - - if(!(ac.d == TEVALPHAARG_ZERO && ac.op == TEVOP_ADD)) - WRITE(p, "%s.a%s", tevAInputTable[ac.d], tevOpTable[ac.op]); - - if (ac.a == ac.b) - WRITE(p, "%s.a", tevAInputTable[ac.a + 8]); - else if (ac.c == TEVALPHAARG_ZERO) - WRITE(p, "%s.a", tevAInputTable[ac.a + 8]); - else if (ac.a == TEVALPHAARG_ZERO) - WRITE(p, "%s.a*%s.a", tevAInputTable[ac.b + 8], tevAInputTable[ac.c + 8]); - else if (ac.b == TEVALPHAARG_ZERO) - WRITE(p, "%s.a*(1.0f-%s.a)", tevAInputTable[ac.a + 8], tevAInputTable[ac.c + 8]); - else - WRITE(p, "lerp(%s.a, %s.a, %s.a)", tevAInputTable[ac.a + 8], tevAInputTable[ac.b + 8], tevAInputTable[ac.c + 8]); - - WRITE(p, "%s",tevBiasTable[ac.bias]); - - if (ac.shift>0) - WRITE(p, ")"); - - } - else - { - //compare alpha combiner goes here - int cmp = (ac.shift<<1)|ac.op|8; // comparemode stored here - WRITE(p, TEVCMPAlphaOPTable[cmp], - tevAInputTable[ac.d], - tevAInputTable[ac.a + 8], - tevAInputTable[ac.b + 8], - tevAInputTable[ac.c + 8]); - } - if (ac.clamp) - WRITE(p, ")"); - WRITE(p, ";\n\n"); - WRITE(p, "// TEV done\n"); -} - -void SampleTexture(char *&p, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType) -{ - if (ApiType == API_D3D11) - WRITE(p, "%s=Tex%d.Sample(samp%d,%s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap,texmap, texcoords, texmap, texswap); - else - WRITE(p, "%s=tex2D(samp%d,%s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap, texcoords, texmap, texswap); -} - -static const char *tevAlphaFuncsTable[] = -{ - "(false)", //ALPHACMP_NEVER 0 - "(prev.a <= %s - (0.25f/255.0f))", //ALPHACMP_LESS 1 - "(abs( prev.a - %s ) < (0.5f/255.0f))", //ALPHACMP_EQUAL 2 - "(prev.a < %s + (0.25f/255.0f))", //ALPHACMP_LEQUAL 3 - "(prev.a >= %s + (0.25f/255.0f))", //ALPHACMP_GREATER 4 - "(abs( prev.a - %s ) >= (0.5f/255.0f))", //ALPHACMP_NEQUAL 5 - "(prev.a > %s - (0.25f/255.0f))", //ALPHACMP_GEQUAL 6 - "(true)" //ALPHACMP_ALWAYS 7 -}; - -static const char *tevAlphaFunclogicTable[] = -{ - " && ", // and - " || ", // or - " != ", // xor - " == " // xnor -}; -static int AlphaPreTest() -{ - u32 op = bpmem.alphaFunc.logic; - u32 comp[2] = {bpmem.alphaFunc.comp0, bpmem.alphaFunc.comp1}; - - // First kill all the simple cases - switch(op) - { - case 0: // AND - if (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) return true; - if (comp[0] == ALPHACMP_NEVER || comp[1] == ALPHACMP_NEVER) return false; - break; - case 1: // OR - if (comp[0] == ALPHACMP_ALWAYS || comp[1] == ALPHACMP_ALWAYS) return true; - if (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)return false; - break; - case 2: // XOR - if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS)) - return true; - if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)) - return false; - break; - case 3: // XNOR - if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS)) - return false; - if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)) - return true; - break; - default: PanicAlert("bad logic for alpha test? %08x", op); - } - return -1; -} - - -static bool WriteAlphaTest(char *&p, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode) -{ - static const char *alphaRef[2] = - { - I_ALPHA"[0].r", - I_ALPHA"[0].g" - }; - - int Pretest = AlphaPreTest(); - if(Pretest >= 0) - { - return Pretest != 0; - } - - // using discard then return works the same in cg and dx9 but not in dx11 - WRITE(p, "if(!( "); - - int compindex = bpmem.alphaFunc.comp0 % 8; - WRITE(p, tevAlphaFuncsTable[compindex],alphaRef[0]);//lookup the first component from the alpha function table - - WRITE(p, "%s", tevAlphaFunclogicTable[bpmem.alphaFunc.logic % 4]);//lookup the logic op - - compindex = bpmem.alphaFunc.comp1 % 8; - WRITE(p, tevAlphaFuncsTable[compindex],alphaRef[1]);//lookup the second component from the alpha function table - WRITE(p, ")){ocol0 = 0;%s%s discard;%s}\n", - dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "ocol1 = 0;" : "", - DepthTextureEnable ? "depth = 1.f;" : "", - (ApiType != API_D3D11) ? "return;" : ""); - return true; -} - -static const char *tevFogFuncsTable[] = -{ - "", //No Fog - "", //? - "", //Linear - "", //? - " fog = 1.0f - pow(2.0f, -8.0f * fog);\n", //exp - " fog = 1.0f - pow(2.0f, -8.0f * fog * fog);\n", //exp2 - " fog = pow(2.0f, -8.0f * (1.0f - fog));\n", //backward exp - " fog = 1.0f - fog;\n fog = pow(2.0f, -8.0f * fog * fog);\n" //backward exp2 -}; - -static void WriteFog(char *&p) -{ - if(bpmem.fog.c_proj_fsel.fsel == 0)return;//no Fog - - if (bpmem.fog.c_proj_fsel.proj == 0) - { - // perspective - // ze = A/(B - (Zs >> B_SHF) - WRITE (p, " float ze = "I_FOG"[1].x / ("I_FOG"[1].y - (zCoord / "I_FOG"[1].w));\n"); - } - else - { - // orthographic - // ze = a*Zs (here, no B_SHF) - WRITE (p, " float ze = "I_FOG"[1].x * zCoord;\n"); - } - - // x_adjust = sqrt((x-center)^2 + k^2)/k - // ze *= x_adjust - //this is complitly teorical as the real hard seems to use a table intead of calculate the values. - if(bpmem.fogRange.Base.Enabled) - { - WRITE (p, " float x_adjust = (2.0f * (clipPos.x / "I_FOG"[2].y)) - 1.0f - "I_FOG"[2].x;\n"); - WRITE (p, " x_adjust = sqrt(x_adjust * x_adjust + "I_FOG"[2].z * "I_FOG"[2].z) / "I_FOG"[2].z;\n"); - WRITE (p, " ze *= x_adjust;\n"); - } - - WRITE (p, " float fog = saturate(ze - "I_FOG"[1].z);\n"); - - if(bpmem.fog.c_proj_fsel.fsel > 3) - { - WRITE(p, "%s", tevFogFuncsTable[bpmem.fog.c_proj_fsel.fsel]); - } - else - { - if(bpmem.fog.c_proj_fsel.fsel != 2) - WARN_LOG(VIDEO, "Unknown Fog Type! %08x", bpmem.fog.c_proj_fsel.fsel); - } - - WRITE(p, " prev.rgb = lerp(prev.rgb,"I_FOG"[0].rgb,fog);\n"); - - +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include +#include +#include + +#include "LightingShaderGen.h" +#include "PixelShaderGen.h" +#include "XFMemory.h" // for texture projection mode +#include "BPMemory.h" +#include "VideoConfig.h" +#include "NativeVertexFormat.h" + +PIXELSHADERUID last_pixel_shader_uid; + +static int AlphaPreTest(); + +static void StageHash(int stage, u32* out) +{ + out[0] |= bpmem.combiners[stage].colorC.hex & 0xFFFFFF; // 24 + u32 alphaC = bpmem.combiners[stage].alphaC.hex & 0xFFFFF0; // 24, strip out tswap and rswap for now + out[0] |= (alphaC&0xF0) << 24; // 8 + out[1] |= alphaC >> 8; // 16 + + // reserve 3 bits for bpmem.tevorders[stage/2].getTexMap + out[1] |= bpmem.tevorders[stage/2].getTexCoord(stage&1) << 19; // 3 + out[1] |= bpmem.tevorders[stage/2].getEnable(stage&1) << 22; // 1 + // reserve 3 bits for bpmem.tevorders[stage/2].getColorChan + + bool bHasIndStage = bpmem.tevind[stage].IsActive() && bpmem.tevind[stage].bt < bpmem.genMode.numindstages; + out[2] |= bHasIndStage << 2; // 1 + + bool needstexcoord = false; + + if (bHasIndStage) + { + out[2] |= (bpmem.tevind[stage].hex & 0x17FFFF) << 3; // 21, TODO: needs an explanation + needstexcoord = true; + } + + + TevStageCombiner::ColorCombiner& cc = bpmem.combiners[stage].colorC; + TevStageCombiner::AlphaCombiner& ac = bpmem.combiners[stage].alphaC; + + if(cc.a == TEVCOLORARG_RASA || cc.a == TEVCOLORARG_RASC + || cc.b == TEVCOLORARG_RASA || cc.b == TEVCOLORARG_RASC + || cc.c == TEVCOLORARG_RASA || cc.c == TEVCOLORARG_RASC + || cc.d == TEVCOLORARG_RASA || cc.d == TEVCOLORARG_RASC + || ac.a == TEVALPHAARG_RASA || ac.b == TEVALPHAARG_RASA + || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA) + { + out[0] |= bpmem.combiners[stage].alphaC.rswap; + out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2].swap1 << 24; // 2 + out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2].swap2 << 26; // 2 + out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2+1].swap1 << 28; // 2 + out[2] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.rswap*2+1].swap2 << 30; // 2 + out[1] |= (bpmem.tevorders[stage/2].getColorChan(stage&1)&1) << 23; + out[2] |= (bpmem.tevorders[stage/2].getColorChan(stage&1)&0x6) >> 1; + } + + out[3] |= bpmem.tevorders[stage/2].getEnable(stage&1); + if (bpmem.tevorders[stage/2].getEnable(stage&1)) + { + if (bHasIndStage) needstexcoord = true; + + out[0] |= bpmem.combiners[stage].alphaC.tswap; + out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2].swap1 << 1; // 2 + out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2].swap2 << 3; // 2 + out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2+1].swap1 << 5; // 2 + out[3] |= bpmem.tevksel[bpmem.combiners[stage].alphaC.tswap*2+1].swap2 << 7; // 2 + out[1] |= bpmem.tevorders[stage/2].getTexMap(stage&1) << 16; + } + + if (cc.a == TEVCOLORARG_KONST || cc.b == TEVCOLORARG_KONST || cc.c == TEVCOLORARG_KONST || cc.d == TEVCOLORARG_KONST + || ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST || ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST) + { + out[3] |= bpmem.tevksel[stage/2].getKC(stage&1) << 9; // 5 + out[3] |= bpmem.tevksel[stage/2].getKA(stage&1) << 14; // 5 + } + + if (needstexcoord) + { + out[1] |= bpmem.tevorders[stage/2].getTexCoord(stage&1) << 16; + } +} + +// Mash together all the inputs that contribute to the code of a generated pixel shader into +// a unique identifier, basically containing all the bits. Yup, it's a lot .... +// It would likely be a lot more efficient to build this incrementally as the attributes +// are set... +void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) +{ + uid->values[0] |= bpmem.genMode.numtevstages; // 4 + uid->values[0] |= bpmem.genMode.numtexgens << 4; // 4 + uid->values[0] |= dstAlphaMode << 8; // 2 + + bool DepthTextureEnable = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) || g_ActiveConfig.bEnablePerPixelDepth; + + uid->values[0] |= DepthTextureEnable << 10; // 1 + + bool enablePL = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; + uid->values[0] |= enablePL << 11; // 1 + + if (!enablePL) uid->values[0] |= xfregs.numTexGen.numTexGens << 12; // 4 + u32 alphaPreTest = AlphaPreTest()+1; + + uid->values[0] |= alphaPreTest << 16; // 2 + + if (alphaPreTest == 1 || (alphaPreTest && !DepthTextureEnable && dstAlphaMode == DSTALPHA_ALPHA_PASS)) + { + // Courtesy of PreAlphaTest, we're done already ;) + // NOTE: The comment header of generated shaders depends on the value of bpmem.genmode.numindstages.. shouldnt really bother about that though. + uid->num_values = 1; + return; + } + + for (unsigned int i = 0; i < bpmem.genMode.numtexgens; ++i) + { + if (18+i < 32) + uid->values[0] |= xfregs.texMtxInfo[i].projection << (18+i); // 1 + else + uid->values[1] |= xfregs.texMtxInfo[i].projection << (i - 14); // 1 + } + + uid->values[1] = bpmem.genMode.numindstages << 2; // 3 + u32 indirectStagesUsed = 0; + for (unsigned int i = 0; i < bpmem.genMode.numindstages; ++i) + if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) + indirectStagesUsed |= (1 << bpmem.tevind[i].bt); + + assert(indirectStagesUsed == (indirectStagesUsed & 0xF)); + + uid->values[1] |= indirectStagesUsed << 5; // 4; + + for (unsigned int i = 0; i < bpmem.genMode.numindstages; ++i) + { + if (indirectStagesUsed & (1 << i)) + { + uid->values[1] |= (bpmem.tevindref.getTexCoord(i) < bpmem.genMode.numtexgens) << (9 + 3*i); // 1 + if (bpmem.tevindref.getTexCoord(i) < bpmem.genMode.numtexgens) + uid->values[1] |= bpmem.tevindref.getTexCoord(i) << (10 + 3*i); // 2 + } + } + + u32* ptr = &uid->values[2]; + for (int i = 0; i < bpmem.genMode.numtevstages+1; ++i) + { + StageHash(i, ptr); + ptr += 4; // max: ptr = &uid->values[66] + } + + ptr[0] |= bpmem.alphaFunc.comp0; // 3 + ptr[0] |= bpmem.alphaFunc.comp1 << 3; // 3 + ptr[0] |= bpmem.alphaFunc.logic << 6; // 2 + + if (alphaPreTest == 0 || alphaPreTest == 2) + { + ptr[0] |= bpmem.fog.c_proj_fsel.fsel << 8; // 3 + if (DepthTextureEnable) + { + ptr[0] |= bpmem.ztex2.op << 11; // 2 + ptr[0] |= bpmem.zcontrol.zcomploc << 13; // 1 + ptr[0] |= bpmem.zmode.testenable << 14; // 1 + ptr[0] |= bpmem.zmode.updateenable << 15; // 1 + } + } + + if (dstAlphaMode != DSTALPHA_ALPHA_PASS) + { + if (bpmem.fog.c_proj_fsel.fsel != 0) + { + ptr[0] |= bpmem.fog.c_proj_fsel.proj << 16; // 1 + ptr[0] |= bpmem.fogRange.Base.Enabled << 17; // 1 + } + } + + ++ptr; + if (enablePL) + ptr += GetLightingShaderId(ptr); + + uid->num_values = ptr - uid->values; +} + +void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) +{ + u32* ptr = uid->values; + *ptr++ = dstAlphaMode; // 0 + *ptr++ = bpmem.genMode.hex; // 1 + *ptr++ = bpmem.ztex2.hex; // 2 + *ptr++ = bpmem.zcontrol.hex; // 3 + *ptr++ = bpmem.zmode.hex; // 4 + *ptr++ = g_ActiveConfig.bEnablePerPixelDepth; // 5 + *ptr++ = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; // 6 + *ptr++ = xfregs.numTexGen.hex; // 7 + + if (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + { + // TODO: Include register states for lighting shader + *ptr++ = xfregs.color[0].hex; + *ptr++ = xfregs.alpha[0].hex; + *ptr++ = xfregs.color[1].hex; + *ptr++ = xfregs.alpha[1].hex; + } + + for (unsigned int i = 0; i < 8; ++i) + *ptr++ = xfregs.texMtxInfo[i].hex; // 8-15 + + for (unsigned int i = 0; i < 16; ++i) + *ptr++ = bpmem.tevind[i].hex; // 16-31 + + *ptr++ = bpmem.tevindref.hex; // 32 + + for (int i = 0; i < bpmem.genMode.numtevstages+1; ++i) // up to 16 times + { + *ptr++ = bpmem.combiners[i].colorC.hex; // 33+5*i + *ptr++ = bpmem.combiners[i].alphaC.hex; // 34+5*i + *ptr++ = bpmem.tevind[i].hex; // 35+5*i + *ptr++ = bpmem.tevksel[i/2].hex; // 36+5*i + *ptr++ = bpmem.tevorders[i/2].hex; // 37+5*i + } + + ptr = &uid->values[113]; + + *ptr++ = bpmem.alphaFunc.hex; // 113 + + *ptr++ = bpmem.fog.c_proj_fsel.hex; // 114 + *ptr++ = bpmem.fogRange.Base.hex; // 115 + + _assert_((ptr - uid->values) == uid->GetNumValues()); +} + +void ValidatePixelShaderIDs(API_TYPE api, PIXELSHADERUIDSAFE old_id, const std::string& old_code, DSTALPHA_MODE dstAlphaMode, u32 components) +{ + if (!g_ActiveConfig.bEnableShaderDebugging) + return; + + PIXELSHADERUIDSAFE new_id; + GetSafePixelShaderId(&new_id, dstAlphaMode); + + if (!(old_id == new_id)) + { + std::string new_code(GeneratePixelShaderCode(dstAlphaMode, api, components)); + if (old_code != new_code) + { + _assert_(old_id.GetNumValues() == new_id.GetNumValues()); + + char msg[8192]; + char* ptr = msg; + ptr += sprintf(ptr, "Pixel shader IDs matched but unique IDs did not!\nUnique IDs (old <-> new):\n"); + const int N = new_id.GetNumValues(); + for (int i = 0; i < N/2; ++i) + ptr += sprintf(ptr, "%02d, %08X %08X | %08X %08X\n", 2*i, old_id.values[2*i], old_id.values[2*i+1], + new_id.values[2*i], new_id.values[2*i+1]); + if (N % 2) + ptr += sprintf(ptr, "%02d, %08X | %08X\n", N-1, old_id.values[N-1], new_id.values[N-1]); + + static int num_failures = 0; + char szTemp[MAX_PATH]; + sprintf(szTemp, "%spsuid_mismatch_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++); + std::ofstream file(szTemp); + file << msg; + file << "\n\nOld shader code:\n" << old_code; + file << "\n\nNew shader code:\n" << new_code; + file.close(); + + PanicAlert("Unique pixel shader ID mismatch!\n\nReport this to the devs, along with the contents of %s.", szTemp); + } + } +} + +// old tev->pixelshader notes +// +// color for this stage (alpha, color) is given by bpmem.tevorders[0].colorchan0 +// konstant for this stage (alpha, color) is given by bpmem.tevksel +// inputs are given by bpmem.combiners[0].colorC.a/b/c/d << could be current chan color +// according to GXTevColorArg table above +// output is given by .outreg +// tevtemp is set according to swapmodetables and + +static void WriteStage(char *&p, int n, API_TYPE ApiType); +static void SampleTexture(char *&p, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType); +// static void WriteAlphaCompare(char *&p, int num, int comp); +static bool WriteAlphaTest(char *&p, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode); +static void WriteFog(char *&p); + +static const char *tevKSelTableC[] = // KCSEL +{ + "1.0f,1.0f,1.0f", // 1 = 0x00 + "0.875f,0.875f,0.875f", // 7_8 = 0x01 + "0.75f,0.75f,0.75f", // 3_4 = 0x02 + "0.625f,0.625f,0.625f", // 5_8 = 0x03 + "0.5f,0.5f,0.5f", // 1_2 = 0x04 + "0.375f,0.375f,0.375f", // 3_8 = 0x05 + "0.25f,0.25f,0.25f", // 1_4 = 0x06 + "0.125f,0.125f,0.125f", // 1_8 = 0x07 + "ERROR", // 0x08 + "ERROR", // 0x09 + "ERROR", // 0x0a + "ERROR", // 0x0b + I_KCOLORS"[0].rgb", // K0 = 0x0C + I_KCOLORS"[1].rgb", // K1 = 0x0D + I_KCOLORS"[2].rgb", // K2 = 0x0E + I_KCOLORS"[3].rgb", // K3 = 0x0F + I_KCOLORS"[0].rrr", // K0_R = 0x10 + I_KCOLORS"[1].rrr", // K1_R = 0x11 + I_KCOLORS"[2].rrr", // K2_R = 0x12 + I_KCOLORS"[3].rrr", // K3_R = 0x13 + I_KCOLORS"[0].ggg", // K0_G = 0x14 + I_KCOLORS"[1].ggg", // K1_G = 0x15 + I_KCOLORS"[2].ggg", // K2_G = 0x16 + I_KCOLORS"[3].ggg", // K3_G = 0x17 + I_KCOLORS"[0].bbb", // K0_B = 0x18 + I_KCOLORS"[1].bbb", // K1_B = 0x19 + I_KCOLORS"[2].bbb", // K2_B = 0x1A + I_KCOLORS"[3].bbb", // K3_B = 0x1B + I_KCOLORS"[0].aaa", // K0_A = 0x1C + I_KCOLORS"[1].aaa", // K1_A = 0x1D + I_KCOLORS"[2].aaa", // K2_A = 0x1E + I_KCOLORS"[3].aaa", // K3_A = 0x1F +}; + +static const char *tevKSelTableA[] = // KASEL +{ + "1.0f", // 1 = 0x00 + "0.875f",// 7_8 = 0x01 + "0.75f", // 3_4 = 0x02 + "0.625f",// 5_8 = 0x03 + "0.5f", // 1_2 = 0x04 + "0.375f",// 3_8 = 0x05 + "0.25f", // 1_4 = 0x06 + "0.125f",// 1_8 = 0x07 + "ERROR", // 0x08 + "ERROR", // 0x09 + "ERROR", // 0x0a + "ERROR", // 0x0b + "ERROR", // 0x0c + "ERROR", // 0x0d + "ERROR", // 0x0e + "ERROR", // 0x0f + I_KCOLORS"[0].r", // K0_R = 0x10 + I_KCOLORS"[1].r", // K1_R = 0x11 + I_KCOLORS"[2].r", // K2_R = 0x12 + I_KCOLORS"[3].r", // K3_R = 0x13 + I_KCOLORS"[0].g", // K0_G = 0x14 + I_KCOLORS"[1].g", // K1_G = 0x15 + I_KCOLORS"[2].g", // K2_G = 0x16 + I_KCOLORS"[3].g", // K3_G = 0x17 + I_KCOLORS"[0].b", // K0_B = 0x18 + I_KCOLORS"[1].b", // K1_B = 0x19 + I_KCOLORS"[2].b", // K2_B = 0x1A + I_KCOLORS"[3].b", // K3_B = 0x1B + I_KCOLORS"[0].a", // K0_A = 0x1C + I_KCOLORS"[1].a", // K1_A = 0x1D + I_KCOLORS"[2].a", // K2_A = 0x1E + I_KCOLORS"[3].a", // K3_A = 0x1F +}; + +static const char *tevScaleTable[] = // CS +{ + "1.0f", // SCALE_1 + "2.0f", // SCALE_2 + "4.0f", // SCALE_4 + "0.5f", // DIVIDE_2 +}; + +static const char *tevBiasTable[] = // TB +{ + "", // ZERO, + "+0.5f", // ADDHALF, + "-0.5f", // SUBHALF, + "", +}; + +static const char *tevOpTable[] = { // TEV + "+", // TEVOP_ADD = 0, + "-", // TEVOP_SUB = 1, +}; + +static const char *tevCInputTable[] = // CC +{ + "(prev.rgb)", // CPREV, + "(prev.aaa)", // APREV, + "(c0.rgb)", // C0, + "(c0.aaa)", // A0, + "(c1.rgb)", // C1, + "(c1.aaa)", // A1, + "(c2.rgb)", // C2, + "(c2.aaa)", // A2, + "(textemp.rgb)", // TEXC, + "(textemp.aaa)", // TEXA, + "(rastemp.rgb)", // RASC, + "(rastemp.aaa)", // RASA, + "float3(1.0f, 1.0f, 1.0f)", // ONE + "float3(0.5f, 0.5f, 0.5f)", // HALF + "(konsttemp.rgb)", //"konsttemp.rgb", // KONST + "float3(0.0f, 0.0f, 0.0f)", // ZERO + ///aded extra values to map clamped values + "(cprev.rgb)", // CPREV, + "(cprev.aaa)", // APREV, + "(cc0.rgb)", // C0, + "(cc0.aaa)", // A0, + "(cc1.rgb)", // C1, + "(cc1.aaa)", // A1, + "(cc2.rgb)", // C2, + "(cc2.aaa)", // A2, + "(textemp.rgb)", // TEXC, + "(textemp.aaa)", // TEXA, + "(crastemp.rgb)", // RASC, + "(crastemp.aaa)", // RASA, + "float3(1.0f, 1.0f, 1.0f)", // ONE + "float3(0.5f, 0.5f, 0.5f)", // HALF + "(ckonsttemp.rgb)", //"konsttemp.rgb", // KONST + "float3(0.0f, 0.0f, 0.0f)", // ZERO + "PADERROR", "PADERROR", "PADERROR", "PADERROR" +}; + +static const char *tevAInputTable[] = // CA +{ + "prev", // APREV, + "c0", // A0, + "c1", // A1, + "c2", // A2, + "textemp", // TEXA, + "rastemp", // RASA, + "konsttemp", // KONST, (hw1 had quarter) + "float4(0.0f, 0.0f, 0.0f, 0.0f)", // ZERO + ///aded extra values to map clamped values + "cprev", // APREV, + "cc0", // A0, + "cc1", // A1, + "cc2", // A2, + "textemp", // TEXA, + "crastemp", // RASA, + "ckonsttemp", // KONST, (hw1 had quarter) + "float4(0.0f, 0.0f, 0.0f, 0.0f)", // ZERO + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", +}; + +static const char *tevRasTable[] = +{ + "colors_0", + "colors_1", + "ERROR", //2 + "ERROR", //3 + "ERROR", //4 + "alphabump", // use bump alpha + "(alphabump*(255.0f/248.0f))", //normalized + "float4(0.0f, 0.0f, 0.0f, 0.0f)", // zero +}; + +//static const char *tevTexFunc[] = { "tex2D", "texRECT" }; + +static const char *tevCOutputTable[] = { "prev.rgb", "c0.rgb", "c1.rgb", "c2.rgb" }; +static const char *tevAOutputTable[] = { "prev.a", "c0.a", "c1.a", "c2.a" }; +static const char *tevIndAlphaSel[] = {"", "x", "y", "z"}; +//static const char *tevIndAlphaScale[] = {"", "*32", "*16", "*8"}; +static const char *tevIndAlphaScale[] = {"*(248.0f/255.0f)", "*(224.0f/255.0f)", "*(240.0f/255.0f)", "*(248.0f/255.0f)"}; +static const char *tevIndBiasField[] = {"", "x", "y", "xy", "z", "xz", "yz", "xyz"}; // indexed by bias +static const char *tevIndBiasAdd[] = {"-128.0f", "1.0f", "1.0f", "1.0f" }; // indexed by fmt +static const char *tevIndWrapStart[] = {"0.0f", "256.0f", "128.0f", "64.0f", "32.0f", "16.0f", "0.001f" }; +static const char *tevIndFmtScale[] = {"255.0f", "31.0f", "15.0f", "7.0f" }; + +#define WRITE p+=sprintf + +static char swapModeTable[4][5]; + +static char text[16384]; +static bool DepthTextureEnable; + +static void BuildSwapModeTable() +{ + static const char *swapColors = "rgba"; + for (int i = 0; i < 4; i++) + { + swapModeTable[i][0] = swapColors[bpmem.tevksel[i*2].swap1]; + swapModeTable[i][1] = swapColors[bpmem.tevksel[i*2].swap2]; + swapModeTable[i][2] = swapColors[bpmem.tevksel[i*2+1].swap1]; + swapModeTable[i][3] = swapColors[bpmem.tevksel[i*2+1].swap2]; + swapModeTable[i][4] = 0; + } +} + +const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType, u32 components) +{ + setlocale(LC_NUMERIC, "C"); // Reset locale for compilation + text[sizeof(text) - 1] = 0x7C; // canary + + BuildSwapModeTable(); // Needed for WriteStage + int numStages = bpmem.genMode.numtevstages + 1; + int numTexgen = bpmem.genMode.numtexgens; + + char *p = text; + WRITE(p, "//Pixel Shader for TEV stages\n"); + WRITE(p, "//%i TEV stages, %i texgens, XXX IND stages\n", + numStages, numTexgen/*, bpmem.genMode.numindstages*/); + + int nIndirectStagesUsed = 0; + if (bpmem.genMode.numindstages > 0) + { + for (int i = 0; i < numStages; ++i) + { + if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) + nIndirectStagesUsed |= 1 << bpmem.tevind[i].bt; + } + } + DepthTextureEnable = (bpmem.ztex2.op != ZTEXTURE_DISABLE && !bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable) || g_ActiveConfig.bEnablePerPixelDepth ; + // Declare samplers + + if(ApiType != API_D3D11) + { + WRITE(p, "uniform sampler2D "); + } + else + { + WRITE(p, "sampler "); + } + + bool bfirst = true; + for (int i = 0; i < 8; ++i) + { + WRITE(p, "%s samp%d : register(s%d)", bfirst?"":",", i, i); + bfirst = false; + } + WRITE(p, ";\n"); + if(ApiType == API_D3D11) + { + WRITE(p, "Texture2D "); + bfirst = true; + for (int i = 0; i < 8; ++i) + { + WRITE(p, "%s Tex%d : register(t%d)", bfirst?"":",", i, i); + bfirst = false; + } + WRITE(p, ";\n"); + } + + WRITE(p, "\n"); + + WRITE(p, "uniform float4 "I_COLORS"[4] : register(c%d);\n", C_COLORS); + WRITE(p, "uniform float4 "I_KCOLORS"[4] : register(c%d);\n", C_KCOLORS); + WRITE(p, "uniform float4 "I_ALPHA"[1] : register(c%d);\n", C_ALPHA); + WRITE(p, "uniform float4 "I_TEXDIMS"[8] : register(c%d);\n", C_TEXDIMS); + WRITE(p, "uniform float4 "I_ZBIAS"[2] : register(c%d);\n", C_ZBIAS); + WRITE(p, "uniform float4 "I_INDTEXSCALE"[2] : register(c%d);\n", C_INDTEXSCALE); + WRITE(p, "uniform float4 "I_INDTEXMTX"[6] : register(c%d);\n", C_INDTEXMTX); + WRITE(p, "uniform float4 "I_FOG"[3] : register(c%d);\n", C_FOG); + + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + { + WRITE(p,"typedef struct { float4 col; float4 cosatt; float4 distatt; float4 pos; float4 dir; } Light;\n"); + WRITE(p,"typedef struct { Light lights[8]; } s_"I_PLIGHTS";\n"); + WRITE(p, "uniform s_"I_PLIGHTS" "I_PLIGHTS" : register(c%d);\n", C_PLIGHTS); + WRITE(p, "typedef struct { float4 C0, C1, C2, C3; } s_"I_PMATERIALS";\n"); + WRITE(p, "uniform s_"I_PMATERIALS" "I_PMATERIALS" : register(c%d);\n", C_PMATERIALS); + } + + WRITE(p, "void main(\n"); + if(ApiType != API_D3D11) + { + WRITE(p, " out float4 ocol0 : COLOR0,%s%s\n in float4 rawpos : %s,\n", + dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "\n out float4 ocol1 : COLOR1," : "", + DepthTextureEnable ? "\n out float depth : DEPTH," : "", + ApiType & API_OPENGL ? "WPOS" : ApiType & API_D3D9_SM20 ? "POSITION" : "VPOS"); + } + else + { + WRITE(p, " out float4 ocol0 : SV_Target0,%s%s\n in float4 rawpos : SV_Position,\n", + dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "\n out float4 ocol1 : SV_Target1," : "", + DepthTextureEnable ? "\n out float depth : SV_Depth," : ""); + } + + WRITE(p, " in float4 colors_0 : COLOR0,\n"); + WRITE(p, " in float4 colors_1 : COLOR1"); + + // compute window position if needed because binding semantic WPOS is not widely supported + if (numTexgen < 7) + { + for (int i = 0; i < numTexgen; ++i) + WRITE(p, ",\n in float3 uv%d : TEXCOORD%d", i, i); + WRITE(p, ",\n in float4 clipPos : TEXCOORD%d", numTexgen); + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + WRITE(p, ",\n in float4 Normal : TEXCOORD%d", numTexgen + 1); + } + else + { + // wpos is in w of first 4 texcoords + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + { + for (int i = 0; i < 8; ++i) + WRITE(p, ",\n in float4 uv%d : TEXCOORD%d", i, i); + } + else + { + for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) + WRITE(p, ",\n in float%d uv%d : TEXCOORD%d", i < 4 ? 4 : 3 , i, i); + } + } + WRITE(p, " ) {\n"); + + char* pmainstart = p; + int Pretest = AlphaPreTest(); + if (dstAlphaMode == DSTALPHA_ALPHA_PASS && !DepthTextureEnable && Pretest >= 0) + { + if (!Pretest) + { + // alpha test will always fail, so restart the shader and just make it an empty function + WRITE(p, "ocol0 = 0;\n"); + WRITE(p, "discard;\n"); + if(ApiType != API_D3D11) + WRITE(p, "return;\n"); + } + else + { + WRITE(p, " ocol0 = "I_ALPHA"[0].aaaa;\n"); + } + WRITE(p, "}\n"); + return text; + } + + WRITE(p, " float4 c0 = "I_COLORS"[1], c1 = "I_COLORS"[2], c2 = "I_COLORS"[3], prev = float4(0.0f, 0.0f, 0.0f, 0.0f), textemp = float4(0.0f, 0.0f, 0.0f, 0.0f), rastemp = float4(0.0f, 0.0f, 0.0f, 0.0f), konsttemp = float4(0.0f, 0.0f, 0.0f, 0.0f);\n" + " float3 comp16 = float3(1.0f, 255.0f, 0.0f), comp24 = float3(1.0f, 255.0f, 255.0f*255.0f);\n" + " float4 alphabump=float4(0.0f,0.0f,0.0f,0.0f);\n" + " float3 tevcoord=float3(0.0f, 0.0f, 0.0f);\n" + " float2 wrappedcoord=float2(0.0f,0.0f), tempcoord=float2(0.0f,0.0f);\n" + " float4 cc0=float4(0.0f,0.0f,0.0f,0.0f), cc1=float4(0.0f,0.0f,0.0f,0.0f);\n" + " float4 cc2=float4(0.0f,0.0f,0.0f,0.0f), cprev=float4(0.0f,0.0f,0.0f,0.0f);\n" + " float4 crastemp=float4(0.0f,0.0f,0.0f,0.0f),ckonsttemp=float4(0.0f,0.0f,0.0f,0.0f);\n\n"); + + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + { + if (xfregs.numTexGen.numTexGens < 7) + { + WRITE(p,"float3 _norm0 = normalize(Normal.xyz);\n\n"); + WRITE(p,"float3 pos = float3(clipPos.x,clipPos.y,Normal.w);\n"); + } + else + { + WRITE(p," float3 _norm0 = normalize(float3(uv4.w,uv5.w,uv6.w));\n\n"); + WRITE(p,"float3 pos = float3(uv0.w,uv1.w,uv7.w);\n"); + } + + + WRITE(p, "float4 mat, lacc;\n" + "float3 ldir, h;\n" + "float dist, dist2, attn;\n"); + + p = GenerateLightingShader(p, components, I_PMATERIALS, I_PLIGHTS, "colors_", "colors_"); + } + + if (numTexgen < 7) + WRITE(p, "clipPos = float4(rawpos.x, rawpos.y, clipPos.z, clipPos.w);\n"); + else + WRITE(p, "float4 clipPos = float4(rawpos.x, rawpos.y, uv2.w, uv3.w);\n"); + + // HACK to handle cases where the tex gen is not enabled + if (numTexgen == 0) + { + WRITE(p, "float3 uv0 = float3(0.0f, 0.0f, 0.0f);\n"); + } + else + { + for (int i = 0; i < numTexgen; ++i) + { + // optional perspective divides + if (xfregs.texMtxInfo[i].projection == XF_TEXPROJ_STQ) + { + WRITE(p, "if (uv%d.z)", i); + WRITE(p, " uv%d.xy = uv%d.xy / uv%d.z;\n", i, i, i); + } + + WRITE(p, "uv%d.xy = uv%d.xy * "I_TEXDIMS"[%d].zw;\n", i, i, i); + } + } + + // indirect texture map lookup + for(u32 i = 0; i < bpmem.genMode.numindstages; ++i) + { + if (nIndirectStagesUsed & (1<= %s.r + (0.25f/255.0f)) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_R8_GT 8 + " %s + ((abs(%s.r - %s.r) < (0.5f/255.0f)) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_R8_EQ 9 + " %s + (( dot(%s.rgb, comp16) >= (dot(%s.rgb, comp16) + (0.25f/255.0f))) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_GR16_GT 10 + " %s + (abs(dot(%s.rgb, comp16) - dot(%s.rgb, comp16)) < (0.5f/255.0f) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_GR16_EQ 11 + " %s + (( dot(%s.rgb, comp24) >= (dot(%s.rgb, comp24) + (0.25f/255.0f))) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_BGR24_GT 12 + " %s + (abs(dot(%s.rgb, comp24) - dot(%s.rgb, comp24)) < (0.5f/255.0f) ? %s : float3(0.0f, 0.0f, 0.0f))",//#define TEVCMP_BGR24_EQ 13 + " %s + (max(sign(%s.rgb - %s.rgb - (0.25f/255.0f)), float3(0.0f, 0.0f, 0.0f)) * %s)",//#define TEVCMP_RGB8_GT 14 + " %s + ((float3(1.0f, 1.0f, 1.0f) - max(sign(abs(%s.rgb - %s.rgb) - (0.5f/255.0f)), float3(0.0f, 0.0f, 0.0f))) * %s)"//#define TEVCMP_RGB8_EQ 15 +}; + +//table with the alpha compare operations +static const char *TEVCMPAlphaOPTable[16] = +{ + "0.0f",//0 + "0.0f",//1 + "0.0f",//2 + "0.0f",//3 + "0.0f",//4 + "0.0f",//5 + "0.0f",//6 + "0.0f",//7 + " %s.a + ((%s.r >= (%s.r + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_R8_GT 8 + " %s.a + (abs(%s.r - %s.r) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_R8_EQ 9 + " %s.a + ((dot(%s.rgb, comp16) >= (dot(%s.rgb, comp16) + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_GR16_GT 10 + " %s.a + (abs(dot(%s.rgb, comp16) - dot(%s.rgb, comp16)) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_GR16_EQ 11 + " %s.a + ((dot(%s.rgb, comp24) >= (dot(%s.rgb, comp24) + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_BGR24_GT 12 + " %s.a + (abs(dot(%s.rgb, comp24) - dot(%s.rgb, comp24)) < (0.5f/255.0f) ? %s.a : 0.0f)",//#define TEVCMP_BGR24_EQ 13 + " %s.a + ((%s.a >= (%s.a + (0.25f/255.0f))) ? %s.a : 0.0f)",//#define TEVCMP_A8_GT 14 + " %s.a + (abs(%s.a - %s.a) < (0.5f/255.0f) ? %s.a : 0.0f)"//#define TEVCMP_A8_EQ 15 + +}; + + +static void WriteStage(char *&p, int n, API_TYPE ApiType) +{ + int texcoord = bpmem.tevorders[n/2].getTexCoord(n&1); + bool bHasTexCoord = (u32)texcoord < bpmem.genMode.numtexgens; + bool bHasIndStage = bpmem.tevind[n].IsActive() && bpmem.tevind[n].bt < bpmem.genMode.numindstages; + + // HACK to handle cases where the tex gen is not enabled + if (!bHasTexCoord) + texcoord = 0; + + WRITE(p, "// TEV stage %d\n", n); + + if (bHasIndStage) + { + WRITE(p, "// indirect op\n"); + // perform the indirect op on the incoming regular coordinates using indtex%d as the offset coords + if (bpmem.tevind[n].bs != ITBA_OFF) + { + WRITE(p, "alphabump = indtex%d.%s %s;\n", + bpmem.tevind[n].bt, + tevIndAlphaSel[bpmem.tevind[n].bs], + tevIndAlphaScale[bpmem.tevind[n].fmt]); + } + // format + WRITE(p, "float3 indtevcrd%d = indtex%d * %s;\n", n, bpmem.tevind[n].bt, tevIndFmtScale[bpmem.tevind[n].fmt]); + + // bias + if (bpmem.tevind[n].bias != ITB_NONE ) + WRITE(p, "indtevcrd%d.%s += %s;\n", n, tevIndBiasField[bpmem.tevind[n].bias], tevIndBiasAdd[bpmem.tevind[n].fmt]); + + // multiply by offset matrix and scale + if (bpmem.tevind[n].mid != 0) + { + if (bpmem.tevind[n].mid <= 3) + { + int mtxidx = 2*(bpmem.tevind[n].mid-1); + WRITE(p, "float2 indtevtrans%d = float2(dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d), dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d));\n", + n, mtxidx, n, mtxidx+1, n); + } + else if (bpmem.tevind[n].mid <= 7 && bHasTexCoord) + { // s matrix + // TODO: Might become negative? + int mtxidx = 2*(bpmem.tevind[n].mid-5); + WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.xx;\n", n, mtxidx, texcoord, n); + } + else if (bpmem.tevind[n].mid <= 11 && bHasTexCoord) + { // t matrix + // TODO: Might become negative? + int mtxidx = 2*(bpmem.tevind[n].mid-9); + WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.yy;\n", n, mtxidx, texcoord, n); + } + else + WRITE(p, "float2 indtevtrans%d = 0;\n", n); + } + else + WRITE(p, "float2 indtevtrans%d = 0;\n", n); + + // --------- + // Wrapping + // --------- + + // wrap S + if (bpmem.tevind[n].sw == ITW_OFF) + WRITE(p, "wrappedcoord.x = uv%d.x;\n", texcoord); + else if (bpmem.tevind[n].sw == ITW_0) + WRITE(p, "wrappedcoord.x = 0.0f;\n"); + else + WRITE(p, "wrappedcoord.x = fmod( uv%d.x, %s );\n", texcoord, tevIndWrapStart[bpmem.tevind[n].sw]); + + // wrap T + if (bpmem.tevind[n].tw == ITW_OFF) + WRITE(p, "wrappedcoord.y = uv%d.y;\n", texcoord); + else if (bpmem.tevind[n].tw == ITW_0) + WRITE(p, "wrappedcoord.y = 0.0f;\n"); + else + WRITE(p, "wrappedcoord.y = fmod( uv%d.y, %s );\n", texcoord, tevIndWrapStart[bpmem.tevind[n].tw]); + + if (bpmem.tevind[n].fb_addprev) // add previous tevcoord + WRITE(p, "tevcoord.xy += wrappedcoord + indtevtrans%d;\n", n); + else + WRITE(p, "tevcoord.xy = wrappedcoord + indtevtrans%d;\n", n); + } + + TevStageCombiner::ColorCombiner &cc = bpmem.combiners[n].colorC; + TevStageCombiner::AlphaCombiner &ac = bpmem.combiners[n].alphaC; + + // blah1 + if(cc.a == TEVCOLORARG_RASA || cc.a == TEVCOLORARG_RASC + || cc.b == TEVCOLORARG_RASA || cc.b == TEVCOLORARG_RASC + || cc.c == TEVCOLORARG_RASA || cc.c == TEVCOLORARG_RASC + || cc.d == TEVCOLORARG_RASA || cc.d == TEVCOLORARG_RASC + || ac.a == TEVALPHAARG_RASA || ac.b == TEVALPHAARG_RASA + || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA) + { + char *rasswap = swapModeTable[bpmem.combiners[n].alphaC.rswap]; + WRITE(p, "rastemp = %s.%s;\n", tevRasTable[bpmem.tevorders[n / 2].getColorChan(n & 1)], rasswap); + WRITE(p, "crastemp = frac(rastemp * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + } + + + if (bpmem.tevorders[n/2].getEnable(n&1)) + { + if(!bHasIndStage) + { + // calc tevcord + if(bHasTexCoord) + WRITE(p, "tevcoord.xy = uv%d.xy;\n", texcoord); + else + WRITE(p, "tevcoord.xy = float2(0.0f, 0.0f);\n"); + } + + char *texswap = swapModeTable[bpmem.combiners[n].alphaC.tswap]; + int texmap = bpmem.tevorders[n/2].getTexMap(n&1); + SampleTexture(p, "textemp", "tevcoord", texswap, texmap, ApiType); + } + else + WRITE(p, "textemp = float4(1.0f, 1.0f, 1.0f, 1.0f);\n"); + + + // blah2 + if (cc.a == TEVCOLORARG_KONST || cc.b == TEVCOLORARG_KONST || cc.c == TEVCOLORARG_KONST || cc.d == TEVCOLORARG_KONST + || ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST || ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST) + { + int kc = bpmem.tevksel[n / 2].getKC(n & 1); + int ka = bpmem.tevksel[n / 2].getKA(n & 1); + WRITE(p, "konsttemp = float4(%s, %s);\n", tevKSelTableC[kc], tevKSelTableA[ka]); + if(kc > 7 || ka > 7) + { + WRITE(p, "ckonsttemp = frac(konsttemp * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + } + else + { + WRITE(p, "ckonsttemp = konsttemp;\n"); + } + } + + if(cc.a == TEVCOLORARG_CPREV || cc.a == TEVCOLORARG_APREV + || cc.b == TEVCOLORARG_CPREV || cc.b == TEVCOLORARG_APREV + || cc.c == TEVCOLORARG_CPREV || cc.c == TEVCOLORARG_APREV + || ac.a == TEVALPHAARG_APREV || ac.b == TEVALPHAARG_APREV || ac.c == TEVALPHAARG_APREV) + WRITE(p, "cprev = frac(prev * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + + + if(cc.a == TEVCOLORARG_C0 || cc.a == TEVCOLORARG_A0 + || cc.b == TEVCOLORARG_C0 || cc.b == TEVCOLORARG_A0 + || cc.c == TEVCOLORARG_C0 || cc.c == TEVCOLORARG_A0 + || ac.a == TEVALPHAARG_A0 || ac.b == TEVALPHAARG_A0 || ac.c == TEVALPHAARG_A0) + WRITE(p, "cc0 = frac(c0 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + + + if(cc.a == TEVCOLORARG_C1 || cc.a == TEVCOLORARG_A1 + || cc.b == TEVCOLORARG_C1 || cc.b == TEVCOLORARG_A1 + || cc.c == TEVCOLORARG_C1 || cc.c == TEVCOLORARG_A1 + || ac.a == TEVALPHAARG_A1 || ac.b == TEVALPHAARG_A1 || ac.c == TEVALPHAARG_A1) + WRITE(p, "cc1 = frac(c1 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + + + if(cc.a == TEVCOLORARG_C2 || cc.a == TEVCOLORARG_A2 + || cc.b == TEVCOLORARG_C2 || cc.b == TEVCOLORARG_A2 + || cc.c == TEVCOLORARG_C2 || cc.c == TEVCOLORARG_A2 + || ac.a == TEVALPHAARG_A2 || ac.b == TEVALPHAARG_A2 || ac.c == TEVALPHAARG_A2) + WRITE(p, "cc2 = frac(c2 * (255.0f/256.0f)) * (256.0f/255.0f);\n"); + + + WRITE(p, "// color combine\n"); + if (cc.clamp) + WRITE(p, "%s = saturate(", tevCOutputTable[cc.dest]); + else + WRITE(p, "%s = ", tevCOutputTable[cc.dest]); + + // combine the color channel + if (cc.bias != TevBias_COMPARE) // if not compare + { + //normal color combiner goes here + if (cc.shift > TEVSCALE_1) + WRITE(p, "%s*(", tevScaleTable[cc.shift]); + + if(!(cc.d == TEVCOLORARG_ZERO && cc.op == TEVOP_ADD)) + WRITE(p, "%s%s", tevCInputTable[cc.d], tevOpTable[cc.op]); + + if (cc.a == cc.b) + WRITE(p, "%s", tevCInputTable[cc.a + 16]); + else if (cc.c == TEVCOLORARG_ZERO) + WRITE(p, "%s", tevCInputTable[cc.a + 16]); + else if (cc.c == TEVCOLORARG_ONE) + WRITE(p, "%s", tevCInputTable[cc.b + 16]); + else if (cc.a == TEVCOLORARG_ZERO) + WRITE(p, "%s*%s", tevCInputTable[cc.b + 16], tevCInputTable[cc.c + 16]); + else if (cc.b == TEVCOLORARG_ZERO) + WRITE(p, "%s*(float3(1.0f, 1.0f, 1.0f)-%s)", tevCInputTable[cc.a + 16], tevCInputTable[cc.c + 16]); + else + WRITE(p, "lerp(%s, %s, %s)", tevCInputTable[cc.a + 16], tevCInputTable[cc.b + 16], tevCInputTable[cc.c + 16]); + + WRITE(p, "%s", tevBiasTable[cc.bias]); + + if (cc.shift > TEVSCALE_1) + WRITE(p, ")"); + } + else + { + int cmp = (cc.shift<<1)|cc.op|8; // comparemode stored here + WRITE(p, TEVCMPColorOPTable[cmp],//lookup the function from the op table + tevCInputTable[cc.d], + tevCInputTable[cc.a + 16], + tevCInputTable[cc.b + 16], + tevCInputTable[cc.c + 16]); + } + if (cc.clamp) + WRITE(p, ")"); + WRITE(p,";\n"); + + WRITE(p, "// alpha combine\n"); + // combine the alpha channel + if (ac.clamp) + WRITE(p, "%s = saturate(", tevAOutputTable[ac.dest]); + else + WRITE(p, "%s = ", tevAOutputTable[ac.dest]); + + if (ac.bias != TevBias_COMPARE) // if not compare + { + //normal alpha combiner goes here + if (ac.shift > TEVSCALE_1) + WRITE(p, "%s*(", tevScaleTable[ac.shift]); + + if(!(ac.d == TEVALPHAARG_ZERO && ac.op == TEVOP_ADD)) + WRITE(p, "%s.a%s", tevAInputTable[ac.d], tevOpTable[ac.op]); + + if (ac.a == ac.b) + WRITE(p, "%s.a", tevAInputTable[ac.a + 8]); + else if (ac.c == TEVALPHAARG_ZERO) + WRITE(p, "%s.a", tevAInputTable[ac.a + 8]); + else if (ac.a == TEVALPHAARG_ZERO) + WRITE(p, "%s.a*%s.a", tevAInputTable[ac.b + 8], tevAInputTable[ac.c + 8]); + else if (ac.b == TEVALPHAARG_ZERO) + WRITE(p, "%s.a*(1.0f-%s.a)", tevAInputTable[ac.a + 8], tevAInputTable[ac.c + 8]); + else + WRITE(p, "lerp(%s.a, %s.a, %s.a)", tevAInputTable[ac.a + 8], tevAInputTable[ac.b + 8], tevAInputTable[ac.c + 8]); + + WRITE(p, "%s",tevBiasTable[ac.bias]); + + if (ac.shift>0) + WRITE(p, ")"); + + } + else + { + //compare alpha combiner goes here + int cmp = (ac.shift<<1)|ac.op|8; // comparemode stored here + WRITE(p, TEVCMPAlphaOPTable[cmp], + tevAInputTable[ac.d], + tevAInputTable[ac.a + 8], + tevAInputTable[ac.b + 8], + tevAInputTable[ac.c + 8]); + } + if (ac.clamp) + WRITE(p, ")"); + WRITE(p, ";\n\n"); + WRITE(p, "// TEV done\n"); +} + +void SampleTexture(char *&p, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType) +{ + if (ApiType == API_D3D11) + WRITE(p, "%s=Tex%d.Sample(samp%d,%s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap,texmap, texcoords, texmap, texswap); + else + WRITE(p, "%s=tex2D(samp%d,%s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap, texcoords, texmap, texswap); +} + +static const char *tevAlphaFuncsTable[] = +{ + "(false)", //ALPHACMP_NEVER 0 + "(prev.a <= %s - (0.25f/255.0f))", //ALPHACMP_LESS 1 + "(abs( prev.a - %s ) < (0.5f/255.0f))", //ALPHACMP_EQUAL 2 + "(prev.a < %s + (0.25f/255.0f))", //ALPHACMP_LEQUAL 3 + "(prev.a >= %s + (0.25f/255.0f))", //ALPHACMP_GREATER 4 + "(abs( prev.a - %s ) >= (0.5f/255.0f))", //ALPHACMP_NEQUAL 5 + "(prev.a > %s - (0.25f/255.0f))", //ALPHACMP_GEQUAL 6 + "(true)" //ALPHACMP_ALWAYS 7 +}; + +static const char *tevAlphaFunclogicTable[] = +{ + " && ", // and + " || ", // or + " != ", // xor + " == " // xnor +}; +static int AlphaPreTest() +{ + u32 op = bpmem.alphaFunc.logic; + u32 comp[2] = {bpmem.alphaFunc.comp0, bpmem.alphaFunc.comp1}; + + // First kill all the simple cases + switch(op) + { + case 0: // AND + if (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) return true; + if (comp[0] == ALPHACMP_NEVER || comp[1] == ALPHACMP_NEVER) return false; + break; + case 1: // OR + if (comp[0] == ALPHACMP_ALWAYS || comp[1] == ALPHACMP_ALWAYS) return true; + if (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)return false; + break; + case 2: // XOR + if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS)) + return true; + if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)) + return false; + break; + case 3: // XNOR + if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS)) + return false; + if ((comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)) + return true; + break; + default: PanicAlert("bad logic for alpha test? %08x", op); + } + return -1; +} + + +static bool WriteAlphaTest(char *&p, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode) +{ + static const char *alphaRef[2] = + { + I_ALPHA"[0].r", + I_ALPHA"[0].g" + }; + + int Pretest = AlphaPreTest(); + if(Pretest >= 0) + { + return Pretest != 0; + } + + // using discard then return works the same in cg and dx9 but not in dx11 + WRITE(p, "if(!( "); + + int compindex = bpmem.alphaFunc.comp0 % 8; + WRITE(p, tevAlphaFuncsTable[compindex],alphaRef[0]);//lookup the first component from the alpha function table + + WRITE(p, "%s", tevAlphaFunclogicTable[bpmem.alphaFunc.logic % 4]);//lookup the logic op + + compindex = bpmem.alphaFunc.comp1 % 8; + WRITE(p, tevAlphaFuncsTable[compindex],alphaRef[1]);//lookup the second component from the alpha function table + WRITE(p, ")){ocol0 = 0;%s%s discard;%s}\n", + dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "ocol1 = 0;" : "", + DepthTextureEnable ? "depth = 1.f;" : "", + (ApiType != API_D3D11) ? "return;" : ""); + return true; +} + +static const char *tevFogFuncsTable[] = +{ + "", //No Fog + "", //? + "", //Linear + "", //? + " fog = 1.0f - pow(2.0f, -8.0f * fog);\n", //exp + " fog = 1.0f - pow(2.0f, -8.0f * fog * fog);\n", //exp2 + " fog = pow(2.0f, -8.0f * (1.0f - fog));\n", //backward exp + " fog = 1.0f - fog;\n fog = pow(2.0f, -8.0f * fog * fog);\n" //backward exp2 +}; + +static void WriteFog(char *&p) +{ + if(bpmem.fog.c_proj_fsel.fsel == 0)return;//no Fog + + if (bpmem.fog.c_proj_fsel.proj == 0) + { + // perspective + // ze = A/(B - (Zs >> B_SHF) + WRITE (p, " float ze = "I_FOG"[1].x / ("I_FOG"[1].y - (zCoord / "I_FOG"[1].w));\n"); + } + else + { + // orthographic + // ze = a*Zs (here, no B_SHF) + WRITE (p, " float ze = "I_FOG"[1].x * zCoord;\n"); + } + + // x_adjust = sqrt((x-center)^2 + k^2)/k + // ze *= x_adjust + //this is complitly teorical as the real hard seems to use a table intead of calculate the values. + if(bpmem.fogRange.Base.Enabled) + { + WRITE (p, " float x_adjust = (2.0f * (clipPos.x / "I_FOG"[2].y)) - 1.0f - "I_FOG"[2].x;\n"); + WRITE (p, " x_adjust = sqrt(x_adjust * x_adjust + "I_FOG"[2].z * "I_FOG"[2].z) / "I_FOG"[2].z;\n"); + WRITE (p, " ze *= x_adjust;\n"); + } + + WRITE (p, " float fog = saturate(ze - "I_FOG"[1].z);\n"); + + if(bpmem.fog.c_proj_fsel.fsel > 3) + { + WRITE(p, "%s", tevFogFuncsTable[bpmem.fog.c_proj_fsel.fsel]); + } + else + { + if(bpmem.fog.c_proj_fsel.fsel != 2) + WARN_LOG(VIDEO, "Unknown Fog Type! %08x", bpmem.fog.c_proj_fsel.fsel); + } + + WRITE(p, " prev.rgb = lerp(prev.rgb,"I_FOG"[0].rgb,fog);\n"); + + } \ No newline at end of file diff --git a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp index a492d5a201..8400fde9a5 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp @@ -1,503 +1,503 @@ -// Copyright (C) 2003 Dolphin Project. - -// This program 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, version 2.0. - -// This program 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 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include - -#include "NativeVertexFormat.h" - -#include "BPMemory.h" -#include "CPMemory.h" -#include "LightingShaderGen.h" -#include "VertexShaderGen.h" -#include "VideoConfig.h" - -VERTEXSHADERUID last_vertex_shader_uid; - -// Mash together all the inputs that contribute to the code of a generated vertex shader into -// a unique identifier, basically containing all the bits. Yup, it's a lot .... -void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components) -{ - uid->values[0] = components | - (xfregs.numTexGen.numTexGens << 23) | - (xfregs.numChan.numColorChans << 27) | - (xfregs.dualTexTrans.enabled << 29); - - // TODO: If pixel lighting is enabled, do we even have to bother about storing lighting related registers here? - GetLightingShaderId(&uid->values[1]); - - uid->values[2] |= (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) << 31; - u32 *pcurvalue = &uid->values[3]; - for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) { - TexMtxInfo tinfo = xfregs.texMtxInfo[i]; - if (tinfo.texgentype != XF_TEXGEN_EMBOSS_MAP) - tinfo.hex &= 0x7ff; - if (tinfo.texgentype != XF_TEXGEN_REGULAR) - tinfo.projection = 0; - - u32 val = ((tinfo.hex >> 1) & 0x1ffff); - if (xfregs.dualTexTrans.enabled && tinfo.texgentype == XF_TEXGEN_REGULAR) { - // rewrite normalization and post index - val |= ((u32)xfregs.postMtxInfo[i].index << 17) | ((u32)xfregs.postMtxInfo[i].normalize << 23); - } - - switch (i & 3) { - case 0: pcurvalue[0] |= val; break; - case 1: pcurvalue[0] |= val << 24; pcurvalue[1] = val >> 8; ++pcurvalue; break; - case 2: pcurvalue[0] |= val << 16; pcurvalue[1] = val >> 16; ++pcurvalue; break; - case 3: pcurvalue[0] |= val << 8; ++pcurvalue; break; - } - } -} - -void GetSafeVertexShaderId(VERTEXSHADERUIDSAFE *uid, u32 components) -{ - // Just store all used registers here without caring whether we need all bits or less. - u32* ptr = uid->values; - *ptr++ = components; - *ptr++ = xfregs.numTexGen.hex; - *ptr++ = xfregs.numChan.hex; - *ptr++ = xfregs.dualTexTrans.hex; - - for (int i = 0; i < 2; ++i) { - *ptr++ = xfregs.color[i].hex; - *ptr++ = xfregs.alpha[i].hex; - } - *ptr++ = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; - for (unsigned int i = 0; i < 8; ++i) { - *ptr++ = xfregs.texMtxInfo[i].hex; - *ptr++ = xfregs.postMtxInfo[i].hex; - } - _assert_((ptr - uid->values) == uid->GetNumValues()); -} - - -void ValidateVertexShaderIDs(API_TYPE api, VERTEXSHADERUIDSAFE old_id, const std::string& old_code, u32 components) -{ - if (!g_ActiveConfig.bEnableShaderDebugging) - return; - - VERTEXSHADERUIDSAFE new_id; - GetSafeVertexShaderId(&new_id, components); - - if (!(old_id == new_id)) - { - std::string new_code(GenerateVertexShaderCode(components, api)); - if (old_code != new_code) - { - _assert_(old_id.GetNumValues() == new_id.GetNumValues()); - - char msg[8192]; - char* ptr = msg; - ptr += sprintf(ptr, "Vertex shader IDs matched but unique IDs did not!\nUnique IDs (old <-> new):\n"); - const int N = new_id.GetNumValues(); - for (int i = 0; i < N/2; ++i) - ptr += sprintf(ptr, "%02d, %08X %08X | %08X %08X\n", 2*i, old_id.values[2*i], old_id.values[2*i+1], - new_id.values[2*i], new_id.values[2*i+1]); - if (N % 2) - ptr += sprintf(ptr, "%02d, %08X | %08X\n", N-1, old_id.values[N-1], new_id.values[N-1]); - - static int num_failures = 0; - char szTemp[MAX_PATH]; - sprintf(szTemp, "%svsuid_mismatch_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++); - std::ofstream file(szTemp); - file << msg; - file << "\n\nOld shader code:\n" << old_code; - file << "\n\nNew shader code:\n" << new_code; - file.close(); - - PanicAlert("Unique pixel shader ID mismatch!\n\nReport this to the devs, along with the contents of %s.", szTemp); - } - } -} - - -static char text[16384]; - -#define WRITE p+=sprintf - -char* GenerateVSOutputStruct(char* p, u32 components, API_TYPE api_type) -{ - WRITE(p, "struct VS_OUTPUT {\n"); - WRITE(p, " float4 pos : POSITION;\n"); - WRITE(p, " float4 colors_0 : COLOR0;\n"); - WRITE(p, " float4 colors_1 : COLOR1;\n"); - - if (xfregs.numTexGen.numTexGens < 7) { - for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) - WRITE(p, " float3 tex%d : TEXCOORD%d;\n", i, i); - WRITE(p, " float4 clipPos : TEXCOORD%d;\n", xfregs.numTexGen.numTexGens); - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - WRITE(p, " float4 Normal : TEXCOORD%d;\n", xfregs.numTexGen.numTexGens + 1); - } else { - // clip position is in w of first 4 texcoords - if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) - { - for (int i = 0; i < 8; ++i) - WRITE(p, " float4 tex%d : TEXCOORD%d;\n", i, i); - } - else - { - for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) - WRITE(p, " float%d tex%d : TEXCOORD%d;\n", i < 4 ? 4 : 3 , i, i); - } - } - WRITE(p, "};\n"); - - return p; -} - -const char *GenerateVertexShaderCode(u32 components, API_TYPE api_type) -{ - setlocale(LC_NUMERIC, "C"); // Reset locale for compilation - text[sizeof(text) - 1] = 0x7C; // canary - - _assert_(bpmem.genMode.numtexgens == xfregs.numTexGen.numTexGens); - _assert_(bpmem.genMode.numcolchans == xfregs.numChan.numColorChans); - - bool is_d3d = (api_type & API_D3D9 || api_type == API_D3D11); - u32 lightMask = 0; - if (xfregs.numChan.numColorChans > 0) - lightMask |= xfregs.color[0].GetFullLightMask() | xfregs.alpha[0].GetFullLightMask(); - if (xfregs.numChan.numColorChans > 1) - lightMask |= xfregs.color[1].GetFullLightMask() | xfregs.alpha[1].GetFullLightMask(); - - char *p = text; - WRITE(p, "//Vertex Shader: comp:%x, \n", components); - WRITE(p, "typedef struct { float4 T0, T1, T2; float4 N0, N1, N2; } s_"I_POSNORMALMATRIX";\n" - "typedef struct { float4 t; } FLT4;\n" - "typedef struct { FLT4 T[24]; } s_"I_TEXMATRICES";\n" - "typedef struct { FLT4 T[64]; } s_"I_TRANSFORMMATRICES";\n" - "typedef struct { FLT4 T[32]; } s_"I_NORMALMATRICES";\n" - "typedef struct { FLT4 T[64]; } s_"I_POSTTRANSFORMMATRICES";\n" - "typedef struct { float4 col; float4 cosatt; float4 distatt; float4 pos; float4 dir; } Light;\n" - "typedef struct { Light lights[8]; } s_"I_LIGHTS";\n" - "typedef struct { float4 C0, C1, C2, C3; } s_"I_MATERIALS";\n" - "typedef struct { float4 T0, T1, T2, T3; } s_"I_PROJECTION";\n" - ); - - p = GenerateVSOutputStruct(p, components, api_type); - - // uniforms - - WRITE(p, "uniform s_"I_TRANSFORMMATRICES" "I_TRANSFORMMATRICES" : register(c%d);\n", C_TRANSFORMMATRICES); - WRITE(p, "uniform s_"I_TEXMATRICES" "I_TEXMATRICES" : register(c%d);\n", C_TEXMATRICES); // also using tex matrices - WRITE(p, "uniform s_"I_NORMALMATRICES" "I_NORMALMATRICES" : register(c%d);\n", C_NORMALMATRICES); - WRITE(p, "uniform s_"I_POSNORMALMATRIX" "I_POSNORMALMATRIX" : register(c%d);\n", C_POSNORMALMATRIX); - WRITE(p, "uniform s_"I_POSTTRANSFORMMATRICES" "I_POSTTRANSFORMMATRICES" : register(c%d);\n", C_POSTTRANSFORMMATRICES); - WRITE(p, "uniform s_"I_LIGHTS" "I_LIGHTS" : register(c%d);\n", C_LIGHTS); - WRITE(p, "uniform s_"I_MATERIALS" "I_MATERIALS" : register(c%d);\n", C_MATERIALS); - WRITE(p, "uniform s_"I_PROJECTION" "I_PROJECTION" : register(c%d);\n", C_PROJECTION); - WRITE(p, "uniform float4 "I_DEPTHPARAMS" : register(c%d);\n", C_DEPTHPARAMS); - - WRITE(p, "VS_OUTPUT main(\n"); - - // inputs - if (components & VB_HAS_NRM0) - WRITE(p, " float3 rawnorm0 : NORMAL0,\n"); - if (components & VB_HAS_NRM1) { - if (is_d3d) - WRITE(p, " float3 rawnorm1 : NORMAL1,\n"); - else - WRITE(p, " float3 rawnorm1 : ATTR%d,\n", SHADER_NORM1_ATTRIB); - } - if (components & VB_HAS_NRM2) { - if (is_d3d) - WRITE(p, " float3 rawnorm2 : NORMAL2,\n"); - else - WRITE(p, " float3 rawnorm2 : ATTR%d,\n", SHADER_NORM2_ATTRIB); - } - if (components & VB_HAS_COL0) - WRITE(p, " float4 color0 : COLOR0,\n"); - if (components & VB_HAS_COL1) - WRITE(p, " float4 color1 : COLOR1,\n"); - for (int i = 0; i < 8; ++i) { - u32 hastexmtx = (components & (VB_HAS_TEXMTXIDX0<= 32 ? (posmtx-32) : posmtx;\n"); - WRITE(p, "float3 N0 = "I_NORMALMATRICES".T[normidx].t.xyz, N1 = "I_NORMALMATRICES".T[normidx+1].t.xyz, N2 = "I_NORMALMATRICES".T[normidx+2].t.xyz;\n"); - } - - if (components & VB_HAS_NRM0) - WRITE(p, "float3 _norm0 = normalize(float3(dot(N0, rawnorm0), dot(N1, rawnorm0), dot(N2, rawnorm0)));\n"); - if (components & VB_HAS_NRM1) - WRITE(p, "float3 _norm1 = float3(dot(N0, rawnorm1), dot(N1, rawnorm1), dot(N2, rawnorm1));\n"); - if (components & VB_HAS_NRM2) - WRITE(p, "float3 _norm2 = float3(dot(N0, rawnorm2), dot(N1, rawnorm2), dot(N2, rawnorm2));\n"); - } - else - { - WRITE(p, "float4 pos = float4(dot("I_POSNORMALMATRIX".T0, rawpos), dot("I_POSNORMALMATRIX".T1, rawpos), dot("I_POSNORMALMATRIX".T2, rawpos), 1.0f);\n"); - if (components & VB_HAS_NRM0) - WRITE(p, "float3 _norm0 = normalize(float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm0)));\n"); - if (components & VB_HAS_NRM1) - WRITE(p, "float3 _norm1 = float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm1));\n"); - if (components & VB_HAS_NRM2) - WRITE(p, "float3 _norm2 = float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm2));\n"); - } - - if (!(components & VB_HAS_NRM0)) - WRITE(p, "float3 _norm0 = float3(0.0f, 0.0f, 0.0f);\n"); - - - - WRITE(p, "o.pos = float4(dot("I_PROJECTION".T0, pos), dot("I_PROJECTION".T1, pos), dot("I_PROJECTION".T2, pos), dot("I_PROJECTION".T3, pos));\n"); - - WRITE(p, "float4 mat, lacc;\n" - "float3 ldir, h;\n" - "float dist, dist2, attn;\n"); - - if(xfregs.numChan.numColorChans == 0) - { - if (components & VB_HAS_COL0) - WRITE(p, "o.colors_0 = color0;\n"); - else - WRITE(p, "o.colors_0 = float4(1.0f, 1.0f, 1.0f, 1.0f);\n"); - } - - // TODO: This probably isn't necessary if pixel lighting is enabled. - p = GenerateLightingShader(p, components, I_MATERIALS, I_LIGHTS, "color", "o.colors_"); - - if(xfregs.numChan.numColorChans < 2) - { - if (components & VB_HAS_COL1) - WRITE(p, "o.colors_1 = color1;\n"); - else - WRITE(p, "o.colors_1 = o.colors_0;\n"); - } - // special case if only pos and tex coord 0 and tex coord input is AB11 - // donko - this has caused problems in some games. removed for now. - bool texGenSpecialCase = false; - /*bool texGenSpecialCase = - ((g_VtxDesc.Hex & 0x60600L) == g_VtxDesc.Hex) && // only pos and tex coord 0 - (g_VtxDesc.Tex0Coord != NOT_PRESENT) && - (xfregs.texcoords[0].texmtxinfo.inputform == XF_TEXINPUT_AB11); - */ - - // transform texcoords - WRITE(p, "float4 coord = float4(0.0f, 0.0f, 1.0f, 1.0f);\n"); - for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) { - TexMtxInfo& texinfo = xfregs.texMtxInfo[i]; - - WRITE(p, "{\n"); - WRITE(p, "coord = float4(0.0f, 0.0f, 1.0f, 1.0f);\n"); - switch (texinfo.sourcerow) { - case XF_SRCGEOM_INROW: - _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); - WRITE(p, "coord = rawpos;\n"); // pos.w is 1 - break; - case XF_SRCNORMAL_INROW: - if (components & VB_HAS_NRM0) { - _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); - WRITE(p, "coord = float4(rawnorm0.xyz, 1.0f);\n"); - } - break; - case XF_SRCCOLORS_INROW: - _assert_( texinfo.texgentype == XF_TEXGEN_COLOR_STRGBC0 || texinfo.texgentype == XF_TEXGEN_COLOR_STRGBC1 ); - break; - case XF_SRCBINORMAL_T_INROW: - if (components & VB_HAS_NRM1) { - _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); - WRITE(p, "coord = float4(rawnorm1.xyz, 1.0f);\n"); - } - break; - case XF_SRCBINORMAL_B_INROW: - if (components & VB_HAS_NRM2) { - _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); - WRITE(p, "coord = float4(rawnorm2.xyz, 1.0f);\n"); - } - break; - default: - _assert_(texinfo.sourcerow <= XF_SRCTEX7_INROW); - if (components & (VB_HAS_UV0<<(texinfo.sourcerow - XF_SRCTEX0_INROW)) ) - WRITE(p, "coord = float4(tex%d.x, tex%d.y, 1.0f, 1.0f);\n", texinfo.sourcerow - XF_SRCTEX0_INROW, texinfo.sourcerow - XF_SRCTEX0_INROW); - break; - } - - // first transformation - switch (texinfo.texgentype) { - case XF_TEXGEN_EMBOSS_MAP: // calculate tex coords into bump map - - if (components & (VB_HAS_NRM1|VB_HAS_NRM2)) { - // transform the light dir into tangent space - WRITE(p, "ldir = normalize("I_LIGHTS".lights[%d].pos.xyz - pos.xyz);\n", texinfo.embosslightshift); - WRITE(p, "o.tex%d.xyz = o.tex%d.xyz + float3(dot(ldir, _norm1), dot(ldir, _norm2), 0.0f);\n", i, texinfo.embosssourceshift); - } - else - { - _assert_(0); // should have normals - WRITE(p, "o.tex%d.xyz = o.tex%d.xyz;\n", i, texinfo.embosssourceshift); - } - - break; - case XF_TEXGEN_COLOR_STRGBC0: - _assert_(texinfo.sourcerow == XF_SRCCOLORS_INROW); - WRITE(p, "o.tex%d.xyz = float3(o.colors_0.x, o.colors_0.y, 1);\n", i); - break; - case XF_TEXGEN_COLOR_STRGBC1: - _assert_(texinfo.sourcerow == XF_SRCCOLORS_INROW); - WRITE(p, "o.tex%d.xyz = float3(o.colors_1.x, o.colors_1.y, 1);\n", i); - break; - case XF_TEXGEN_REGULAR: - default: - if (components & (VB_HAS_TEXMTXIDX0< +#include + +#include "NativeVertexFormat.h" + +#include "BPMemory.h" +#include "CPMemory.h" +#include "LightingShaderGen.h" +#include "VertexShaderGen.h" +#include "VideoConfig.h" + +VERTEXSHADERUID last_vertex_shader_uid; + +// Mash together all the inputs that contribute to the code of a generated vertex shader into +// a unique identifier, basically containing all the bits. Yup, it's a lot .... +void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components) +{ + uid->values[0] = components | + (xfregs.numTexGen.numTexGens << 23) | + (xfregs.numChan.numColorChans << 27) | + (xfregs.dualTexTrans.enabled << 29); + + // TODO: If pixel lighting is enabled, do we even have to bother about storing lighting related registers here? + GetLightingShaderId(&uid->values[1]); + + uid->values[2] |= (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) << 31; + u32 *pcurvalue = &uid->values[3]; + for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) { + TexMtxInfo tinfo = xfregs.texMtxInfo[i]; + if (tinfo.texgentype != XF_TEXGEN_EMBOSS_MAP) + tinfo.hex &= 0x7ff; + if (tinfo.texgentype != XF_TEXGEN_REGULAR) + tinfo.projection = 0; + + u32 val = ((tinfo.hex >> 1) & 0x1ffff); + if (xfregs.dualTexTrans.enabled && tinfo.texgentype == XF_TEXGEN_REGULAR) { + // rewrite normalization and post index + val |= ((u32)xfregs.postMtxInfo[i].index << 17) | ((u32)xfregs.postMtxInfo[i].normalize << 23); + } + + switch (i & 3) { + case 0: pcurvalue[0] |= val; break; + case 1: pcurvalue[0] |= val << 24; pcurvalue[1] = val >> 8; ++pcurvalue; break; + case 2: pcurvalue[0] |= val << 16; pcurvalue[1] = val >> 16; ++pcurvalue; break; + case 3: pcurvalue[0] |= val << 8; ++pcurvalue; break; + } + } +} + +void GetSafeVertexShaderId(VERTEXSHADERUIDSAFE *uid, u32 components) +{ + // Just store all used registers here without caring whether we need all bits or less. + u32* ptr = uid->values; + *ptr++ = components; + *ptr++ = xfregs.numTexGen.hex; + *ptr++ = xfregs.numChan.hex; + *ptr++ = xfregs.dualTexTrans.hex; + + for (int i = 0; i < 2; ++i) { + *ptr++ = xfregs.color[i].hex; + *ptr++ = xfregs.alpha[i].hex; + } + *ptr++ = g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting; + for (unsigned int i = 0; i < 8; ++i) { + *ptr++ = xfregs.texMtxInfo[i].hex; + *ptr++ = xfregs.postMtxInfo[i].hex; + } + _assert_((ptr - uid->values) == uid->GetNumValues()); +} + + +void ValidateVertexShaderIDs(API_TYPE api, VERTEXSHADERUIDSAFE old_id, const std::string& old_code, u32 components) +{ + if (!g_ActiveConfig.bEnableShaderDebugging) + return; + + VERTEXSHADERUIDSAFE new_id; + GetSafeVertexShaderId(&new_id, components); + + if (!(old_id == new_id)) + { + std::string new_code(GenerateVertexShaderCode(components, api)); + if (old_code != new_code) + { + _assert_(old_id.GetNumValues() == new_id.GetNumValues()); + + char msg[8192]; + char* ptr = msg; + ptr += sprintf(ptr, "Vertex shader IDs matched but unique IDs did not!\nUnique IDs (old <-> new):\n"); + const int N = new_id.GetNumValues(); + for (int i = 0; i < N/2; ++i) + ptr += sprintf(ptr, "%02d, %08X %08X | %08X %08X\n", 2*i, old_id.values[2*i], old_id.values[2*i+1], + new_id.values[2*i], new_id.values[2*i+1]); + if (N % 2) + ptr += sprintf(ptr, "%02d, %08X | %08X\n", N-1, old_id.values[N-1], new_id.values[N-1]); + + static int num_failures = 0; + char szTemp[MAX_PATH]; + sprintf(szTemp, "%svsuid_mismatch_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++); + std::ofstream file(szTemp); + file << msg; + file << "\n\nOld shader code:\n" << old_code; + file << "\n\nNew shader code:\n" << new_code; + file.close(); + + PanicAlert("Unique pixel shader ID mismatch!\n\nReport this to the devs, along with the contents of %s.", szTemp); + } + } +} + + +static char text[16384]; + +#define WRITE p+=sprintf + +char* GenerateVSOutputStruct(char* p, u32 components, API_TYPE api_type) +{ + WRITE(p, "struct VS_OUTPUT {\n"); + WRITE(p, " float4 pos : POSITION;\n"); + WRITE(p, " float4 colors_0 : COLOR0;\n"); + WRITE(p, " float4 colors_1 : COLOR1;\n"); + + if (xfregs.numTexGen.numTexGens < 7) { + for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) + WRITE(p, " float3 tex%d : TEXCOORD%d;\n", i, i); + WRITE(p, " float4 clipPos : TEXCOORD%d;\n", xfregs.numTexGen.numTexGens); + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + WRITE(p, " float4 Normal : TEXCOORD%d;\n", xfregs.numTexGen.numTexGens + 1); + } else { + // clip position is in w of first 4 texcoords + if(g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) + { + for (int i = 0; i < 8; ++i) + WRITE(p, " float4 tex%d : TEXCOORD%d;\n", i, i); + } + else + { + for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) + WRITE(p, " float%d tex%d : TEXCOORD%d;\n", i < 4 ? 4 : 3 , i, i); + } + } + WRITE(p, "};\n"); + + return p; +} + +const char *GenerateVertexShaderCode(u32 components, API_TYPE api_type) +{ + setlocale(LC_NUMERIC, "C"); // Reset locale for compilation + text[sizeof(text) - 1] = 0x7C; // canary + + _assert_(bpmem.genMode.numtexgens == xfregs.numTexGen.numTexGens); + _assert_(bpmem.genMode.numcolchans == xfregs.numChan.numColorChans); + + bool is_d3d = (api_type & API_D3D9 || api_type == API_D3D11); + u32 lightMask = 0; + if (xfregs.numChan.numColorChans > 0) + lightMask |= xfregs.color[0].GetFullLightMask() | xfregs.alpha[0].GetFullLightMask(); + if (xfregs.numChan.numColorChans > 1) + lightMask |= xfregs.color[1].GetFullLightMask() | xfregs.alpha[1].GetFullLightMask(); + + char *p = text; + WRITE(p, "//Vertex Shader: comp:%x, \n", components); + WRITE(p, "typedef struct { float4 T0, T1, T2; float4 N0, N1, N2; } s_"I_POSNORMALMATRIX";\n" + "typedef struct { float4 t; } FLT4;\n" + "typedef struct { FLT4 T[24]; } s_"I_TEXMATRICES";\n" + "typedef struct { FLT4 T[64]; } s_"I_TRANSFORMMATRICES";\n" + "typedef struct { FLT4 T[32]; } s_"I_NORMALMATRICES";\n" + "typedef struct { FLT4 T[64]; } s_"I_POSTTRANSFORMMATRICES";\n" + "typedef struct { float4 col; float4 cosatt; float4 distatt; float4 pos; float4 dir; } Light;\n" + "typedef struct { Light lights[8]; } s_"I_LIGHTS";\n" + "typedef struct { float4 C0, C1, C2, C3; } s_"I_MATERIALS";\n" + "typedef struct { float4 T0, T1, T2, T3; } s_"I_PROJECTION";\n" + ); + + p = GenerateVSOutputStruct(p, components, api_type); + + // uniforms + + WRITE(p, "uniform s_"I_TRANSFORMMATRICES" "I_TRANSFORMMATRICES" : register(c%d);\n", C_TRANSFORMMATRICES); + WRITE(p, "uniform s_"I_TEXMATRICES" "I_TEXMATRICES" : register(c%d);\n", C_TEXMATRICES); // also using tex matrices + WRITE(p, "uniform s_"I_NORMALMATRICES" "I_NORMALMATRICES" : register(c%d);\n", C_NORMALMATRICES); + WRITE(p, "uniform s_"I_POSNORMALMATRIX" "I_POSNORMALMATRIX" : register(c%d);\n", C_POSNORMALMATRIX); + WRITE(p, "uniform s_"I_POSTTRANSFORMMATRICES" "I_POSTTRANSFORMMATRICES" : register(c%d);\n", C_POSTTRANSFORMMATRICES); + WRITE(p, "uniform s_"I_LIGHTS" "I_LIGHTS" : register(c%d);\n", C_LIGHTS); + WRITE(p, "uniform s_"I_MATERIALS" "I_MATERIALS" : register(c%d);\n", C_MATERIALS); + WRITE(p, "uniform s_"I_PROJECTION" "I_PROJECTION" : register(c%d);\n", C_PROJECTION); + WRITE(p, "uniform float4 "I_DEPTHPARAMS" : register(c%d);\n", C_DEPTHPARAMS); + + WRITE(p, "VS_OUTPUT main(\n"); + + // inputs + if (components & VB_HAS_NRM0) + WRITE(p, " float3 rawnorm0 : NORMAL0,\n"); + if (components & VB_HAS_NRM1) { + if (is_d3d) + WRITE(p, " float3 rawnorm1 : NORMAL1,\n"); + else + WRITE(p, " float3 rawnorm1 : ATTR%d,\n", SHADER_NORM1_ATTRIB); + } + if (components & VB_HAS_NRM2) { + if (is_d3d) + WRITE(p, " float3 rawnorm2 : NORMAL2,\n"); + else + WRITE(p, " float3 rawnorm2 : ATTR%d,\n", SHADER_NORM2_ATTRIB); + } + if (components & VB_HAS_COL0) + WRITE(p, " float4 color0 : COLOR0,\n"); + if (components & VB_HAS_COL1) + WRITE(p, " float4 color1 : COLOR1,\n"); + for (int i = 0; i < 8; ++i) { + u32 hastexmtx = (components & (VB_HAS_TEXMTXIDX0<= 32 ? (posmtx-32) : posmtx;\n"); + WRITE(p, "float3 N0 = "I_NORMALMATRICES".T[normidx].t.xyz, N1 = "I_NORMALMATRICES".T[normidx+1].t.xyz, N2 = "I_NORMALMATRICES".T[normidx+2].t.xyz;\n"); + } + + if (components & VB_HAS_NRM0) + WRITE(p, "float3 _norm0 = normalize(float3(dot(N0, rawnorm0), dot(N1, rawnorm0), dot(N2, rawnorm0)));\n"); + if (components & VB_HAS_NRM1) + WRITE(p, "float3 _norm1 = float3(dot(N0, rawnorm1), dot(N1, rawnorm1), dot(N2, rawnorm1));\n"); + if (components & VB_HAS_NRM2) + WRITE(p, "float3 _norm2 = float3(dot(N0, rawnorm2), dot(N1, rawnorm2), dot(N2, rawnorm2));\n"); + } + else + { + WRITE(p, "float4 pos = float4(dot("I_POSNORMALMATRIX".T0, rawpos), dot("I_POSNORMALMATRIX".T1, rawpos), dot("I_POSNORMALMATRIX".T2, rawpos), 1.0f);\n"); + if (components & VB_HAS_NRM0) + WRITE(p, "float3 _norm0 = normalize(float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm0)));\n"); + if (components & VB_HAS_NRM1) + WRITE(p, "float3 _norm1 = float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm1));\n"); + if (components & VB_HAS_NRM2) + WRITE(p, "float3 _norm2 = float3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm2));\n"); + } + + if (!(components & VB_HAS_NRM0)) + WRITE(p, "float3 _norm0 = float3(0.0f, 0.0f, 0.0f);\n"); + + + + WRITE(p, "o.pos = float4(dot("I_PROJECTION".T0, pos), dot("I_PROJECTION".T1, pos), dot("I_PROJECTION".T2, pos), dot("I_PROJECTION".T3, pos));\n"); + + WRITE(p, "float4 mat, lacc;\n" + "float3 ldir, h;\n" + "float dist, dist2, attn;\n"); + + if(xfregs.numChan.numColorChans == 0) + { + if (components & VB_HAS_COL0) + WRITE(p, "o.colors_0 = color0;\n"); + else + WRITE(p, "o.colors_0 = float4(1.0f, 1.0f, 1.0f, 1.0f);\n"); + } + + // TODO: This probably isn't necessary if pixel lighting is enabled. + p = GenerateLightingShader(p, components, I_MATERIALS, I_LIGHTS, "color", "o.colors_"); + + if(xfregs.numChan.numColorChans < 2) + { + if (components & VB_HAS_COL1) + WRITE(p, "o.colors_1 = color1;\n"); + else + WRITE(p, "o.colors_1 = o.colors_0;\n"); + } + // special case if only pos and tex coord 0 and tex coord input is AB11 + // donko - this has caused problems in some games. removed for now. + bool texGenSpecialCase = false; + /*bool texGenSpecialCase = + ((g_VtxDesc.Hex & 0x60600L) == g_VtxDesc.Hex) && // only pos and tex coord 0 + (g_VtxDesc.Tex0Coord != NOT_PRESENT) && + (xfregs.texcoords[0].texmtxinfo.inputform == XF_TEXINPUT_AB11); + */ + + // transform texcoords + WRITE(p, "float4 coord = float4(0.0f, 0.0f, 1.0f, 1.0f);\n"); + for (unsigned int i = 0; i < xfregs.numTexGen.numTexGens; ++i) { + TexMtxInfo& texinfo = xfregs.texMtxInfo[i]; + + WRITE(p, "{\n"); + WRITE(p, "coord = float4(0.0f, 0.0f, 1.0f, 1.0f);\n"); + switch (texinfo.sourcerow) { + case XF_SRCGEOM_INROW: + _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); + WRITE(p, "coord = rawpos;\n"); // pos.w is 1 + break; + case XF_SRCNORMAL_INROW: + if (components & VB_HAS_NRM0) { + _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); + WRITE(p, "coord = float4(rawnorm0.xyz, 1.0f);\n"); + } + break; + case XF_SRCCOLORS_INROW: + _assert_( texinfo.texgentype == XF_TEXGEN_COLOR_STRGBC0 || texinfo.texgentype == XF_TEXGEN_COLOR_STRGBC1 ); + break; + case XF_SRCBINORMAL_T_INROW: + if (components & VB_HAS_NRM1) { + _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); + WRITE(p, "coord = float4(rawnorm1.xyz, 1.0f);\n"); + } + break; + case XF_SRCBINORMAL_B_INROW: + if (components & VB_HAS_NRM2) { + _assert_( texinfo.inputform == XF_TEXINPUT_ABC1 ); + WRITE(p, "coord = float4(rawnorm2.xyz, 1.0f);\n"); + } + break; + default: + _assert_(texinfo.sourcerow <= XF_SRCTEX7_INROW); + if (components & (VB_HAS_UV0<<(texinfo.sourcerow - XF_SRCTEX0_INROW)) ) + WRITE(p, "coord = float4(tex%d.x, tex%d.y, 1.0f, 1.0f);\n", texinfo.sourcerow - XF_SRCTEX0_INROW, texinfo.sourcerow - XF_SRCTEX0_INROW); + break; + } + + // first transformation + switch (texinfo.texgentype) { + case XF_TEXGEN_EMBOSS_MAP: // calculate tex coords into bump map + + if (components & (VB_HAS_NRM1|VB_HAS_NRM2)) { + // transform the light dir into tangent space + WRITE(p, "ldir = normalize("I_LIGHTS".lights[%d].pos.xyz - pos.xyz);\n", texinfo.embosslightshift); + WRITE(p, "o.tex%d.xyz = o.tex%d.xyz + float3(dot(ldir, _norm1), dot(ldir, _norm2), 0.0f);\n", i, texinfo.embosssourceshift); + } + else + { + _assert_(0); // should have normals + WRITE(p, "o.tex%d.xyz = o.tex%d.xyz;\n", i, texinfo.embosssourceshift); + } + + break; + case XF_TEXGEN_COLOR_STRGBC0: + _assert_(texinfo.sourcerow == XF_SRCCOLORS_INROW); + WRITE(p, "o.tex%d.xyz = float3(o.colors_0.x, o.colors_0.y, 1);\n", i); + break; + case XF_TEXGEN_COLOR_STRGBC1: + _assert_(texinfo.sourcerow == XF_SRCCOLORS_INROW); + WRITE(p, "o.tex%d.xyz = float3(o.colors_1.x, o.colors_1.y, 1);\n", i); + break; + case XF_TEXGEN_REGULAR: + default: + if (components & (VB_HAS_TEXMTXIDX0< Date: Thu, 29 Sep 2011 23:32:05 +0200 Subject: Clean up various things. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 19 +++++++++++-------- Source/Core/VideoCommon/Src/PixelShaderGen.h | 17 +++++------------ Source/Core/VideoCommon/Src/VertexShaderGen.cpp | 4 ++-- Source/Core/VideoCommon/Src/VertexShaderGen.h | 3 --- 4 files changed, 18 insertions(+), 25 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index f52c6b4190..1726855d54 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -27,8 +27,6 @@ #include "VideoConfig.h" #include "NativeVertexFormat.h" -PIXELSHADERUID last_pixel_shader_uid; - static int AlphaPreTest(); static void StageHash(int stage, u32* out) @@ -104,8 +102,9 @@ static void StageHash(int stage, u32* out) // a unique identifier, basically containing all the bits. Yup, it's a lot .... // It would likely be a lot more efficient to build this incrementally as the attributes // are set... -void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) +void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode, u32 components) { + memset(uid->values, 0, sizeof(uid->values)); uid->values[0] |= bpmem.genMode.numtevstages; // 4 uid->values[0] |= bpmem.genMode.numtexgens << 4; // 4 uid->values[0] |= dstAlphaMode << 8; // 2 @@ -192,13 +191,17 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode) ++ptr; if (enablePL) + { ptr += GetLightingShaderId(ptr); + *ptr++ = components; + } uid->num_values = ptr - uid->values; } -void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) +void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode, u32 components) { + memset(uid->values, 0, sizeof(uid->values)); u32* ptr = uid->values; *ptr++ = dstAlphaMode; // 0 *ptr++ = bpmem.genMode.hex; // 1 @@ -211,11 +214,11 @@ void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode) if (g_ActiveConfig.bEnablePixelLighting && g_ActiveConfig.backend_info.bSupportsPixelLighting) { - // TODO: Include register states for lighting shader *ptr++ = xfregs.color[0].hex; *ptr++ = xfregs.alpha[0].hex; *ptr++ = xfregs.color[1].hex; *ptr++ = xfregs.alpha[1].hex; + *ptr++ = components; } for (unsigned int i = 0; i < 8; ++i) @@ -251,7 +254,7 @@ void ValidatePixelShaderIDs(API_TYPE api, PIXELSHADERUIDSAFE old_id, const std:: return; PIXELSHADERUIDSAFE new_id; - GetSafePixelShaderId(&new_id, dstAlphaMode); + GetSafePixelShaderId(&new_id, dstAlphaMode, components); if (!(old_id == new_id)) { @@ -874,13 +877,13 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) } else if (bpmem.tevind[n].mid <= 7 && bHasTexCoord) { // s matrix - // TODO: Might become negative? + _assert_(bpmem.tevind[n].mid >= 5); int mtxidx = 2*(bpmem.tevind[n].mid-5); WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.xx;\n", n, mtxidx, texcoord, n); } else if (bpmem.tevind[n].mid <= 11 && bHasTexCoord) { // t matrix - // TODO: Might become negative? + _assert_(bpmem.tevind[n].mid >= 9); int mtxidx = 2*(bpmem.tevind[n].mid-9); WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.yy;\n", n, mtxidx, texcoord, n); } diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.h b/Source/Core/VideoCommon/Src/PixelShaderGen.h index b24b656784..31242a916e 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.h +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.h @@ -44,8 +44,8 @@ #define C_PLIGHTS (C_FOG + 3) #define C_PMATERIALS (C_PLIGHTS + 40) #define C_PENVCONST_END (C_PMATERIALS + 4) -#define PIXELSHADERUID_MAX_VALUES 69 -#define PIXELSHADERUID_MAX_VALUES_SAFE 117 +#define PIXELSHADERUID_MAX_VALUES 70 +#define PIXELSHADERUID_MAX_VALUES_SAFE 120 // DO NOT make anything in this class virtual. template @@ -53,14 +53,10 @@ class _PIXELSHADERUID { public: u32 values[safe ? PIXELSHADERUID_MAX_VALUES_SAFE : PIXELSHADERUID_MAX_VALUES]; - u16 num_values; + int num_values; _PIXELSHADERUID() { - memset(values, 0, sizeof(values)); - - if (safe) num_values = sizeof(values) / sizeof(values[0]); - else num_values = 0; } _PIXELSHADERUID(const _PIXELSHADERUID& r) @@ -119,13 +115,10 @@ enum DSTALPHA_MODE const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType, u32 components); -// TODO: Wtf, those need components as well! -.- -void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode); -void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode); +void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode, u32 components); +void GetSafePixelShaderId(PIXELSHADERUIDSAFE *uid, DSTALPHA_MODE dstAlphaMode, u32 components); // Used to make sure that our optimized pixel shader IDs don't lose any possible shader code changes void ValidatePixelShaderIDs(API_TYPE api, PIXELSHADERUIDSAFE old_id, const std::string& old_code, DSTALPHA_MODE dstAlphaMode, u32 components); -extern PIXELSHADERUID last_pixel_shader_uid; - #endif // GCOGL_PIXELSHADER_H diff --git a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp index 8400fde9a5..5076f9f044 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/VertexShaderGen.cpp @@ -26,12 +26,11 @@ #include "VertexShaderGen.h" #include "VideoConfig.h" -VERTEXSHADERUID last_vertex_shader_uid; - // Mash together all the inputs that contribute to the code of a generated vertex shader into // a unique identifier, basically containing all the bits. Yup, it's a lot .... void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components) { + memset(uid->values, 0, sizeof(uid->values)); uid->values[0] = components | (xfregs.numTexGen.numTexGens << 23) | (xfregs.numChan.numColorChans << 27) | @@ -67,6 +66,7 @@ void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components) void GetSafeVertexShaderId(VERTEXSHADERUIDSAFE *uid, u32 components) { // Just store all used registers here without caring whether we need all bits or less. + memset(uid->values, 0, sizeof(uid->values)); u32* ptr = uid->values; *ptr++ = components; *ptr++ = xfregs.numTexGen.hex; diff --git a/Source/Core/VideoCommon/Src/VertexShaderGen.h b/Source/Core/VideoCommon/Src/VertexShaderGen.h index 0680be3c86..0c522286b1 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderGen.h +++ b/Source/Core/VideoCommon/Src/VertexShaderGen.h @@ -57,7 +57,6 @@ public: _VERTEXSHADERUID() { - memset(values, 0, sizeof(values)); } _VERTEXSHADERUID(const _VERTEXSHADERUID& r) @@ -116,6 +115,4 @@ void GetSafeVertexShaderId(VERTEXSHADERUIDSAFE *uid, u32 components); // Used to make sure that our optimized vertex shader IDs don't lose any possible shader code changes void ValidateVertexShaderIDs(API_TYPE api, VERTEXSHADERUIDSAFE old_id, const std::string& old_code, u32 components); -extern VERTEXSHADERUID last_vertex_shader_uid; - #endif // GCOGL_VERTEXSHADER_H -- cgit v1.2.3 From 266cafb82d67edb05cd932bd3f94febaf3348f40 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 1 Oct 2011 02:36:03 +0200 Subject: Add a call to VertexManager::Flush() when changing TC gen settings In previous revisions of Dolphin, changing texture generation settings (via GX_SetTexCoordGen for example) did not regenerate the vertex shader and flush the vertices. Adding this flush should fix texture problems in a few game, for example in Superman: Shadow of Apokolips: Before: http://i.imgur.com/mHmfb.jpg After: http://i.imgur.com/2ThES.png --- Source/Core/VideoCommon/Src/XFStructs.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/XFStructs.cpp b/Source/Core/VideoCommon/Src/XFStructs.cpp index b4afdf3d6b..ec3975cb07 100644 --- a/Source/Core/VideoCommon/Src/XFStructs.cpp +++ b/Source/Core/VideoCommon/Src/XFStructs.cpp @@ -154,6 +154,8 @@ void XFRegWritten(int transferSize, u32 baseAddress, u32 *pData) case XFMEM_SETTEXMTXINFO+5: case XFMEM_SETTEXMTXINFO+6: case XFMEM_SETTEXMTXINFO+7: + VertexManager::Flush(); + nextAddress = XFMEM_SETTEXMTXINFO + 8; break; @@ -165,6 +167,8 @@ void XFRegWritten(int transferSize, u32 baseAddress, u32 *pData) case XFMEM_SETPOSMTXINFO+5: case XFMEM_SETPOSMTXINFO+6: case XFMEM_SETPOSMTXINFO+7: + VertexManager::Flush(); + nextAddress = XFMEM_SETPOSMTXINFO + 8; break; -- cgit v1.2.3 From 59072adc32bf779dee4879a456fcb2bdc71036fc Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 8 Oct 2011 17:33:21 +0200 Subject: Align stack variables on a 16-bytes boundary in SSSE3 functions Fixes issue 4450. Thanks to pholklore1 for his patch. --- Source/Core/VideoCommon/Src/VertexLoader_Position.cpp | 4 ++-- Source/Core/VideoCommon/Src/VertexLoader_TextCoord.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/VertexLoader_Position.cpp b/Source/Core/VideoCommon/Src/VertexLoader_Position.cpp index b8483b119c..06481f9ddf 100644 --- a/Source/Core/VideoCommon/Src/VertexLoader_Position.cpp +++ b/Source/Core/VideoCommon/Src/VertexLoader_Position.cpp @@ -179,8 +179,8 @@ void Pos_ReadIndex_Float_SSSE3(int Index) if(Index < MaxSize) { const u32* pData = (const u32 *)(cached_arraybases[ARRAY_POSITION] + (Index * arraystrides[ARRAY_POSITION])); - const __m128i a = _mm_loadu_si128((__m128i*)pData); - __m128i b = _mm_shuffle_epi8(a, three ? kMaskSwap32_3 : kMaskSwap32_2); + GC_ALIGNED128(const __m128i a = _mm_loadu_si128((__m128i*)pData)); + GC_ALIGNED128(__m128i b = _mm_shuffle_epi8(a, three ? kMaskSwap32_3 : kMaskSwap32_2)); _mm_storeu_si128((__m128i*)VertexManager::s_pCurBufferPointer, b); LOG_VTX(); VertexManager::s_pCurBufferPointer += 12; diff --git a/Source/Core/VideoCommon/Src/VertexLoader_TextCoord.cpp b/Source/Core/VideoCommon/Src/VertexLoader_TextCoord.cpp index de853081ed..ba3bb73f43 100644 --- a/Source/Core/VideoCommon/Src/VertexLoader_TextCoord.cpp +++ b/Source/Core/VideoCommon/Src/VertexLoader_TextCoord.cpp @@ -353,8 +353,8 @@ void LOADERDECL TexCoord_ReadIndex16_Float2_SSSE3() { u16 Index = DataReadU16(); const u32 *pData = (const u32 *)(cached_arraybases[ARRAY_TEXCOORD0+tcIndex] + (Index * arraystrides[ARRAY_TEXCOORD0+tcIndex])); - const __m128i a = _mm_loadl_epi64((__m128i*)pData); - const __m128i b = _mm_shuffle_epi8(a, kMaskSwap32); + GC_ALIGNED128(const __m128i a = _mm_loadl_epi64((__m128i*)pData)); + GC_ALIGNED128(const __m128i b = _mm_shuffle_epi8(a, kMaskSwap32)); u8* p = VertexManager::s_pCurBufferPointer; _mm_storel_epi64((__m128i*)p, b); LOG_TEX2(); -- cgit v1.2.3 From 5d14bb5e70dd1122c36e68ee529fcda8db67243e Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 15 Oct 2011 22:19:42 +1100 Subject: Changed the save state system to load/save only after the screen has been drawn. This should help stabilise the save states. --- Source/Core/VideoCommon/Src/PixelEngine.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 2a328de95d..3b6d1b7487 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -31,6 +31,7 @@ #include "CommandProcessor.h" #include "HW/ProcessorInterface.h" #include "DLCache.h" +#include "State.h" namespace PixelEngine { @@ -329,6 +330,8 @@ void UpdateFinishInterrupt(bool active) { ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active); interruptSetFinish = active; + if (active) + State::ProcessRequestedStates(0); } } -- cgit v1.2.3 From 852fe9c4bebb992aa91d7ba107ca15688ca87221 Mon Sep 17 00:00:00 2001 From: crudelios Date: Wed, 26 Oct 2011 01:19:10 +0100 Subject: Added proper Bounding Box support. Should fix most graphical issues with Paper Mario: TTYD and Super Paper Mario. Fixes issue 360. Since only those two games seem to require BBox support, and as per ector's suggestion, BBox is only enabled for those two games. BBoxes and Display List Caches don't get along too well, causing Paper Mario: TTYD to hang during certain effects where BBoxes are used. For now, I disabled DList Cache for the Paper Mario games, hopefully both will be compatible in the future. --- Source/Core/VideoCommon/Src/BPStructs.cpp | 44 ++++++++--------- Source/Core/VideoCommon/Src/VertexLoader.cpp | 73 +++++++++++++++------------- Source/Core/VideoCommon/Src/VideoCommon.h | 6 --- Source/Core/VideoCommon/Src/VideoConfig.cpp | 1 + Source/Core/VideoCommon/Src/VideoConfig.h | 1 + 5 files changed, 63 insertions(+), 62 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/BPStructs.cpp b/Source/Core/VideoCommon/Src/BPStructs.cpp index cee68327f5..d5909fdd95 100644 --- a/Source/Core/VideoCommon/Src/BPStructs.cpp +++ b/Source/Core/VideoCommon/Src/BPStructs.cpp @@ -257,9 +257,8 @@ void BPWritten(const BPCmd& bp) // We should be able to get away with deactivating the current bbox tracking // here. Not sure if there's a better spot to put this. // the number of lines copied is determined by the y scale * source efb height -#ifdef BBOX_SUPPORT + PixelEngine::bbox_active = false; -#endif float yScale; if (PE_copy.scale_invert) @@ -400,28 +399,29 @@ void BPWritten(const BPCmd& bp) case BPMEM_CLEARBBOX1: case BPMEM_CLEARBBOX2: { -#ifdef BBOX_SUPPORT - // which is which? these are GUESSES! - if (bp.address == BPMEM_CLEARBBOX1) { - int right = bp.newvalue >> 10; - int left = bp.newvalue & 0x3ff; + if(g_ActiveConfig.bUseBBox) + { + // which is which? these are GUESSES! + if (bp.address == BPMEM_CLEARBBOX1) { + int right = bp.newvalue >> 10; + int left = bp.newvalue & 0x3ff; - // We should only set these if bbox is calculated properly. - PixelEngine::bbox[0] = left; - PixelEngine::bbox[1] = right; - PixelEngine::bbox_active = true; - // WARN_LOG(VIDEO, "ClearBBox LR: %i, %08x - %i, %i", bp.address, bp.newvalue, left, right); - } else { - int bottom = bp.newvalue >> 10; - int top = bp.newvalue & 0x3ff; - - // We should only set these if bbox is calculated properly. - PixelEngine::bbox[2] = top; - PixelEngine::bbox[3] = bottom; - PixelEngine::bbox_active = true; - // WARN_LOG(VIDEO, "ClearBBox TB: %i, %08x - %i, %i", bp.address, bp.newvalue, top, bottom); + // We should only set these if bbox is calculated properly. + PixelEngine::bbox[0] = left; + PixelEngine::bbox[1] = right; + PixelEngine::bbox_active = true; + // WARN_LOG(VIDEO, "ClearBBox LR: %i, %08x - %i, %i", bp.address, bp.newvalue, left, right); + } else { + int bottom = bp.newvalue >> 10; + int top = bp.newvalue & 0x3ff; + + // We should only set these if bbox is calculated properly. + PixelEngine::bbox[2] = top; + PixelEngine::bbox[3] = bottom; + PixelEngine::bbox_active = true; + // WARN_LOG(VIDEO, "ClearBBox TB: %i, %08x - %i, %i", bp.address, bp.newvalue, top, bottom); + } } -#endif } break; case BPMEM_TEXINVALIDATE: // Used, if game has manual control the Texture Cache, which we don't allow diff --git a/Source/Core/VideoCommon/Src/VertexLoader.cpp b/Source/Core/VideoCommon/Src/VertexLoader.cpp index ce9e81219d..ee0538751a 100644 --- a/Source/Core/VideoCommon/Src/VertexLoader.cpp +++ b/Source/Core/VideoCommon/Src/VertexLoader.cpp @@ -101,7 +101,7 @@ void LOADERDECL PosMtx_Write() void LOADERDECL UpdateBoundingBox() { - if (!PixelEngine::bbox_active) + if (!PixelEngine::bbox_active) return; // Truly evil hack, reading backwards from the write pointer. If we were writing to write-only @@ -119,40 +119,46 @@ void LOADERDECL UpdateBoundingBox() t[1] = p[0] * world_matrix[4] + p[1] * world_matrix[5] + p[2] * world_matrix[6] + world_matrix[7]; t[2] = p[0] * world_matrix[8] + p[1] * world_matrix[9] + p[2] * world_matrix[10] + world_matrix[11]; - float o[4]; - o[2] = t[0] * proj_matrix[8] + t[1] * proj_matrix[9] + t[2] * proj_matrix[10] + proj_matrix[11]; - // Depth culling - if (o[2] < 0.0) { - // No pixels are likely to be drawn - don't update bounding box. - return; - } + float o[3]; o[0] = t[0] * proj_matrix[0] + t[1] * proj_matrix[1] + t[2] * proj_matrix[2] + proj_matrix[3]; o[1] = t[0] * proj_matrix[4] + t[1] * proj_matrix[5] + t[2] * proj_matrix[6] + proj_matrix[7]; - o[3] = t[0] * proj_matrix[12] + t[1] * proj_matrix[13] + t[2] * proj_matrix[14] + proj_matrix[15]; - - o[0] /= o[3]; - o[1] /= o[3]; + o[2] = t[0] * proj_matrix[12] + t[1] * proj_matrix[13] + t[2] * proj_matrix[14] + proj_matrix[15]; + + + o[0] /= o[2]; + o[1] /= o[2]; // should possibly adjust for viewport? - o[0] = (o[0] + 1.0f) * 320.0f; - o[1] = (o[1] + 1.0f) * 240.0f; - - if (o[0] < PixelEngine::bbox[0]) PixelEngine::bbox[0] = (u16)std::max(0.0f, o[0]); - if (o[0] > PixelEngine::bbox[1]) PixelEngine::bbox[1] = (u16)std::min(640.0f, o[0]); - if (o[1] < PixelEngine::bbox[2]) PixelEngine::bbox[2] = (u16)std::max(0.0f, o[1]); - if (o[1] > PixelEngine::bbox[3]) PixelEngine::bbox[3] = (u16)std::min(480.0f, o[1]); - - // Hardware tests bounding boxes in 2x2 blocks => left and top are even, right and bottom are odd - PixelEngine::bbox[0] &= ~1; - PixelEngine::bbox[1] |= 1; - PixelEngine::bbox[2] &= ~1; - PixelEngine::bbox[3] |= 1; - - /* - if (GetAsyncKeyState(VK_LSHIFT)) { - ERROR_LOG(VIDEO, "XForm: %f %f %f to %f %f", p[0], p[1], p[2], o[0], o[1]); - ERROR_LOG(VIDEO, "%i %i %i %i", g_VideoInitialize.pBBox[0], g_VideoInitialize.pBBox[1], g_VideoInitialize.pBBox[2], g_VideoInitialize.pBBox[3]); - }*/ + o[0] = (o[0] + 1.0f) * 304.0f; + o[1] = (1.0f - o[1]) * 240.0f; + + if (o[0] < PixelEngine::bbox[0]) + { + PixelEngine::bbox[0] = (u16) std::max(0.0f, o[0]); + + // Hardware tests bounding boxes in 2x2 blocks => left and top are even, right and bottom are odd + PixelEngine::bbox[0] &= ~1; + } + + if (o[0] > PixelEngine::bbox[1]) + { + PixelEngine::bbox[1] = (u16) std::min(608.0f, o[0]); + if(!(PixelEngine::bbox[1] & 1) && PixelEngine::bbox[1] != 0) + PixelEngine::bbox[1]--; + } + + if (o[1] < PixelEngine::bbox[2]) + { + PixelEngine::bbox[2] = (u16) std::max(0.0f, o[1]); + PixelEngine::bbox[2] &= ~1; + } + + if (o[1] > PixelEngine::bbox[3]) + { + PixelEngine::bbox[3] = (u16) std::min(480.0f, o[1]); + if(!(PixelEngine::bbox[3] & 1) && PixelEngine::bbox[3] != 0) + PixelEngine::bbox[3]--; + } } void LOADERDECL TexMtx_ReadDirect_UByte() @@ -290,9 +296,8 @@ void VertexLoader::CompileVertexTranslator() // OK, so we just got a point. Let's go back and read it for the bounding box. -#ifdef BBOX_SUPPORT - WriteCall(UpdateBoundingBox); -#endif + if(g_ActiveConfig.bUseBBox) + WriteCall(UpdateBoundingBox); // Normals vtx_decl.num_normals = 0; diff --git a/Source/Core/VideoCommon/Src/VideoCommon.h b/Source/Core/VideoCommon/Src/VideoCommon.h index 80c0937884..9f6dd59cd3 100644 --- a/Source/Core/VideoCommon/Src/VideoCommon.h +++ b/Source/Core/VideoCommon/Src/VideoCommon.h @@ -56,12 +56,6 @@ enum MAX_XFB_HEIGHT = 574 }; -// If this is enabled, bounding boxes will be computed for everything drawn. -// This can theoretically have a big speed hit in some geom heavy games. Needs more work. -// Helps some effects in Paper Mario (but they aren't quite right yet). -// Do testing to figure out if the speed hit is bad? -// #define BBOX_SUPPORT - // Logging // ---------- void HandleGLError(); diff --git a/Source/Core/VideoCommon/Src/VideoConfig.cpp b/Source/Core/VideoCommon/Src/VideoConfig.cpp index 2c7788c9cc..b6ba76f45e 100644 --- a/Source/Core/VideoCommon/Src/VideoConfig.cpp +++ b/Source/Core/VideoCommon/Src/VideoConfig.cpp @@ -172,6 +172,7 @@ void VideoConfig::GameIniLoad(const char *ini_file) iniFile.GetIfExists("Video", "PH_ZNear", &sPhackvalue[0]); iniFile.GetIfExists("Video", "PH_ZFar", &sPhackvalue[1]); iniFile.GetIfExists("Video", "ZTPSpeedupHack", &bZTPSpeedHack); + iniFile.GetIfExists("Video", "UseBBox", &bUseBBox); } void VideoConfig::VerifyValidity() diff --git a/Source/Core/VideoCommon/Src/VideoConfig.h b/Source/Core/VideoCommon/Src/VideoConfig.h index 83ec08784d..3bd833cc73 100644 --- a/Source/Core/VideoCommon/Src/VideoConfig.h +++ b/Source/Core/VideoCommon/Src/VideoConfig.h @@ -135,6 +135,7 @@ struct VideoConfig std::string sPhackvalue[2]; float fAspectRatioHackW, fAspectRatioHackH; bool bZTPSpeedHack; // The Legend of Zelda: Twilight Princess + bool bUseBBox; bool bEnablePixelLighting; bool bEnablePerPixelDepth; -- cgit v1.2.3 From dd551814c9c160c1dd33ed0f29e722e73fb43eca Mon Sep 17 00:00:00 2001 From: crudelios Date: Fri, 28 Oct 2011 21:12:12 +0100 Subject: Bounding Box bugfixes. - Fixes all (I hope) BBox-related unknown pointer crashes. - Fixes wrong BBox values with Frame Skip on (and the resulting unknown pointer crashes). - Fixes a small oversight on the change I made to the ISO Properties dialog. This should also be a (very very little) bit faster than the previous version. --- Source/Core/VideoCommon/Src/BPStructs.cpp | 10 ++++-- Source/Core/VideoCommon/Src/PixelEngine.cpp | 47 +++++++++++++++++++++++----- Source/Core/VideoCommon/Src/VertexLoader.cpp | 39 +++++------------------ 3 files changed, 54 insertions(+), 42 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/BPStructs.cpp b/Source/Core/VideoCommon/Src/BPStructs.cpp index d5909fdd95..d06f1de8b9 100644 --- a/Source/Core/VideoCommon/Src/BPStructs.cpp +++ b/Source/Core/VideoCommon/Src/BPStructs.cpp @@ -37,6 +37,8 @@ u32 mapTexAddress; bool mapTexFound; int numWrites; +extern volatile bool g_bSkipCurrentFrame; + static const float s_gammaLUT[] = { 1.0f, @@ -401,7 +403,11 @@ void BPWritten(const BPCmd& bp) { if(g_ActiveConfig.bUseBBox) { - // which is which? these are GUESSES! + // Don't compute bounding box if this frame is being skipped! + // Wrong but valid values are better than bogus values... + if(g_bSkipCurrentFrame) + break; + if (bp.address == BPMEM_CLEARBBOX1) { int right = bp.newvalue >> 10; int left = bp.newvalue & 0x3ff; @@ -410,7 +416,6 @@ void BPWritten(const BPCmd& bp) PixelEngine::bbox[0] = left; PixelEngine::bbox[1] = right; PixelEngine::bbox_active = true; - // WARN_LOG(VIDEO, "ClearBBox LR: %i, %08x - %i, %i", bp.address, bp.newvalue, left, right); } else { int bottom = bp.newvalue >> 10; int top = bp.newvalue & 0x3ff; @@ -419,7 +424,6 @@ void BPWritten(const BPCmd& bp) PixelEngine::bbox[2] = top; PixelEngine::bbox[3] = bottom; PixelEngine::bbox_active = true; - // WARN_LOG(VIDEO, "ClearBBox TB: %i, %08x - %i, %i", bp.address, bp.newvalue, top, bottom); } } } diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 3b6d1b7487..90f1546ebd 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -206,14 +206,45 @@ void Read16(u16& _uReturnValue, const u32 _iAddress) INFO_LOG(PIXELENGINE, "(r16) TOKEN_REG : %04x", _uReturnValue); break; - // The return values for these BBOX registers need to be gotten from the bounding box of the object. - // See http://code.google.com/p/dolphin-emu/issues/detail?id=360#c74 for more details. - - // 0x80, 0xa0, 0x80, 0xa0 makes Paper Mario happy. - case PE_BBOX_LEFT: _uReturnValue = bbox[0]; INFO_LOG(PIXELENGINE, "R: BBOX_LEFT = %i", bbox[0]); bbox_active = false; break; - case PE_BBOX_RIGHT: _uReturnValue = bbox[1]; INFO_LOG(PIXELENGINE, "R: BBOX_RIGHT = %i", bbox[1]); bbox_active = false; break; - case PE_BBOX_TOP: _uReturnValue = bbox[2]; INFO_LOG(PIXELENGINE, "R: BBOX_TOP = %i", bbox[2]); bbox_active = false; break; - case PE_BBOX_BOTTOM: _uReturnValue = bbox[3]; INFO_LOG(PIXELENGINE, "R: BBOX_BOTTOM = %i", bbox[3]); bbox_active = false; break; + case PE_BBOX_LEFT: + { + // Left must be even and 606px max + _uReturnValue = std::min((u16) 606, bbox[0]) & ~1; + + INFO_LOG(PIXELENGINE, "R: BBOX_LEFT = %i", _uReturnValue); + bbox_active = false; + break; + } + + case PE_BBOX_RIGHT: + { + // Right must be odd and 607px max + _uReturnValue = std::min((u16) 607, bbox[1]) | 1; + + INFO_LOG(PIXELENGINE, "R: BBOX_RIGHT = %i", _uReturnValue); + bbox_active = false; + break; + } + + case PE_BBOX_TOP: + { + // Top must be even and 478px max + _uReturnValue = std::min((u16) 478, bbox[2]) & ~1; + + INFO_LOG(PIXELENGINE, "R: BBOX_TOP = %i", _uReturnValue); + bbox_active = false; + break; + } + + case PE_BBOX_BOTTOM: + { + // Bottom must be odd and 479px max + _uReturnValue = std::min((u16) 479, bbox[3]) | 1; + + INFO_LOG(PIXELENGINE, "R: BBOX_BOTTOM = %i", _uReturnValue); + bbox_active = false; + break; + } case PE_PERF_0L: case PE_PERF_0H: diff --git a/Source/Core/VideoCommon/Src/VertexLoader.cpp b/Source/Core/VideoCommon/Src/VertexLoader.cpp index ee0538751a..fb34ce1bc1 100644 --- a/Source/Core/VideoCommon/Src/VertexLoader.cpp +++ b/Source/Core/VideoCommon/Src/VertexLoader.cpp @@ -111,7 +111,7 @@ void LOADERDECL UpdateBoundingBox() // Then convert to screen space and update the bounding box. float p[3] = {data[0], data[1], data[2]}; - const float *world_matrix = (float*)xfmem + MatrixIndexA.PosNormalMtxIdx * 4; + const float *world_matrix = (float*)xfmem + MatrixIndexA.PosNormalMtxIdx * 4; const float *proj_matrix = &g_fProjectionMatrix[0]; float t[3]; @@ -124,41 +124,18 @@ void LOADERDECL UpdateBoundingBox() o[1] = t[0] * proj_matrix[4] + t[1] * proj_matrix[5] + t[2] * proj_matrix[6] + proj_matrix[7]; o[2] = t[0] * proj_matrix[12] + t[1] * proj_matrix[13] + t[2] * proj_matrix[14] + proj_matrix[15]; - o[0] /= o[2]; o[1] /= o[2]; - // should possibly adjust for viewport? + // Max width seems to be 608, while max height is 480 + // Here height is set to 484 as BBox bottom always seems to be off by a few pixels o[0] = (o[0] + 1.0f) * 304.0f; - o[1] = (1.0f - o[1]) * 240.0f; - - if (o[0] < PixelEngine::bbox[0]) - { - PixelEngine::bbox[0] = (u16) std::max(0.0f, o[0]); + o[1] = (1.0f - o[1]) * 242.0f; - // Hardware tests bounding boxes in 2x2 blocks => left and top are even, right and bottom are odd - PixelEngine::bbox[0] &= ~1; - } - - if (o[0] > PixelEngine::bbox[1]) - { - PixelEngine::bbox[1] = (u16) std::min(608.0f, o[0]); - if(!(PixelEngine::bbox[1] & 1) && PixelEngine::bbox[1] != 0) - PixelEngine::bbox[1]--; - } - - if (o[1] < PixelEngine::bbox[2]) - { - PixelEngine::bbox[2] = (u16) std::max(0.0f, o[1]); - PixelEngine::bbox[2] &= ~1; - } - - if (o[1] > PixelEngine::bbox[3]) - { - PixelEngine::bbox[3] = (u16) std::min(480.0f, o[1]); - if(!(PixelEngine::bbox[3] & 1) && PixelEngine::bbox[3] != 0) - PixelEngine::bbox[3]--; - } + if (o[0] < PixelEngine::bbox[0]) PixelEngine::bbox[0] = (u16) std::max(0.0f, o[0]); + if (o[0] > PixelEngine::bbox[1]) PixelEngine::bbox[1] = (u16) o[0]; + if (o[1] < PixelEngine::bbox[2]) PixelEngine::bbox[2] = (u16) std::max(0.0f, o[1]); + if (o[1] > PixelEngine::bbox[3]) PixelEngine::bbox[3] = (u16) o[1]; } void LOADERDECL TexMtx_ReadDirect_UByte() -- cgit v1.2.3 From 8f31968466fb3cbb1ca033cf3baf5ba843640f6f Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Tue, 1 Nov 2011 01:37:54 +0100 Subject: Revert "Merge branch 'zcomploc-support'" This reverts commit 9dad9ebe899b4f2c36923c9721a09ff45b4e573c, reversing changes made to e76bc71efe8c3c724e14a2f741fe4f34669d1bea. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 27 +++++++++----------------- 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index f9aa7fd551..1726855d54 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -143,6 +143,8 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode, u32 compo if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) indirectStagesUsed |= (1 << bpmem.tevind[i].bt); + assert(indirectStagesUsed == (indirectStagesUsed & 0xF)); + uid->values[1] |= indirectStagesUsed << 5; // 4; for (unsigned int i = 0; i < bpmem.genMode.numindstages; ++i) @@ -169,10 +171,10 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode, u32 compo if (alphaPreTest == 0 || alphaPreTest == 2) { ptr[0] |= bpmem.fog.c_proj_fsel.fsel << 8; // 3 - ptr[0] |= bpmem.zcontrol.zcomploc << 11; // 1 if (DepthTextureEnable) { - ptr[0] |= bpmem.ztex2.op << 12; // 2 + ptr[0] |= bpmem.ztex2.op << 11; // 2 + ptr[0] |= bpmem.zcontrol.zcomploc << 13; // 1 ptr[0] |= bpmem.zmode.testenable << 14; // 1 ptr[0] |= bpmem.zmode.updateenable << 15; // 1 } @@ -1179,21 +1181,10 @@ static bool WriteAlphaTest(char *&p, API_TYPE ApiType,DSTALPHA_MODE dstAlphaMode compindex = bpmem.alphaFunc.comp1 % 8; WRITE(p, tevAlphaFuncsTable[compindex],alphaRef[1]);//lookup the second component from the alpha function table - WRITE(p, ")) {\n"); - - WRITE(p, "ocol0 = 0;\n"); - if (dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND) - WRITE(p, "ocol1 = 0;\n"); - if (DepthTextureEnable) - WRITE(p, "depth = 1.f;\n"); - if (!bpmem.zcontrol.zcomploc) - { - WRITE(p, "discard;\n"); - if (ApiType != API_D3D11) - WRITE(p, "return;\n"); - } - - WRITE(p, "}\n"); + WRITE(p, ")){ocol0 = 0;%s%s discard;%s}\n", + dstAlphaMode == DSTALPHA_DUAL_SOURCE_BLEND ? "ocol1 = 0;" : "", + DepthTextureEnable ? "depth = 1.f;" : "", + (ApiType != API_D3D11) ? "return;" : ""); return true; } @@ -1251,4 +1242,4 @@ static void WriteFog(char *&p) WRITE(p, " prev.rgb = lerp(prev.rgb,"I_FOG"[0].rgb,fog);\n"); -} +} \ No newline at end of file -- cgit v1.2.3 From e5d051a4e977443785f4e78cb6464eca0408e578 Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Tue, 22 Nov 2011 19:24:05 -0600 Subject: Update linux libav frame dump code for recent api changes. --- Source/Core/VideoCommon/Src/AVIDump.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/AVIDump.cpp b/Source/Core/VideoCommon/Src/AVIDump.cpp index a5a6098489..469e00d9f5 100644 --- a/Source/Core/VideoCommon/Src/AVIDump.cpp +++ b/Source/Core/VideoCommon/Src/AVIDump.cpp @@ -267,8 +267,6 @@ bool AVIDump::CreateFile() s_Stream->codec->gop_size = 12; s_Stream->codec->pix_fmt = g_Config.bUseFFV1 ? PIX_FMT_BGRA : PIX_FMT_YUV420P; - av_set_parameters(s_FormatContext, NULL); - if (!(codec = avcodec_find_encoder(s_Stream->codec->codec_id)) || (avcodec_open(s_Stream->codec, codec) < 0)) { @@ -287,14 +285,14 @@ bool AVIDump::CreateFile() s_OutBuffer = new uint8_t[s_size]; NOTICE_LOG(VIDEO, "Opening file %s for dumping", s_FormatContext->filename); - if (url_fopen(&s_FormatContext->pb, s_FormatContext->filename, URL_WRONLY) < 0) + if (avio_open(&s_FormatContext->pb, s_FormatContext->filename, AVIO_FLAG_WRITE) < 0) { WARN_LOG(VIDEO, "Could not open %s", s_FormatContext->filename); CloseFile(); return false; } - av_write_header(s_FormatContext); + avformat_write_header(s_FormatContext, NULL); return true; } @@ -374,7 +372,7 @@ void AVIDump::CloseFile() if (s_FormatContext) { if (s_FormatContext->pb) - url_fclose(s_FormatContext->pb); + avio_close(s_FormatContext->pb); av_free(s_FormatContext); s_FormatContext = NULL; } -- cgit v1.2.3 From bb182d88b7f7b8f577709bd34811f474237e4355 Mon Sep 17 00:00:00 2001 From: Maarten ter Huurne Date: Mon, 5 Dec 2011 05:26:28 +0100 Subject: Link against systemwide OpenCL on OS X. --- Source/Core/VideoCommon/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 481ced250a..d0e28f571c 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -40,6 +40,10 @@ set(SRCS Src/BPFunctions.cpp Src/memcpy_amd.cpp) set(LIBS core) +if(NOT ${CL} STREQUAL CL-NOTFOUND) + list(APPEND LIBS ${CL}) +endif() + if(wxWidgets_FOUND AND WIN32) set(SRCS ${SRCS} Src/EmuWindow.cpp) endif() -- cgit v1.2.3 From 3d9c35f58e84aa2c1b69bf4dfe381a9b526a3dd2 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sat, 10 Dec 2011 16:08:26 +0100 Subject: VideoCommon: Fix upper and lower depth bytes being switched when performing Z16L EFB copies (EFB to texture only). Fixes issue 4989. Fixes issue 5056. --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index d3b7ca14ba..3550227389 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -429,7 +429,7 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat float colmat[28] = {0}; float *const fConstAdd = colmat + 16; float *const ColorMask = colmat + 20; - ColorMask[0] = ColorMask[1] = ColorMask[2] = ColorMask[3] = 255.0f; + ColorMask[0] = ColorMask[1] = ColorMask[2] = ColorMask[3] = 255.0f; ColorMask[4] = ColorMask[5] = ColorMask[6] = ColorMask[7] = 1.0f / 255.0f; unsigned int cbufid = -1; @@ -437,7 +437,7 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat { switch (dstFormat) { - case 0: // Z4 + case 0: // Z4 colmat[3] = colmat[7] = colmat[11] = colmat[15] = 1.0f; cbufid = 0; break; @@ -447,7 +447,7 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat cbufid = 1; break; - case 3: // Z16 + case 3: // Z16 colmat[1] = colmat[5] = colmat[9] = colmat[12] = 1.0f; cbufid = 24; break; @@ -472,8 +472,10 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat cbufid = 5; break; - case 12: // Z16L - colmat[2] = colmat[6] = colmat[10] = colmat[13] = 1.0f; + case 12: // Z16L - copy lower 16 depth bits + // expected to be used as an IA8 texture (upper 8 bits stored as intensity, lower 8 bits stored as alpha) + // Used e.g. in Zelda: Skyward Sword + colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1.0f; cbufid = 6; break; @@ -483,7 +485,6 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat cbufid = 7; break; } - } else if (isIntensity) { -- cgit v1.2.3 From 3e773f093df179370faff6021a5c9a85af6181f7 Mon Sep 17 00:00:00 2001 From: nitsuja Date: Mon, 19 Dec 2011 15:13:26 -0800 Subject: fixed a freeze on emu shutdown in windows build --- Source/Core/VideoCommon/Src/EmuWindow.cpp | 30 ++++++++++++++++++++++++++++++ Source/Core/VideoCommon/Src/EmuWindow.h | 1 + 2 files changed, 31 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/EmuWindow.cpp b/Source/Core/VideoCommon/Src/EmuWindow.cpp index ff96887a32..30dca24cb1 100644 --- a/Source/Core/VideoCommon/Src/EmuWindow.cpp +++ b/Source/Core/VideoCommon/Src/EmuWindow.cpp @@ -36,6 +36,9 @@ WNDCLASSEX wndClass; const TCHAR m_szClassName[] = _T("DolphinEmuWnd"); int g_winstyle; static volatile bool s_sizing; +static const int TITLE_TEXT_BUF_SIZE = 1024; +TCHAR m_titleTextBuffer[TITLE_TEXT_BUF_SIZE]; +static const int WM_SETTEXT_CUSTOM = WM_USER + WM_SETTEXT; bool IsSizing() { @@ -225,6 +228,10 @@ LRESULT CALLBACK WndProc( HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam ) PostMessage(m_hParent, WM_USER, WM_USER_SETCURSOR, 0); return true; + case WM_SETTEXT_CUSTOM: + SendMessage(hWnd, WM_SETTEXT, wParam, lParam); + break; + default: return DefWindowProc(hWnd, iMsg, wParam, lParam); } @@ -357,4 +364,27 @@ void SetSize(int width, int height) MoveWindow(m_hWnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, TRUE); } +void SetWindowText(const TCHAR* text) +{ + // the simple way. + // we don't do this because it's a blocking call and the GUI thread might be waiting for us. + //::SetWindowText(m_hWnd, text); + + // copy to m_titleTextBuffer in such a way that + // it remains null-terminated and without garbage data at every point in time, + // in case another thread reads it while we're doing this. + for (int i = 0; i < TITLE_TEXT_BUF_SIZE-1; ++i) + { + m_titleTextBuffer[i+1] = 0; + TCHAR c = text[i]; + m_titleTextBuffer[i] = c; + if (!c) + break; + } + + // the OS doesn't allow posting WM_SETTEXT, + // so we post our own message and convert it to that in WndProc + PostMessage(m_hWnd, WM_SETTEXT_CUSTOM, 0, (LPARAM)m_titleTextBuffer); +} + } diff --git a/Source/Core/VideoCommon/Src/EmuWindow.h b/Source/Core/VideoCommon/Src/EmuWindow.h index 1403ffcf57..bf5aa73cdf 100644 --- a/Source/Core/VideoCommon/Src/EmuWindow.h +++ b/Source/Core/VideoCommon/Src/EmuWindow.h @@ -14,6 +14,7 @@ void Close(); void SetSize(int displayWidth, int displayHeight); bool IsSizing(); void OSDMenu(WPARAM wParam); +void SetWindowText(const TCHAR* text); } -- cgit v1.2.3 From 39613a95a80db27d262ad8ed4b75858c78aeed83 Mon Sep 17 00:00:00 2001 From: nitsuja Date: Mon, 2 Jan 2012 02:20:22 -0800 Subject: initialize the uninitialized --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 7 +++++++ Source/Core/VideoCommon/Src/MainBase.cpp | 12 ++++++++++++ Source/Core/VideoCommon/Src/PixelEngine.cpp | 10 ++++++++++ Source/Core/VideoCommon/Src/VertexShaderManager.cpp | 6 ++++++ Source/Core/VideoCommon/Src/VideoState.cpp | 11 +++++++++++ Source/Core/VideoCommon/Src/VideoState.h | 1 + 6 files changed, 47 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index ad05a9797e..67c8f411fa 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -109,6 +109,8 @@ void Init() m_CPCtrlReg.Hex = 0; + m_CPClearReg.Hex = 0; + m_bboxleft = 0; m_bboxtop = 0; m_bboxright = 640; @@ -130,6 +132,11 @@ void Init() interruptFinishWaiting = false; interruptTokenWaiting = false; + bProcessFifoToLoWatermark = false; + bProcessFifoAllDistance = false; + isPossibleWaitingSetDrawDone = false; + OnOverflow = false; + et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper); } diff --git a/Source/Core/VideoCommon/Src/MainBase.cpp b/Source/Core/VideoCommon/Src/MainBase.cpp index 1fc8b15bec..af21ebbb94 100644 --- a/Source/Core/VideoCommon/Src/MainBase.cpp +++ b/Source/Core/VideoCommon/Src/MainBase.cpp @@ -171,6 +171,18 @@ u32 VideoBackendHardware::Video_AccessEFB(EFBAccessType type, u32 x, u32 y, u32 static volatile u32 s_doStateRequested = false; +void VideoBackendHardware::InitializeShared() +{ + VideoCommon_Init(); + + s_swapRequested = 0; + s_efbAccessRequested = 0; + s_FifoShuttingDown = 0; + memset((void*)&s_beginFieldArgs, 0, sizeof(s_beginFieldArgs)); + memset(&s_accessEFBArgs, 0, sizeof(s_accessEFBArgs)); + s_AccessEFBResult = 0; +} + static volatile struct { unsigned char **ptr; diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 90f1546ebd..456e0fb535 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -155,6 +155,16 @@ void SetFinish_OnMainThread(u64 userdata, int cyclesLate); void Init() { m_Control.Hex = 0; + m_ZConf.Hex = 0; + m_AlphaConf.Hex = 0; + m_DstAlphaConf.Hex = 0; + m_AlphaModeConf.Hex = 0; + m_AlphaRead.Hex = 0; + + g_bSignalTokenInterrupt = false; + g_bSignalFinishInterrupt = false; + interruptSetToken = false; + interruptSetFinish = false; et_SetTokenOnMainThread = CoreTiming::RegisterEvent("SetToken", SetToken_OnMainThread); et_SetFinishOnMainThread = CoreTiming::RegisterEvent("SetFinish", SetFinish_OnMainThread); diff --git a/Source/Core/VideoCommon/Src/VertexShaderManager.cpp b/Source/Core/VideoCommon/Src/VertexShaderManager.cpp index a18829fde2..d7d25c5c85 100644 --- a/Source/Core/VideoCommon/Src/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/Src/VertexShaderManager.cpp @@ -150,6 +150,12 @@ void VertexShaderManager::Init() memset(&xfregs, 0, sizeof(xfregs)); memset(xfmem, 0, sizeof(xfmem)); ResetView(); + + // TODO: should these go inside ResetView()? + Matrix44::LoadIdentity(s_viewportCorrection); + memset(g_fProjectionMatrix, 0, sizeof(g_fProjectionMatrix)); + for (int i = 0; i < 4; ++i) + g_fProjectionMatrix[i*5] = 1.0f; } void VertexShaderManager::Shutdown() diff --git a/Source/Core/VideoCommon/Src/VideoState.cpp b/Source/Core/VideoCommon/Src/VideoState.cpp index 1a23410fcc..b1c1ed0889 100644 --- a/Source/Core/VideoCommon/Src/VideoState.cpp +++ b/Source/Core/VideoCommon/Src/VideoState.cpp @@ -60,3 +60,14 @@ void VideoCommon_RunLoop(bool enable) { EmulatorState(enable); } + +void VideoCommon_Init() +{ + memset(arraybases, 0, sizeof(arraybases)); + memset(arraystrides, 0, sizeof(arraystrides)); + memset(&MatrixIndexA, 0, sizeof(MatrixIndexA)); + memset(&MatrixIndexB, 0, sizeof(MatrixIndexB)); + memset(&g_VtxDesc, 0, sizeof(g_VtxDesc)); + memset(g_VtxAttr, 0, sizeof(g_VtxAttr)); + memset(texMem, 0, TMEM_SIZE); +} diff --git a/Source/Core/VideoCommon/Src/VideoState.h b/Source/Core/VideoCommon/Src/VideoState.h index b29cf17257..e596d9ad05 100644 --- a/Source/Core/VideoCommon/Src/VideoState.h +++ b/Source/Core/VideoCommon/Src/VideoState.h @@ -23,5 +23,6 @@ void VideoCommon_DoState(PointerWrap &p); void VideoCommon_RunLoop(bool enable); +void VideoCommon_Init(); #endif // _VIDEOSTATE_H -- cgit v1.2.3 From 2d6d73df95ff064f43260570f3f6487d55385585 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sat, 21 Jan 2012 14:58:29 +0100 Subject: [maintenance] Add a note explaining why we aren't emulating CP clear register writes. For further reference, see also r700f5eabc752. --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 13 ++++++++----- Source/Core/VideoCommon/Src/CommandProcessor.h | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index 67c8f411fa..fc85b8e39d 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -311,6 +311,7 @@ void Write16(const u16 _Value, const u32 _Address) UCPClearReg tmpCtrl(_Value); m_CPClearReg.Hex = tmpCtrl.Hex; DEBUG_LOG(COMMANDPROCESSOR,"\t write to CLEAR_REGISTER : %04x", _Value); + SetCpClearRegister(); } break; @@ -677,13 +678,15 @@ void SetCpControlRegister() } +// NOTE: The implementation of this function should be correct, but we intentionally aren't using it at the moment. +// We don't emulate proper GP timing anyway at the moment, so this code would just slow down emulation. void SetCpClearRegister() { - if (IsOnThread()) - { - if (!m_CPClearReg.ClearFifoUnderflow && m_CPClearReg.ClearFifoOverflow) - bProcessFifoToLoWatermark = true; - } +// if (IsOnThread()) +// { +// if (!m_CPClearReg.ClearFifoUnderflow && m_CPClearReg.ClearFifoOverflow) +// bProcessFifoToLoWatermark = true; +// } } } // end of namespace CommandProcessor diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.h b/Source/Core/VideoCommon/Src/CommandProcessor.h index 7fb4e5a45f..db6772d66d 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.h +++ b/Source/Core/VideoCommon/Src/CommandProcessor.h @@ -158,6 +158,7 @@ void UpdateInterruptsFromVideoBackend(u64 userdata); bool AllowIdleSkipping(); +void SetCpClearRegister(); void SetCpControlRegister(); void SetCpStatusRegister(); void SetOverflowStatusFromGatherPipe(); -- cgit v1.2.3 From c5008fe9dea7baf3368e214fe70e148d3bcca83b Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 26 Dec 2011 17:35:27 +0100 Subject: TextureCache: Renaming some variables OGL: Fix a possible bug at texture dumping OGL: Add a TODO about a possible bug --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 30 ++++++++++++------------ Source/Core/VideoCommon/Src/TextureCacheBase.h | 26 ++++++++++---------- 2 files changed, 29 insertions(+), 27 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 3550227389..b56dc171aa 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -266,7 +266,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, } if (((entry->isRenderTarget || entry->isDynamic) && hash_value == entry->hash && address == entry->addr) - || ((address == entry->addr) && (hash_value == entry->hash) && full_format == entry->format && entry->mipLevels == maxlevel)) + || ((address == entry->addr) && (hash_value == entry->hash) && full_format == entry->format && entry->num_mipmaps == maxlevel)) { entry->isDynamic = false; goto return_entry; @@ -279,8 +279,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, texture_is_dynamic = (entry->isRenderTarget || entry->isDynamic) && !g_ActiveConfig.bCopyEFBToTexture; if (!entry->isRenderTarget && - ((!entry->isDynamic && width == entry->realW && height == entry->realH && full_format == entry->format && entry->mipLevels == maxlevel) - || (entry->isDynamic && entry->realW == width && entry->realH == height))) + ((!entry->isDynamic && width == entry->native_width && height == entry->native_height && full_format == entry->format && entry->num_mipmaps == maxlevel) + || (entry->isDynamic && entry->native_width == width && entry->native_height == height))) { // reuse the texture } @@ -332,7 +332,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // Sometimes, we can get around recreating a texture if only the number of mip levels gets changes // e.g. if our texture cache entry got too many mipmap levels we can limit the number of used levels by setting the appropriate render states // Thus, we don't update this member for every Load, but just whenever the texture gets recreated - entry->mipLevels = maxlevel; + entry->num_mipmaps = maxlevel; GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); } @@ -341,11 +341,11 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, entry->format = full_format; entry->size_in_bytes = texture_size; - entry->virtualW = width; - entry->virtualH = height; + entry->native_width = nativeW; + entry->native_height = nativeH; - entry->realW = nativeW; - entry->realH = nativeH; + entry->virtual_width = width; + entry->virtual_height = height; entry->isRenderTarget = false; entry->isNonPow2 = false; @@ -633,8 +633,8 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat TCacheEntryBase *entry = textures[dstAddr]; if (entry) { - if ((entry->isRenderTarget && entry->virtualW == scaled_tex_w && entry->virtualH == scaled_tex_h) - || (entry->isDynamic && entry->realW == tex_w && entry->realH == tex_h)) + if ((entry->isRenderTarget && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h) + || (entry->isDynamic && entry->native_width == tex_w && entry->native_height == tex_h)) { texture_is_dynamic = entry->isDynamic; } @@ -660,14 +660,14 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat entry->addr = dstAddr; entry->hash = 0; - entry->realW = tex_w; - entry->realH = tex_h; + entry->native_width = tex_w; + entry->native_height = tex_h; - entry->virtualW = scaled_tex_w; - entry->virtualH = scaled_tex_h; + entry->virtual_width = scaled_tex_w; + entry->virtual_height = scaled_tex_h; entry->format = dstFormat; - entry->mipLevels = 0; + entry->num_mipmaps = 0; entry->isRenderTarget = true; entry->isNonPow2 = true; diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index 1c86e64006..f115b367b4 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -16,29 +16,31 @@ class TextureCache public: struct TCacheEntryBase { - // TODO: organize + // common members u32 addr; u32 size_in_bytes; u64 hash; - //u32 paletteHash; - u32 oldpixel; + //u32 pal_hash; u32 format; - - int frameCount; - unsigned int realW, realH; // Texture dimensions from the GameCube's point of view - unsigned int virtualW, virtualH; // Texture dimensions from OUR point of view - // Real and virtual dimensions are usually the same, but may be - // different if e.g. we use high-res textures. Then, realW,realH will - // be the dimensions of the original GameCube texture and - // virtualW,virtualH will be the dimensions of the high-res texture. + //bool is_preloaded; - unsigned int mipLevels; + unsigned int num_mipmaps; + unsigned int native_width, native_height; // Texture dimensions from the GameCube's point of view + unsigned int virtual_width, virtual_height; // Texture dimensions from OUR point of view - for hires textures or scaled EFB copies + + // EFB copies bool isRenderTarget; // copied from EFB bool isDynamic; // Used for hybrid EFB copies to enable checks for CPU modifications + + // deprecated members + u32 oldpixel; + int frameCount; bool isNonPow2; // doesn't seem to be used anywhere + + //TCacheEntryBase() //{ // // TODO: remove these -- cgit v1.2.3 From 94a8536b8cc44ab1435982695bc4a245e9f38f75 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 26 Dec 2011 18:05:01 +0100 Subject: TextureCacheBase: Simplify texture cache entry initialization --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 52 ++++++++---------------- Source/Core/VideoCommon/Src/TextureCacheBase.h | 29 +++++++++++++ 2 files changed, 46 insertions(+), 35 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index b56dc171aa..a5db110c7a 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -135,7 +135,7 @@ void TextureCache::MakeRangeDynamic(u32 start_address, u32 size) const int rangePosition = iter->second->IntersectsMemoryRange(start_address, size); if (0 == rangePosition) { - iter->second->hash = 0; + iter->second->SetHashes(TEXHASH_INVALID); } } } @@ -190,8 +190,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, const unsigned int nativeH = height; bool isPow2; - u64 hash_value = 0; - u64 texHash = 0; + u64 hash_value = TEXHASH_INVALID; + u64 texHash = TEXHASH_INVALID; u32 texID = address; u32 full_format = texformat; const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat); @@ -252,7 +252,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, } else { - hash_value = 0; + hash_value = TEXHASH_INVALID; } } else @@ -262,7 +262,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, } else if ((entry->isRenderTarget || entry->isDynamic) && g_ActiveConfig.bCopyEFBToTexture) { - hash_value = 0; + hash_value = TEXHASH_INVALID; } if (((entry->isRenderTarget || entry->isDynamic) && hash_value == entry->hash && address == entry->addr) @@ -332,24 +332,15 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // Sometimes, we can get around recreating a texture if only the number of mip levels gets changes // e.g. if our texture cache entry got too many mipmap levels we can limit the number of used levels by setting the appropriate render states // Thus, we don't update this member for every Load, but just whenever the texture gets recreated - entry->num_mipmaps = maxlevel; + entry->num_mipmaps = maxlevel; // TODO: Does this actually work? We can't really adjust mipmap settings per-stage... GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); } - entry->addr = address; - entry->format = full_format; - entry->size_in_bytes = texture_size; - - entry->native_width = nativeW; - entry->native_height = nativeH; - - entry->virtual_width = width; - entry->virtual_height = height; - - entry->isRenderTarget = false; + entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps); + entry->SetDimensions(nativeW, nativeH, width, height); + entry->SetEFBCopyParameters(false, texture_is_dynamic); entry->isNonPow2 = false; - entry->isDynamic = texture_is_dynamic; entry->oldpixel = *(u32*)ptr; @@ -472,9 +463,9 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat cbufid = 5; break; - case 12: // Z16L - copy lower 16 depth bits - // expected to be used as an IA8 texture (upper 8 bits stored as intensity, lower 8 bits stored as alpha) - // Used e.g. in Zelda: Skyward Sword + case 12: // Z16L - copy lower 16 depth bits + // expected to be used as an IA8 texture (upper 8 bits stored as intensity, lower 8 bits stored as alpha) + // Used e.g. in Zelda: Skyward Sword colmat[1] = colmat[5] = colmat[9] = colmat[14] = 1.0f; cbufid = 6; break; @@ -657,21 +648,12 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat // create the texture textures[dstAddr] = entry = g_texture_cache->CreateRenderTargetTexture(scaled_tex_w, scaled_tex_h); - entry->addr = dstAddr; - entry->hash = 0; - - entry->native_width = tex_w; - entry->native_height = tex_h; - - entry->virtual_width = scaled_tex_w; - entry->virtual_height = scaled_tex_h; - - entry->format = dstFormat; - entry->num_mipmaps = 0; - - entry->isRenderTarget = true; + // TODO: Using the wrong dstFormat, dumb... + entry->SetGeneralParameters(dstAddr, 0, dstFormat, 0); + entry->SetDimensions(tex_w, tex_h, scaled_tex_w, scaled_tex_h); + entry->SetEFBCopyParameters(true, false); + entry->SetHashes(TEXHASH_INVALID); entry->isNonPow2 = true; - entry->isDynamic = false; } entry->frameCount = frameCount; diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index f115b367b4..5ef0c3c464 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -16,6 +16,8 @@ class TextureCache public: struct TCacheEntryBase { +#define TEXHASH_INVALID 0 + // common members u32 addr; u32 size_in_bytes; @@ -29,6 +31,33 @@ public: unsigned int native_width, native_height; // Texture dimensions from the GameCube's point of view unsigned int virtual_width, virtual_height; // Texture dimensions from OUR point of view - for hires textures or scaled EFB copies + void SetGeneralParameters(u32 addr, u32 size, u32 format, unsigned int num_mipmaps) + { + this->addr = addr; + this->size_in_bytes = size; + this->format = format; + this->num_mipmaps = num_mipmaps; + } + + void SetDimensions(unsigned int native_width, unsigned int native_height, unsigned int virtual_width, unsigned int virtual_height) + { + this->native_width = native_width; + this->native_height = native_height; + this->virtual_width = virtual_width; + this->virtual_height = virtual_height; + } + + void SetHashes(u64 hash/*, u32 pal_hash*/) + { + this->hash = hash; + //this->pal_hash = pal_hash; + } + + void SetEFBCopyParameters(bool is_efb_copy, bool is_dynamic) + { + isRenderTarget = is_efb_copy; + isDynamic = is_dynamic; + } // EFB copies bool isRenderTarget; // copied from EFB -- cgit v1.2.3 From 9c39952c3415e1592098b46c801d668b3049fa83 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 26 Dec 2011 18:33:05 +0100 Subject: TextureCacheBase: Kill deprecated entry member isNonPow2 TextureCacheBase: Add a TODO about a potential bug --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 4 +-- Source/Core/VideoCommon/Src/TextureCacheBase.h | 38 +++++++----------------- 2 files changed, 12 insertions(+), 30 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index a5db110c7a..8bca9e36d9 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -93,7 +93,7 @@ void TextureCache::Cleanup() tcend = textures.end(); while (iter != tcend) { - if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second->frameCount) + if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second->frameCount) // TODO: Deleting EFB copies might not be a good idea here... { delete iter->second; textures.erase(iter++); @@ -340,7 +340,6 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps); entry->SetDimensions(nativeW, nativeH, width, height); entry->SetEFBCopyParameters(false, texture_is_dynamic); - entry->isNonPow2 = false; entry->oldpixel = *(u32*)ptr; @@ -653,7 +652,6 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat entry->SetDimensions(tex_w, tex_h, scaled_tex_w, scaled_tex_h); entry->SetEFBCopyParameters(true, false); entry->SetHashes(TEXHASH_INVALID); - entry->isNonPow2 = true; } entry->frameCount = frameCount; diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index 5ef0c3c464..5625958b76 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -31,6 +31,17 @@ public: unsigned int native_width, native_height; // Texture dimensions from the GameCube's point of view unsigned int virtual_width, virtual_height; // Texture dimensions from OUR point of view - for hires textures or scaled EFB copies + // EFB copies + bool isRenderTarget; // copied from EFB + bool isDynamic; // Used for hybrid EFB copies to enable checks for CPU modifications + + // used to delete textures which haven't been used for TEXTURE_KILL_THRESHOLD frames + int frameCount; + + // deprecated members + u32 oldpixel; + + void SetGeneralParameters(u32 addr, u32 size, u32 format, unsigned int num_mipmaps) { this->addr = addr; @@ -59,33 +70,6 @@ public: isDynamic = is_dynamic; } - // EFB copies - bool isRenderTarget; // copied from EFB - bool isDynamic; // Used for hybrid EFB copies to enable checks for CPU modifications - - // deprecated members - u32 oldpixel; - int frameCount; - bool isNonPow2; // doesn't seem to be used anywhere - - - - //TCacheEntryBase() - //{ - // // TODO: remove these - // isRenderTarget = 0; - // hash = 0; - // //paletteHash = 0; - // oldpixel = 0; - // addr = 0; - // size_in_bytes = 0; - // frameCount = 0; - // isNonPow2 = true; - // w = 0; - // h = 0; - // scaledW = 0; - // scaledH = 0; - //} virtual ~TCacheEntryBase(); -- cgit v1.2.3 From 8bc9e443fd5ceb7b5f4b11ccf600d6ca5f332427 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 26 Dec 2011 18:45:22 +0100 Subject: TextureCacheBase: De-uglify entry lookup a bit --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 38 +++++++++--------------- 1 file changed, 14 insertions(+), 24 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 8bca9e36d9..e2d74e18e5 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -88,9 +88,8 @@ TextureCache::~TextureCache() void TextureCache::Cleanup() { - TexCache::iterator - iter = textures.begin(), - tcend = textures.end(); + TexCache::iterator iter = textures.begin(); + TexCache::iterator tcend = textures.end(); while (iter != tcend) { if (frameCount > TEXTURE_KILL_THRESHOLD + iter->second->frameCount) // TODO: Deleting EFB copies might not be a good idea here... @@ -236,34 +235,25 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, TCacheEntryBase *entry = textures[texID]; if (entry) { - if (!g_ActiveConfig.bSafeTextureCache) + if (g_ActiveConfig.bSafeTextureCache) + { + if (g_ActiveConfig.bCopyEFBToTexture && (entry->isRenderTarget || entry->isDynamic)) + hash_value = TEXHASH_INVALID; + } + else { - if (entry->isRenderTarget || entry->isDynamic) + if (!(entry->isRenderTarget || entry->isDynamic)) + hash_value = *(u32*)ptr; + else if (g_ActiveConfig.bCopyEFBToTexture) + hash_value = TEXHASH_INVALID; + else { - if (!g_ActiveConfig.bCopyEFBToTexture) - { hash_value = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); if (isPaletteTexture) - { - hash_value ^= GetHash64(&texMem[tlutaddr], palette_size, - g_ActiveConfig.iSafeTextureCache_ColorSamples); - } - } - else - { - hash_value = TEXHASH_INVALID; - } - } - else - { - hash_value = *(u32*)ptr; + hash_value ^= GetHash64(&texMem[tlutaddr], palette_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); } } - else if ((entry->isRenderTarget || entry->isDynamic) && g_ActiveConfig.bCopyEFBToTexture) - { - hash_value = TEXHASH_INVALID; - } if (((entry->isRenderTarget || entry->isDynamic) && hash_value == entry->hash && address == entry->addr) || ((address == entry->addr) && (hash_value == entry->hash) && full_format == entry->format && entry->num_mipmaps == maxlevel)) -- cgit v1.2.3 From dcf18fbaaf20cf1eade8c76eccd7fcd279b43957 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 26 Dec 2011 19:17:04 +0100 Subject: TextureCacheBase: Force autogenerating mipmaps if custom textures are used --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index e2d74e18e5..afe4ce8abc 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -282,7 +282,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, } } } - + if (g_ActiveConfig.bHiresTextures) { // Load Custom textures @@ -298,16 +298,14 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, { expandedWidth = width = newWidth; expandedHeight = height = newHeight; - - // TODO: shouldn't generating mips be forced on now? - // native mips with a custom texture wouldn't make sense } } if (pcfmt == PC_TEX_FMT_NONE) pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, - expandedHeight, texformat, tlutaddr, tlutfmt, g_ActiveConfig.backend_info.bUseRGBATextures); + expandedHeight, texformat, tlutaddr, tlutfmt, g_ActiveConfig.backend_info.bUseRGBATextures); + UseNativeMips = UseNativeMips && (width == nativeW && height == nativeH); // Only load native mips if their dimensions fit to our virtual texture dimensions isPow2 = !((width & (width - 1)) || (height & (height - 1))); texLevels = (isPow2 && UseNativeMips && maxlevel) ? GetPow2(std::max(width, height)) : !isPow2; @@ -330,13 +328,11 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps); entry->SetDimensions(nativeW, nativeH, width, height); entry->SetEFBCopyParameters(false, texture_is_dynamic); - entry->oldpixel = *(u32*)ptr; if (g_ActiveConfig.bSafeTextureCache || entry->isDynamic) entry->hash = hash_value; else - // don't like rand() here entry->hash = *(u32*)ptr = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF); // load texture -- cgit v1.2.3 From f68ee87e0e03b909d66a8a333297499d357aa22b Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 26 Dec 2011 19:45:13 +0100 Subject: TextureCacheBase: De-uglify texcache entry lookup even more + documentation --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 48 ++++++++++++++---------- 1 file changed, 29 insertions(+), 19 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index afe4ce8abc..1c6f8a4718 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -235,6 +235,9 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, TCacheEntryBase *entry = textures[texID]; if (entry) { + // 1. Adjust reference hash: + // safe texcache: reference hash was calculated above for normal textures. 0 for virtual EFB copies. + // unsafe texcache: 0 for virtual EFB copies. Safe hash for dynamic EFB copies. First pixel for normal textures. if (g_ActiveConfig.bSafeTextureCache) { if (g_ActiveConfig.bCopyEFBToTexture && (entry->isRenderTarget || entry->isDynamic)) @@ -255,31 +258,38 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, } } - if (((entry->isRenderTarget || entry->isDynamic) && hash_value == entry->hash && address == entry->addr) - || ((address == entry->addr) && (hash_value == entry->hash) && full_format == entry->format && entry->num_mipmaps == maxlevel)) + // 2. a) For EFB copies, only the hash and the texture address need to match + if ((entry->isRenderTarget || entry->isDynamic) && hash_value == entry->hash && address == entry->addr) { + // TODO: Print a warning if the format changes! In this case, we could reinterpret the internal texture object data to the new pixel format (similiar to what is already being done in Renderer::ReinterpretPixelFormat()) entry->isDynamic = false; goto return_entry; } + + // 2. b) For normal textures, all texture parameters need to match + if (address == entry->addr && hash_value == entry->hash && full_format == entry->format && + entry->num_mipmaps == maxlevel && entry->native_width == nativeW && entry->native_height == nativeH) + { + goto return_entry; + } + + // 3. If we reach this line, we'll have to upload the new texture data to VRAM. + // If we're lucky, the texture parameters didn't change and we can reuse the internal texture object instead of destroying and recreating it. + texture_is_dynamic = (entry->isRenderTarget || entry->isDynamic) && !g_ActiveConfig.bCopyEFBToTexture; + + // TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies? + // TODO: Actually, it should be enough if the internal texture format matches... + if (!entry->isRenderTarget && + ((!entry->isDynamic && width == entry->native_width && height == entry->native_height && full_format == entry->format && entry->num_mipmaps == maxlevel) + || (entry->isDynamic && entry->native_width == width && entry->native_height == height))) + { + // reuse the texture + } else { - // Let's reload the new texture data into the same texture, - // instead of destroying it and having to create a new one. - // Might speed up movie playback very, very slightly. - texture_is_dynamic = (entry->isRenderTarget || entry->isDynamic) && !g_ActiveConfig.bCopyEFBToTexture; - - if (!entry->isRenderTarget && - ((!entry->isDynamic && width == entry->native_width && height == entry->native_height && full_format == entry->format && entry->num_mipmaps == maxlevel) - || (entry->isDynamic && entry->native_width == width && entry->native_height == height))) - { - // reuse the texture - } - else - { - // delete the texture and make a new one - delete entry; - entry = NULL; - } + // delete the texture and make a new one + delete entry; + entry = NULL; } } -- cgit v1.2.3 From 0c1e015ec3172ecab09f0315b98aeadd1191f4a4 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 26 Dec 2011 20:05:32 +0100 Subject: TextureCacheBase: Small change --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 2 +- Source/Core/VideoCommon/Src/TextureCacheBase.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 1c6f8a4718..c29646273a 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -367,7 +367,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, expandedHeight = (currentHeight + bsh) & (~bsh); TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, texformat, tlutaddr, tlutfmt, g_ActiveConfig.backend_info.bUseRGBATextures); - entry->Load(currentWidth, currentHeight, expandedWidth, level); + entry->Load(currentWidth, currentHeight, expandedWidth, level, false); ptr += ((std::max(mipWidth, bsw) * std::max(mipHeight, bsh) * bsdepth) >> 1); mipWidth >>= 1; diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index 5625958b76..7b14d737d2 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -77,7 +77,7 @@ public: virtual bool Save(const char filename[]) = 0; virtual void Load(unsigned int width, unsigned int height, - unsigned int expanded_width, unsigned int level, bool autogen_mips = false) = 0; + unsigned int expanded_width, unsigned int level, bool autogen_mips) = 0; virtual void FromRenderTarget(u32 dstAddr, unsigned int dstFormat, unsigned int srcFormat, const EFBRectangle& srcRect, bool isIntensity, bool scaleByHalf, unsigned int cbufid, -- cgit v1.2.3 From 93dbd93a8d35e3a87ae1c918369e952be3404b59 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 26 Dec 2011 21:11:31 +0100 Subject: TextureCacheBase: More cleanup... --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 53 +++++++++++++----------- 1 file changed, 28 insertions(+), 25 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index c29646273a..926b86b29a 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -191,10 +191,10 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, u64 hash_value = TEXHASH_INVALID; u64 texHash = TEXHASH_INVALID; + u64 tlut_hash = TEXHASH_INVALID; u32 texID = address; u32 full_format = texformat; const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat); - const u32 palette_size = TexDecoder_GetPaletteSize(texformat); bool texture_is_dynamic = false; unsigned int texLevels; PC_TexFormat pcfmt = PC_TEX_FMT_NONE; @@ -204,37 +204,40 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, if (isPaletteTexture) full_format = texformat | (tlutfmt << 16); - // hires texture loading and texture dumping require accurate hashes - if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures) + if (isPaletteTexture && (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures)) { - texHash = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); + const u32 palette_size = TexDecoder_GetPaletteSize(texformat); + tlut_hash = GetHash64(&texMem[tlutaddr], palette_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); + + // NOTE: For non-paletted textures, texID is equal to the texture address. + // A paletted texture, however, may have multiple texIDs assigned though depending on the currently used tlut. + // This (changing texID depending on the tlut_hash) is a trick to get around + // an issue with Metroid Prime's fonts (it has multiple sets of fonts on each other + // stored in a single texture and uses the palette to make different characters + // visible or invisible. Thus, unless we want to recreate the textures for every drawn character, + // we must make sure that a paletted texture gets assigned multiple IDs for each tlut used. + // + // TODO: Because texID isn't always the same as the address now, CopyRenderTargetToTexture might be broken now + if (g_ActiveConfig.bSafeTextureCache) + texID ^= ((u32)tlut_hash) ^(u32)(tlut_hash >> 32); + } - if (isPaletteTexture) - { - // WARNING! texID != address now => may break CopyRenderTargetToTexture (cf. TODO up) - // tlut size can be up to 32768B (GX_TF_C14X2) but Safer == Slower. - // This trick (to change the texID depending on the TLUT addr) is a trick to get around - // an issue with metroid prime's fonts, where it has multiple sets of fonts on top of - // each other stored in a single texture, and uses the palette to make different characters - // visible or invisible. Thus, unless we want to recreate the textures for every drawn character, - // we must make sure that texture with different tluts get different IDs. - const u64 tlutHash = GetHash64(texMem + tlutaddr, palette_size, - g_ActiveConfig.iSafeTextureCache_ColorSamples); + TCacheEntryBase *entry = textures[texID]; + if (entry) + { + // hires texture loading and texture dumping require accurate hashes + if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures) + { + texHash = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); - texHash ^= tlutHash; + if (isPaletteTexture) + texHash ^= tlut_hash; if (g_ActiveConfig.bSafeTextureCache) - texID ^= ((u32)tlutHash) ^ (u32)(tlutHash >> 32); + hash_value = texHash; } - if (g_ActiveConfig.bSafeTextureCache) - hash_value = texHash; - } - - TCacheEntryBase *entry = textures[texID]; - if (entry) - { // 1. Adjust reference hash: // safe texcache: reference hash was calculated above for normal textures. 0 for virtual EFB copies. // unsafe texcache: 0 for virtual EFB copies. Safe hash for dynamic EFB copies. First pixel for normal textures. @@ -254,7 +257,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, hash_value = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); if (isPaletteTexture) - hash_value ^= GetHash64(&texMem[tlutaddr], palette_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); + hash_value ^= tlut_hash; // TODO: Ugly, this is using Safe Texture Cache parameters in nonsafe TC } } -- cgit v1.2.3 From 8c2d87f668ccc2f210bfbd379a04ced272a52d54 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 26 Dec 2011 21:37:18 +0100 Subject: TextureCacheBase: Move around stuff, add some TODOs --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 25 +++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 926b86b29a..e7df59275e 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -173,12 +173,9 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, int texformat, unsigned int tlutaddr, int tlutfmt, bool UseNativeMips, unsigned int maxlevel) { - // necessary? if (0 == address) return NULL; - u8* ptr = Memory::GetPointer(address); - // TexelSizeInNibbles(format)*width*height/16; const unsigned int bsw = TexDecoder_GetBlockWidthInTexels(texformat) - 1; const unsigned int bsh = TexDecoder_GetBlockHeightInTexels(texformat) - 1; @@ -187,20 +184,17 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, unsigned int expandedHeight = (height + bsh) & (~bsh); const unsigned int nativeW = width; const unsigned int nativeH = height; - bool isPow2; - u64 hash_value = TEXHASH_INVALID; - u64 texHash = TEXHASH_INVALID; - u64 tlut_hash = TEXHASH_INVALID; + // TODO: Force STC enabled when using custom textures or when dumping textures. There's no need for having two different texture hashes then. u32 texID = address; + u64 hash_value = TEXHASH_INVALID; // Hash assigned to texcache entry + u64 texHash = TEXHASH_INVALID; // Accurate hash used for texture dumping, hires texture lookup. Equal to hash_value with STC. + u64 tlut_hash = TEXHASH_INVALID; + u32 full_format = texformat; - const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat); - bool texture_is_dynamic = false; - unsigned int texLevels; PC_TexFormat pcfmt = PC_TEX_FMT_NONE; const bool isPaletteTexture = (texformat == GX_TF_C4 || texformat == GX_TF_C8 || texformat == GX_TF_C14X2); - if (isPaletteTexture) full_format = texformat | (tlutfmt << 16); @@ -223,6 +217,11 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, } + bool texture_is_dynamic = false; + const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat); + + u8* ptr = Memory::GetPointer(address); + TCacheEntryBase *entry = textures[texID]; if (entry) { @@ -318,6 +317,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, texformat, tlutaddr, tlutfmt, g_ActiveConfig.backend_info.bUseRGBATextures); + bool isPow2; + unsigned int texLevels; UseNativeMips = UseNativeMips && (width == nativeW && height == nativeH); // Only load native mips if their dimensions fit to our virtual texture dimensions isPow2 = !((width & (width - 1)) || (height & (height - 1))); texLevels = (isPow2 && UseNativeMips && maxlevel) ? @@ -333,6 +334,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // Sometimes, we can get around recreating a texture if only the number of mip levels gets changes // e.g. if our texture cache entry got too many mipmap levels we can limit the number of used levels by setting the appropriate render states // Thus, we don't update this member for every Load, but just whenever the texture gets recreated + // + // TODO: Won't we end up recreating textures all the time because maxlevel doesn't necessarily equal texLevels? entry->num_mipmaps = maxlevel; // TODO: Does this actually work? We can't really adjust mipmap settings per-stage... GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); -- cgit v1.2.3 From 5239ba88c99c5516b412aa3dc408a448b2bd7eba Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 26 Dec 2011 22:04:59 +0100 Subject: TextureCache: Remove unsafe texture cache --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 64 +++++------------------- Source/Core/VideoCommon/Src/TextureCacheBase.h | 3 -- Source/Core/VideoCommon/Src/VideoConfig.cpp | 12 +---- Source/Core/VideoCommon/Src/VideoConfig.h | 1 - 4 files changed, 13 insertions(+), 67 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index e7df59275e..369712bede 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -23,28 +23,19 @@ enum TextureCache *g_texture_cache; - GC_ALIGNED16(u8 *TextureCache::temp) = NULL; +GC_ALIGNED16(u8 *TextureCache::temp) = NULL; TextureCache::TexCache TextureCache::textures; bool TextureCache::DeferredInvalidate; TextureCache::TCacheEntryBase::~TCacheEntryBase() { - if (0 == addr) - return; - - if (!isRenderTarget && !g_ActiveConfig.bSafeTextureCache) - { - u32 *const ptr = (u32*)Memory::GetPointer(addr); - if (ptr && *ptr == hash) - *ptr = oldpixel; - } } TextureCache::TextureCache() { if (!temp) - temp =(u8*) AllocateAlignedMemory(TEMP_SIZE,16); + temp = (u8*)AllocateAlignedMemory(TEMP_SIZE,16); TexDecoder_SetTexFmtOverlayOptions(g_ActiveConfig.bTexFmtOverlayEnable, g_ActiveConfig.bTexFmtOverlayCenter); if(g_ActiveConfig.bHiresTextures && !g_ActiveConfig.bDumpTextures) HiresTextures::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str()); @@ -198,7 +189,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, if (isPaletteTexture) full_format = texformat | (tlutfmt << 16); - if (isPaletteTexture && (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures)) + if (isPaletteTexture) { const u32 palette_size = TexDecoder_GetPaletteSize(texformat); tlut_hash = GetHash64(&texMem[tlutaddr], palette_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); @@ -212,8 +203,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // we must make sure that a paletted texture gets assigned multiple IDs for each tlut used. // // TODO: Because texID isn't always the same as the address now, CopyRenderTargetToTexture might be broken now - if (g_ActiveConfig.bSafeTextureCache) - texID ^= ((u32)tlut_hash) ^(u32)(tlut_hash >> 32); + texID ^= ((u32)tlut_hash) ^(u32)(tlut_hash >> 32); } @@ -225,40 +215,15 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, TCacheEntryBase *entry = textures[texID]; if (entry) { - // hires texture loading and texture dumping require accurate hashes - if (g_ActiveConfig.bSafeTextureCache || g_ActiveConfig.bHiresTextures || g_ActiveConfig.bDumpTextures) - { - texHash = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); + // 1. Calculate reference hash: + // calculated from RAM texture data for normal textures. Hashes for paletted textures are modified by tlut_hash. 0 for virtual EFB copies. + hash_value = texHash = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); - if (isPaletteTexture) - texHash ^= tlut_hash; + if (isPaletteTexture) + hash_value = texHash ^= tlut_hash; - if (g_ActiveConfig.bSafeTextureCache) - hash_value = texHash; - } - - // 1. Adjust reference hash: - // safe texcache: reference hash was calculated above for normal textures. 0 for virtual EFB copies. - // unsafe texcache: 0 for virtual EFB copies. Safe hash for dynamic EFB copies. First pixel for normal textures. - if (g_ActiveConfig.bSafeTextureCache) - { - if (g_ActiveConfig.bCopyEFBToTexture && (entry->isRenderTarget || entry->isDynamic)) - hash_value = TEXHASH_INVALID; - } - else - { - if (!(entry->isRenderTarget || entry->isDynamic)) - hash_value = *(u32*)ptr; - else if (g_ActiveConfig.bCopyEFBToTexture) - hash_value = TEXHASH_INVALID; - else - { - hash_value = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); - - if (isPaletteTexture) - hash_value ^= tlut_hash; // TODO: Ugly, this is using Safe Texture Cache parameters in nonsafe TC - } - } + if (g_ActiveConfig.bCopyEFBToTexture && (entry->isRenderTarget || entry->isDynamic)) + hash_value = TEXHASH_INVALID; // 2. a) For EFB copies, only the hash and the texture address need to match if ((entry->isRenderTarget || entry->isDynamic) && hash_value == entry->hash && address == entry->addr) @@ -344,12 +309,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps); entry->SetDimensions(nativeW, nativeH, width, height); entry->SetEFBCopyParameters(false, texture_is_dynamic); - entry->oldpixel = *(u32*)ptr; - - if (g_ActiveConfig.bSafeTextureCache || entry->isDynamic) - entry->hash = hash_value; - else - entry->hash = *(u32*)ptr = (u32)(((double)rand() / RAND_MAX) * 0xFFFFFFFF); + entry->hash = hash_value; // load texture entry->Load(width, height, expandedWidth, 0, (texLevels == 0)); diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index 7b14d737d2..7bc8c01480 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -38,9 +38,6 @@ public: // used to delete textures which haven't been used for TEXTURE_KILL_THRESHOLD frames int frameCount; - // deprecated members - u32 oldpixel; - void SetGeneralParameters(u32 addr, u32 size, u32 format, unsigned int num_mipmaps) { diff --git a/Source/Core/VideoCommon/Src/VideoConfig.cpp b/Source/Core/VideoCommon/Src/VideoConfig.cpp index b6ba76f45e..5bb6e4dd99 100644 --- a/Source/Core/VideoCommon/Src/VideoConfig.cpp +++ b/Source/Core/VideoCommon/Src/VideoConfig.cpp @@ -57,12 +57,8 @@ void VideoConfig::Load(const char *ini_file) iniFile.Get("Settings", "Crop", &bCrop, false); iniFile.Get("Settings", "UseXFB", &bUseXFB, 0); iniFile.Get("Settings", "UseRealXFB", &bUseRealXFB, 0); - iniFile.Get("Settings", "UseNativeMips", &bUseNativeMips, false); - - iniFile.Get("Settings", "SafeTextureCache", &bSafeTextureCache, true); // Settings - //Safe texture cache params + iniFile.Get("Settings", "UseNativeMips", &bUseNativeMips, false); iniFile.Get("Settings", "SafeTextureCacheColorSamples", &iSafeTextureCache_ColorSamples,128); - iniFile.Get("Settings", "ShowFPS", &bShowFPS, false); // Settings iniFile.Get("Settings", "ShowInputDisplay", &bShowInputDisplay, false); iniFile.Get("Settings", "OverlayStats", &bOverlayStats, false); @@ -134,7 +130,6 @@ void VideoConfig::GameIniLoad(const char *ini_file) iniFile.GetIfExists("Video_Settings", "UseXFB", &bUseXFB); iniFile.GetIfExists("Video_Settings", "UseRealXFB", &bUseRealXFB); iniFile.GetIfExists("Video_Settings", "UseNativeMips", &bUseNativeMips); - iniFile.GetIfExists("Video_Settings", "SafeTextureCache", &bSafeTextureCache); iniFile.GetIfExists("Video_Settings", "SafeTextureCacheColorSamples", &iSafeTextureCache_ColorSamples); iniFile.GetIfExists("Video_Settings", "DLOptimize", &iCompileDLsLevel); iniFile.GetIfExists("Video_Settings", "HiresTextures", &bHiresTextures); @@ -196,11 +191,7 @@ void VideoConfig::Save(const char *ini_file) iniFile.Set("Settings", "UseXFB", bUseXFB); iniFile.Set("Settings", "UseRealXFB", bUseRealXFB); iniFile.Set("Settings", "UseNativeMips", bUseNativeMips); - - iniFile.Set("Settings", "SafeTextureCache", bSafeTextureCache); - //safe texture cache params iniFile.Set("Settings", "SafeTextureCacheColorSamples", iSafeTextureCache_ColorSamples); - iniFile.Set("Settings", "ShowFPS", bShowFPS); iniFile.Set("Settings", "ShowInputDisplay", bShowInputDisplay); iniFile.Set("Settings", "OverlayStats", bOverlayStats); @@ -279,7 +270,6 @@ void VideoConfig::GameIniSave(const char* default_ini, const char* game_ini) SET_IF_DIFFERS("Video_Settings", "UseXFB", bUseXFB); SET_IF_DIFFERS("Video_Settings", "UseRealXFB", bUseRealXFB); SET_IF_DIFFERS("Video_Settings", "UseNativeMips", bUseNativeMips); - SET_IF_DIFFERS("Video_Settings", "SafeTextureCache", bSafeTextureCache); SET_IF_DIFFERS("Video_Settings", "SafeTextureCacheColorSamples", iSafeTextureCache_ColorSamples); SET_IF_DIFFERS("Video_Settings", "DLOptimize", iCompileDLsLevel); SET_IF_DIFFERS("Video_Settings", "HiresTextures", bHiresTextures); diff --git a/Source/Core/VideoCommon/Src/VideoConfig.h b/Source/Core/VideoCommon/Src/VideoConfig.h index 3bd833cc73..7c846676bb 100644 --- a/Source/Core/VideoCommon/Src/VideoConfig.h +++ b/Source/Core/VideoCommon/Src/VideoConfig.h @@ -129,7 +129,6 @@ struct VideoConfig bool bOSDHotKey; bool bCopyEFBToTexture; bool bCopyEFBScaled; - bool bSafeTextureCache; int iSafeTextureCache_ColorSamples; int iPhackvalue[4]; std::string sPhackvalue[2]; -- cgit v1.2.3 From 67129404dd8d6944ccc5ad1ae24ece33ae5c8bba Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 26 Dec 2011 23:14:12 +0100 Subject: TextureCacheBase: Small bugfix Added documentation for hybrid EFB copy stuff --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 48 +++++++++++++++++++----- Source/Core/VideoCommon/Src/TextureCacheBase.h | 2 +- 2 files changed, 39 insertions(+), 11 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 369712bede..7c78b6f937 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -189,6 +189,10 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, if (isPaletteTexture) full_format = texformat | (tlutfmt << 16); + u8* ptr = Memory::GetPointer(address); + const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat); + + hash_value = texHash = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); if (isPaletteTexture) { const u32 palette_size = TexDecoder_GetPaletteSize(texformat); @@ -204,24 +208,15 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // // TODO: Because texID isn't always the same as the address now, CopyRenderTargetToTexture might be broken now texID ^= ((u32)tlut_hash) ^(u32)(tlut_hash >> 32); + hash_value = texHash ^= tlut_hash; } - bool texture_is_dynamic = false; - const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat); - - u8* ptr = Memory::GetPointer(address); - TCacheEntryBase *entry = textures[texID]; if (entry) { // 1. Calculate reference hash: // calculated from RAM texture data for normal textures. Hashes for paletted textures are modified by tlut_hash. 0 for virtual EFB copies. - hash_value = texHash = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); - - if (isPaletteTexture) - hash_value = texHash ^= tlut_hash; - if (g_ActiveConfig.bCopyEFBToTexture && (entry->isRenderTarget || entry->isDynamic)) hash_value = TEXHASH_INVALID; @@ -378,6 +373,39 @@ return_entry: void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, unsigned int srcFormat, const EFBRectangle& srcRect, bool isIntensity, bool scaleByHalf) { + // Emulation methods: + // - EFB to RAM: + // Encodes the requested EFB data at its native resolution to the emulated RAM using shaders. + // Load() decodes the data from there again (using TextureDecoder) if the EFB copy is being used as a texture again. + // Advantage: CPU can read data from the EFB copy and we don't lose any important updates to the texture + // Disadvantage: Encoding+decoding steps often are redundant because only some games read or modify EFB copies before using them as textures. + // - EFB to texture: + // Copies the requested EFB data to a texture object in VRAM, performing any color conversion using shaders. + // Advantage: Works for many games, since in most cases EFB copies aren't read or modified at all before being used as a texture again. + // Since we don't do any further encoding or decoding here, this method is much faster. + // It also allows enhancing the visual quality by doing scaled EFB copies. + // - hybrid EFB copies: + // 1) Whenever this function gets called, encode the requested EFB data to RAM (like EFB to RAM) + // 2a) If we haven't copied to the specified dstAddr yet, copy the requested EFB data to a texture object in VRAM as well (like EFB to texture) + // Create a texture cache entry for the render target (isRenderTarget = true, isDynamic = false) + // Store a hash of the encoded RAM data in the texcache entry. + // 2b) If we already have created a texcache entry for dstAddr (i.e. if we copied to dstAddr before) AND isDynamic is false: + // Do the same like above, but reuse the old texcache entry instead of creating a new one. + // 2c) If we already have created a texcache entry for dstAddr AND isDynamic is true (isRenderTarget will be false then) + // Only encode the texture to RAM (like EFB to RAM) and store a hash of the encoded data in the existing texcache entry. + // Do NOT copy the requested EFB data to a VRAM object. Reason: the texture is dynamic, i.e. the CPU is modifying it. Storing a VRAM copy is useless, because we'd end up deleting it and reloading the data from RAM again anyway. + // 3) If the EFB copy gets used as a texture, compare the source RAM hash with the hash you stored when encoding the EFB data to RAM. + // 3a) If the two hashes match AND isDynamic is still false, reuse the VRAM copy you created + // 3b) If the two hashes differ AND isDynamic is still false, screw your existing VRAM copy. Set isRenderTarget to false and isDynamic to true. + // Redecode the source RAM data to a VRAM object. The entry basically behaves like a normal texture now. + // 3c) If isDynamic is true, treat the EFB copy like a normal texture. + // Advantage: Neither as fast as EFB to texture nor as slow as EFB to RAM, so it's a good compromise. + // Non-dynamic EFB copies can be visually enhanced like with EFB to texture. + // Compatibility ideally is as good as with EFB to RAM. + // Disadvantage: Depends on accurate texture hashing being enabled. However, with accurate hashing you end up being as slow as EFB to RAM anyway. + // + // Disadvantage of all methods: Calling this function requires the GPU to perform a pipeline flush which stalls any further CPU processing. + float colmat[28] = {0}; float *const fConstAdd = colmat + 16; float *const ColorMask = colmat + 20; diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index 7bc8c01480..6062e5accb 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -33,7 +33,7 @@ public: // EFB copies bool isRenderTarget; // copied from EFB - bool isDynamic; // Used for hybrid EFB copies to enable checks for CPU modifications + bool isDynamic; // Used for hybrid EFB copies to enable checks for CPU modifications, see CopyFromRenderTarget for details // used to delete textures which haven't been used for TEXTURE_KILL_THRESHOLD frames int frameCount; -- cgit v1.2.3 From 3b38295cbd08c98e7654e8715a88f4456cd61de5 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Tue, 27 Dec 2011 00:05:26 +0100 Subject: TextureCacheBase: De-uglify hybrid EFB copies (documentation needs updating though) TextureCacheBase: Fixed dynamic EFB copies being set to normal textures. --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 36 +++++++++--------------- Source/Core/VideoCommon/Src/TextureCacheBase.h | 18 ++++++------ 2 files changed, 24 insertions(+), 30 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 7c78b6f937..9b4891b98e 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -157,7 +157,7 @@ void TextureCache::ClearRenderTargets() iter = textures.begin(), tcend = textures.end(); for (; iter!=tcend; ++iter) - iter->second->isRenderTarget = false; + iter->second->efbcopy_state = EC_NO_COPY; } TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, @@ -211,20 +211,18 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, hash_value = texHash ^= tlut_hash; } - bool texture_is_dynamic = false; TCacheEntryBase *entry = textures[texID]; if (entry) { // 1. Calculate reference hash: // calculated from RAM texture data for normal textures. Hashes for paletted textures are modified by tlut_hash. 0 for virtual EFB copies. - if (g_ActiveConfig.bCopyEFBToTexture && (entry->isRenderTarget || entry->isDynamic)) + if (g_ActiveConfig.bCopyEFBToTexture && entry->IsEfbCopy()) hash_value = TEXHASH_INVALID; // 2. a) For EFB copies, only the hash and the texture address need to match - if ((entry->isRenderTarget || entry->isDynamic) && hash_value == entry->hash && address == entry->addr) + if (entry->IsEfbCopy() && hash_value == entry->hash && address == entry->addr) { // TODO: Print a warning if the format changes! In this case, we could reinterpret the internal texture object data to the new pixel format (similiar to what is already being done in Renderer::ReinterpretPixelFormat()) - entry->isDynamic = false; goto return_entry; } @@ -237,13 +235,11 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // 3. If we reach this line, we'll have to upload the new texture data to VRAM. // If we're lucky, the texture parameters didn't change and we can reuse the internal texture object instead of destroying and recreating it. - texture_is_dynamic = (entry->isRenderTarget || entry->isDynamic) && !g_ActiveConfig.bCopyEFBToTexture; - + // // TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies? // TODO: Actually, it should be enough if the internal texture format matches... - if (!entry->isRenderTarget && - ((!entry->isDynamic && width == entry->native_width && height == entry->native_height && full_format == entry->format && entry->num_mipmaps == maxlevel) - || (entry->isDynamic && entry->native_width == width && entry->native_height == height))) + if ((entry->efbcopy_state == EC_NO_COPY && width == entry->native_width && height == entry->native_height && full_format == entry->format && entry->num_mipmaps == maxlevel) + || (entry->efbcopy_state == EC_VRAM_DYNAMIC && entry->native_width == width && entry->native_height == height)) { // reuse the texture } @@ -297,14 +293,16 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // // TODO: Won't we end up recreating textures all the time because maxlevel doesn't necessarily equal texLevels? entry->num_mipmaps = maxlevel; // TODO: Does this actually work? We can't really adjust mipmap settings per-stage... + entry->efbcopy_state = EC_NO_COPY; GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); } entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps); entry->SetDimensions(nativeW, nativeH, width, height); - entry->SetEFBCopyParameters(false, texture_is_dynamic); entry->hash = hash_value; + if (g_ActiveConfig.bCopyEFBToTexture) entry->efbcopy_state = EC_NO_COPY; + else if (entry->IsEfbCopy()) entry->efbcopy_state = EC_VRAM_DYNAMIC; // load texture entry->Load(width, height, expandedWidth, 0, (texLevels == 0)); @@ -608,15 +606,15 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat unsigned int scaled_tex_w = g_ActiveConfig.bCopyEFBScaled ? Renderer::EFBToScaledX(tex_w) : tex_w; unsigned int scaled_tex_h = g_ActiveConfig.bCopyEFBScaled ? Renderer::EFBToScaledY(tex_h) : tex_h; - bool texture_is_dynamic = false; TCacheEntryBase *entry = textures[dstAddr]; if (entry) { - if ((entry->isRenderTarget && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h) - || (entry->isDynamic && entry->native_width == tex_w && entry->native_height == tex_h)) + if ((entry->efbcopy_state == EC_VRAM_READY && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h) + || (entry->efbcopy_state == EC_VRAM_DYNAMIC && entry->native_width == tex_w && entry->native_height == tex_h)) { - texture_is_dynamic = entry->isDynamic; + scaled_tex_w = tex_w; + scaled_tex_h = tex_h; } else { @@ -626,12 +624,6 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat } } - if (texture_is_dynamic) - { - scaled_tex_w = tex_w; - scaled_tex_h = tex_h; - } - if (NULL == entry) { // create the texture @@ -640,8 +632,8 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat // TODO: Using the wrong dstFormat, dumb... entry->SetGeneralParameters(dstAddr, 0, dstFormat, 0); entry->SetDimensions(tex_w, tex_h, scaled_tex_w, scaled_tex_h); - entry->SetEFBCopyParameters(true, false); entry->SetHashes(TEXHASH_INVALID); + entry->efbcopy_state = EC_VRAM_READY; } entry->frameCount = frameCount; diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index 6062e5accb..88a76776df 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -14,6 +14,13 @@ class TextureCache { public: + enum EFBCopyState + { + EC_NO_COPY, + EC_VRAM_READY, + EC_VRAM_DYNAMIC, + }; + struct TCacheEntryBase { #define TEXHASH_INVALID 0 @@ -32,8 +39,9 @@ public: unsigned int virtual_width, virtual_height; // Texture dimensions from OUR point of view - for hires textures or scaled EFB copies // EFB copies - bool isRenderTarget; // copied from EFB - bool isDynamic; // Used for hybrid EFB copies to enable checks for CPU modifications, see CopyFromRenderTarget for details + enum EFBCopyState efbcopy_state; + + bool IsEfbCopy() { return efbcopy_state != EC_NO_COPY; } // used to delete textures which haven't been used for TEXTURE_KILL_THRESHOLD frames int frameCount; @@ -61,12 +69,6 @@ public: //this->pal_hash = pal_hash; } - void SetEFBCopyParameters(bool is_efb_copy, bool is_dynamic) - { - isRenderTarget = is_efb_copy; - isDynamic = is_dynamic; - } - virtual ~TCacheEntryBase(); -- cgit v1.2.3 From 9fed10fc75d63f38f97acbfcf68d4b1ee67ebb2a Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Fri, 30 Dec 2011 01:10:42 +0100 Subject: Remove SaveTexture declaration in VideoCommon since it's only actually defined in OpenGL --- Source/Core/VideoCommon/Src/ImageWrite.h | 1 - 1 file changed, 1 deletion(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/ImageWrite.h b/Source/Core/VideoCommon/Src/ImageWrite.h index 962ee5b5e7..9ea6cab859 100644 --- a/Source/Core/VideoCommon/Src/ImageWrite.h +++ b/Source/Core/VideoCommon/Src/ImageWrite.h @@ -21,7 +21,6 @@ #include "Common.h" bool SaveTGA(const char* filename, int width, int height, void* pdata); -bool SaveTexture(const char* filename, u32 textarget, u32 tex, int width, int height); bool SaveData(const char* filename, const char* pdata); #endif // _IMAGEWRITE_H -- cgit v1.2.3 From b34b6e47f20b055e3b35f28f04e99fc6de86415b Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Tue, 24 Jan 2012 15:25:01 +0100 Subject: TextureCacheBase: Remove a redundant variable --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 9b4891b98e..6f2c9ebd0c 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -176,10 +176,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, const unsigned int nativeW = width; const unsigned int nativeH = height; - // TODO: Force STC enabled when using custom textures or when dumping textures. There's no need for having two different texture hashes then. u32 texID = address; - u64 hash_value = TEXHASH_INVALID; // Hash assigned to texcache entry - u64 texHash = TEXHASH_INVALID; // Accurate hash used for texture dumping, hires texture lookup. Equal to hash_value with STC. + u64 tex_hash = TEXHASH_INVALID; // Hash assigned to texcache entry (also used to generate filenames used for texture dumping and custom texture lookup) u64 tlut_hash = TEXHASH_INVALID; u32 full_format = texformat; @@ -192,7 +190,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, u8* ptr = Memory::GetPointer(address); const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat); - hash_value = texHash = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); + tex_hash = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); if (isPaletteTexture) { const u32 palette_size = TexDecoder_GetPaletteSize(texformat); @@ -208,7 +206,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // // TODO: Because texID isn't always the same as the address now, CopyRenderTargetToTexture might be broken now texID ^= ((u32)tlut_hash) ^(u32)(tlut_hash >> 32); - hash_value = texHash ^= tlut_hash; + tex_hash ^= tlut_hash; } TCacheEntryBase *entry = textures[texID]; @@ -217,17 +215,17 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // 1. Calculate reference hash: // calculated from RAM texture data for normal textures. Hashes for paletted textures are modified by tlut_hash. 0 for virtual EFB copies. if (g_ActiveConfig.bCopyEFBToTexture && entry->IsEfbCopy()) - hash_value = TEXHASH_INVALID; + tex_hash = TEXHASH_INVALID; // 2. a) For EFB copies, only the hash and the texture address need to match - if (entry->IsEfbCopy() && hash_value == entry->hash && address == entry->addr) + if (entry->IsEfbCopy() && tex_hash == entry->hash && address == entry->addr) { // TODO: Print a warning if the format changes! In this case, we could reinterpret the internal texture object data to the new pixel format (similiar to what is already being done in Renderer::ReinterpretPixelFormat()) goto return_entry; } // 2. b) For normal textures, all texture parameters need to match - if (address == entry->addr && hash_value == entry->hash && full_format == entry->format && + if (address == entry->addr && tex_hash == entry->hash && full_format == entry->format && entry->num_mipmaps == maxlevel && entry->native_width == nativeW && entry->native_height == nativeH) { goto return_entry; @@ -259,7 +257,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, unsigned int newWidth = width; unsigned int newHeight = height; - sprintf(texPathTemp, "%s_%08x_%i", SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str(), (u32) (texHash & 0x00000000FFFFFFFFLL), texformat); + sprintf(texPathTemp, "%s_%08x_%i", SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str(), (u32) (tex_hash & 0x00000000FFFFFFFFLL), texformat); pcfmt = HiresTextures::GetHiresTex(texPathTemp, &newWidth, &newHeight, texformat, temp); if (pcfmt != PC_TEX_FMT_NONE) @@ -300,7 +298,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps); entry->SetDimensions(nativeW, nativeH, width, height); - entry->hash = hash_value; + entry->hash = tex_hash; if (g_ActiveConfig.bCopyEFBToTexture) entry->efbcopy_state = EC_NO_COPY; else if (entry->IsEfbCopy()) entry->efbcopy_state = EC_VRAM_DYNAMIC; @@ -349,7 +347,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, sprintf(szTemp, "%s/%s_%08x_%i.png", szDir.c_str(), SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str(), - (u32) (texHash & 0x00000000FFFFFFFFLL), texformat); + (u32) (tex_hash & 0x00000000FFFFFFFFLL), texformat); if (false == File::Exists(szTemp)) entry->Save(szTemp); -- cgit v1.2.3 From cf899781f980ecf7dbe373a36c78a2a302e3951d Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Tue, 24 Jan 2012 16:38:27 +0100 Subject: TextureCacheBase: Update and improve documentation for EFB copies --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 40 ++++++++++++++---------- Source/Core/VideoCommon/Src/TextureCacheBase.h | 6 ++-- 2 files changed, 26 insertions(+), 20 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 6f2c9ebd0c..0a59aeefa4 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -377,30 +377,36 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat // Disadvantage: Encoding+decoding steps often are redundant because only some games read or modify EFB copies before using them as textures. // - EFB to texture: // Copies the requested EFB data to a texture object in VRAM, performing any color conversion using shaders. - // Advantage: Works for many games, since in most cases EFB copies aren't read or modified at all before being used as a texture again. + // Advantage: Works for many games, since in most cases EFB copies aren't read or modified at all before being used as a texture again. // Since we don't do any further encoding or decoding here, this method is much faster. // It also allows enhancing the visual quality by doing scaled EFB copies. // - hybrid EFB copies: - // 1) Whenever this function gets called, encode the requested EFB data to RAM (like EFB to RAM) - // 2a) If we haven't copied to the specified dstAddr yet, copy the requested EFB data to a texture object in VRAM as well (like EFB to texture) - // Create a texture cache entry for the render target (isRenderTarget = true, isDynamic = false) - // Store a hash of the encoded RAM data in the texcache entry. - // 2b) If we already have created a texcache entry for dstAddr (i.e. if we copied to dstAddr before) AND isDynamic is false: - // Do the same like above, but reuse the old texcache entry instead of creating a new one. - // 2c) If we already have created a texcache entry for dstAddr AND isDynamic is true (isRenderTarget will be false then) - // Only encode the texture to RAM (like EFB to RAM) and store a hash of the encoded data in the existing texcache entry. - // Do NOT copy the requested EFB data to a VRAM object. Reason: the texture is dynamic, i.e. the CPU is modifying it. Storing a VRAM copy is useless, because we'd end up deleting it and reloading the data from RAM again anyway. + // 1a) Whenever this function gets called, encode the requested EFB data to RAM (like EFB to RAM) + // 1b) Set efbcopy_state to EC_VRAM_DYNAMIC for all texture cache entries in the destination address range. + // If EFB copy caching is enabled, further checks will (try to) prevent redundant EFB copies. + // 2) Check if a texture cache entry for the specified dstAddr already exists (i.e. if an EFB copy was triggered to that address before): + // 2a) Entry doesn't exist: + // - Also copy the requested EFB data to a texture object in VRAM (like EFB to texture) + // - Create a texture cache entry for the target (efbcopy_state = EC_VRAM_READY) + // - Store a hash of the encoded RAM data in the texcache entry. + // 2b) Entry exists AND efbcopy_state is EC_VRAM_READY: + // - Like case 2a, but reuse the old texcache entry instead of creating a new one. + // 2c) Entry exists AND efbcopy_state is EC_VRAM_DYNAMIC: + // - Only encode the texture to RAM (like EFB to RAM) and store a hash of the encoded data in the existing texcache entry. + // - Do NOT copy the requested EFB data to a VRAM object. Reason: the texture is dynamic, i.e. the CPU is modifying it. Storing a VRAM copy is useless, because we'd always end up deleting it and reloading the data from RAM anyway. // 3) If the EFB copy gets used as a texture, compare the source RAM hash with the hash you stored when encoding the EFB data to RAM. - // 3a) If the two hashes match AND isDynamic is still false, reuse the VRAM copy you created - // 3b) If the two hashes differ AND isDynamic is still false, screw your existing VRAM copy. Set isRenderTarget to false and isDynamic to true. + // 3a) If the two hashes match AND efbcopy_state is EC_VRAM_READY, reuse the VRAM copy you created + // 3b) If the two hashes differ AND efbcopy_state is EC_VRAM_READY, screw your existing VRAM copy. Set efbcopy_state to EC_VRAM_DYNAMIC. // Redecode the source RAM data to a VRAM object. The entry basically behaves like a normal texture now. - // 3c) If isDynamic is true, treat the EFB copy like a normal texture. - // Advantage: Neither as fast as EFB to texture nor as slow as EFB to RAM, so it's a good compromise. - // Non-dynamic EFB copies can be visually enhanced like with EFB to texture. - // Compatibility ideally is as good as with EFB to RAM. - // Disadvantage: Depends on accurate texture hashing being enabled. However, with accurate hashing you end up being as slow as EFB to RAM anyway. + // 3c) If efbcopy_state is EC_VRAM_DYNAMIC, treat the EFB copy like a normal texture. + // Advantage: Non-dynamic EFB copies can be visually enhanced like with EFB to texture. + // Compatibility is as good as EFB to RAM. + // Disadvantage: Slower than EFB to texture and often even slower than EFB to RAM. + // EFB copy cache depends on accurate texture hashing being enabled. However, with accurate hashing you end up being as slow as without a copy cache anyway. // // Disadvantage of all methods: Calling this function requires the GPU to perform a pipeline flush which stalls any further CPU processing. + // + // For historical reasons, Dolphin doesn't actually implement "pure" EFB to RAM emulation, but only EFB to texture and hybrid EFB copies. float colmat[28] = {0}; float *const fConstAdd = colmat + 16; diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index 88a76776df..59960c7fb8 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -16,9 +16,9 @@ class TextureCache public: enum EFBCopyState { - EC_NO_COPY, - EC_VRAM_READY, - EC_VRAM_DYNAMIC, + EC_NO_COPY, // regular texture + EC_VRAM_READY, // EFB copy sits in VRAM and is ready to use + EC_VRAM_DYNAMIC, // EFB copy sits in RAM and needs to be decoded before using it as a texture }; struct TCacheEntryBase -- cgit v1.2.3 From 1446fb33d515fc300c33afd535b22f441544b86e Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sun, 29 Jan 2012 20:24:23 +0100 Subject: TextureCacheBase: Replace the efbcopy_state member variable of texture cache entries with a more general "texture type" --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 32 ++++++++++++------------ Source/Core/VideoCommon/Src/TextureCacheBase.h | 18 ++++++------- 2 files changed, 24 insertions(+), 26 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 0a59aeefa4..885116ae18 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -157,7 +157,7 @@ void TextureCache::ClearRenderTargets() iter = textures.begin(), tcend = textures.end(); for (; iter!=tcend; ++iter) - iter->second->efbcopy_state = EC_NO_COPY; + iter->second->type = TCET_AUTOFETCH; } TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, @@ -236,8 +236,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // // TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies? // TODO: Actually, it should be enough if the internal texture format matches... - if ((entry->efbcopy_state == EC_NO_COPY && width == entry->native_width && height == entry->native_height && full_format == entry->format && entry->num_mipmaps == maxlevel) - || (entry->efbcopy_state == EC_VRAM_DYNAMIC && entry->native_width == width && entry->native_height == height)) + if ((entry->type == TCET_AUTOFETCH && width == entry->native_width && height == entry->native_height && full_format == entry->format && entry->num_mipmaps == maxlevel) + || (entry->type == TCET_EC_DYNAMIC && entry->native_width == width && entry->native_height == height)) { // reuse the texture } @@ -291,7 +291,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // // TODO: Won't we end up recreating textures all the time because maxlevel doesn't necessarily equal texLevels? entry->num_mipmaps = maxlevel; // TODO: Does this actually work? We can't really adjust mipmap settings per-stage... - entry->efbcopy_state = EC_NO_COPY; + entry->type = TCET_AUTOFETCH; GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); } @@ -299,8 +299,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps); entry->SetDimensions(nativeW, nativeH, width, height); entry->hash = tex_hash; - if (g_ActiveConfig.bCopyEFBToTexture) entry->efbcopy_state = EC_NO_COPY; - else if (entry->IsEfbCopy()) entry->efbcopy_state = EC_VRAM_DYNAMIC; + if (g_ActiveConfig.bCopyEFBToTexture) entry->type = TCET_AUTOFETCH; + else if (entry->IsEfbCopy()) entry->type = TCET_EC_DYNAMIC; // load texture entry->Load(width, height, expandedWidth, 0, (texLevels == 0)); @@ -382,23 +382,23 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat // It also allows enhancing the visual quality by doing scaled EFB copies. // - hybrid EFB copies: // 1a) Whenever this function gets called, encode the requested EFB data to RAM (like EFB to RAM) - // 1b) Set efbcopy_state to EC_VRAM_DYNAMIC for all texture cache entries in the destination address range. + // 1b) Set type to TCET_EC_DYNAMIC for all texture cache entries in the destination address range. // If EFB copy caching is enabled, further checks will (try to) prevent redundant EFB copies. // 2) Check if a texture cache entry for the specified dstAddr already exists (i.e. if an EFB copy was triggered to that address before): // 2a) Entry doesn't exist: // - Also copy the requested EFB data to a texture object in VRAM (like EFB to texture) - // - Create a texture cache entry for the target (efbcopy_state = EC_VRAM_READY) + // - Create a texture cache entry for the target (type = TCET_EC_VRAM) // - Store a hash of the encoded RAM data in the texcache entry. - // 2b) Entry exists AND efbcopy_state is EC_VRAM_READY: + // 2b) Entry exists AND type is TCET_EC_VRAM: // - Like case 2a, but reuse the old texcache entry instead of creating a new one. - // 2c) Entry exists AND efbcopy_state is EC_VRAM_DYNAMIC: + // 2c) Entry exists AND type is TCET_EC_DYNAMIC: // - Only encode the texture to RAM (like EFB to RAM) and store a hash of the encoded data in the existing texcache entry. // - Do NOT copy the requested EFB data to a VRAM object. Reason: the texture is dynamic, i.e. the CPU is modifying it. Storing a VRAM copy is useless, because we'd always end up deleting it and reloading the data from RAM anyway. // 3) If the EFB copy gets used as a texture, compare the source RAM hash with the hash you stored when encoding the EFB data to RAM. - // 3a) If the two hashes match AND efbcopy_state is EC_VRAM_READY, reuse the VRAM copy you created - // 3b) If the two hashes differ AND efbcopy_state is EC_VRAM_READY, screw your existing VRAM copy. Set efbcopy_state to EC_VRAM_DYNAMIC. + // 3a) If the two hashes match AND type is TCET_EC_VRAM, reuse the VRAM copy you created + // 3b) If the two hashes differ AND type is TCET_EC_VRAM, screw your existing VRAM copy. Set type to TCET_EC_DYNAMIC. // Redecode the source RAM data to a VRAM object. The entry basically behaves like a normal texture now. - // 3c) If efbcopy_state is EC_VRAM_DYNAMIC, treat the EFB copy like a normal texture. + // 3c) If type is TCET_EC_DYNAMIC, treat the EFB copy like a normal texture. // Advantage: Non-dynamic EFB copies can be visually enhanced like with EFB to texture. // Compatibility is as good as EFB to RAM. // Disadvantage: Slower than EFB to texture and often even slower than EFB to RAM. @@ -614,8 +614,8 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat TCacheEntryBase *entry = textures[dstAddr]; if (entry) { - if ((entry->efbcopy_state == EC_VRAM_READY && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h) - || (entry->efbcopy_state == EC_VRAM_DYNAMIC && entry->native_width == tex_w && entry->native_height == tex_h)) + if ((entry->type == TCET_EC_VRAM && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h) + || (entry->type == TCET_EC_DYNAMIC && entry->native_width == tex_w && entry->native_height == tex_h)) { scaled_tex_w = tex_w; scaled_tex_h = tex_h; @@ -637,7 +637,7 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat entry->SetGeneralParameters(dstAddr, 0, dstFormat, 0); entry->SetDimensions(tex_w, tex_h, scaled_tex_w, scaled_tex_h); entry->SetHashes(TEXHASH_INVALID); - entry->efbcopy_state = EC_VRAM_READY; + entry->type = TCET_EC_VRAM; } entry->frameCount = frameCount; diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index 59960c7fb8..b27663dd81 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -14,11 +14,12 @@ class TextureCache { public: - enum EFBCopyState + enum TexCacheEntryType { - EC_NO_COPY, // regular texture - EC_VRAM_READY, // EFB copy sits in VRAM and is ready to use - EC_VRAM_DYNAMIC, // EFB copy sits in RAM and needs to be decoded before using it as a texture + TCET_AUTOFETCH, // Most textures, automatically fetched whenever they change +// TCET_PRELOADED, // Textures which reside in TMEM areas which are manually managed by the game + TCET_EC_VRAM, // EFB copy which sits in VRAM and is ready to be used + TCET_EC_DYNAMIC, // EFB copy which sits in RAM and needs to be decoded before being used }; struct TCacheEntryBase @@ -32,17 +33,12 @@ public: //u32 pal_hash; u32 format; - //bool is_preloaded; + enum TexCacheEntryType type; unsigned int num_mipmaps; unsigned int native_width, native_height; // Texture dimensions from the GameCube's point of view unsigned int virtual_width, virtual_height; // Texture dimensions from OUR point of view - for hires textures or scaled EFB copies - // EFB copies - enum EFBCopyState efbcopy_state; - - bool IsEfbCopy() { return efbcopy_state != EC_NO_COPY; } - // used to delete textures which haven't been used for TEXTURE_KILL_THRESHOLD frames int frameCount; @@ -83,6 +79,8 @@ public: const float *colmat) = 0; int IntersectsMemoryRange(u32 range_address, u32 range_size) const; + + bool IsEfbCopy() { return (type == TCET_EC_VRAM || type == TCET_EC_DYNAMIC); } }; virtual ~TextureCache(); // needs virtual for DX11 dtor -- cgit v1.2.3 From d1605abfa98c39de0e9b9e57944f9dc864c03b42 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sun, 29 Jan 2012 21:55:07 +0100 Subject: Add license header for TextureCacheBase files --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 16 ++++++++++++++++ Source/Core/VideoCommon/Src/TextureCacheBase.h | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 885116ae18..f37e4b2dd5 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -1,3 +1,19 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ #include "MemoryUtil.h" diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index b27663dd81..7ae650a927 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -1,3 +1,19 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program 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, version 2.0. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ #ifndef _TEXTURECACHEBASE_H #define _TEXTURECACHEBASE_H -- cgit v1.2.3 From dabb35afce2539b312935d2fcfe264018d24dbec Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sun, 29 Jan 2012 21:17:22 +0100 Subject: Prepare texture preloading support --- Source/Core/VideoCommon/Src/BPMemory.h | 41 ++++++++++++++++++++-------- Source/Core/VideoCommon/Src/BPStructs.cpp | 27 ++++++++++++------ Source/Core/VideoCommon/Src/TextureDecoder.h | 4 +-- 3 files changed, 49 insertions(+), 23 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/BPMemory.h b/Source/Core/VideoCommon/Src/BPMemory.h index 1f6f453a77..0bb2bf9013 100644 --- a/Source/Core/VideoCommon/Src/BPMemory.h +++ b/Source/Core/VideoCommon/Src/BPMemory.h @@ -65,10 +65,10 @@ #define BPMEM_UNKOWN_57 0x57 #define BPMEM_REVBITS 0x58 #define BPMEM_SCISSOROFFSET 0x59 -#define BPMEM_UNKNOWN_60 0x60 -#define BPMEM_UNKNOWN_61 0x61 -#define BPMEM_UNKNOWN_62 0x62 -#define BPMEM_TEXMODESYNC 0x63 +#define BPMEM_PRELOAD_ADDR 0x60 +#define BPMEM_PRELOAD_TMEMEVEN 0x61 +#define BPMEM_PRELOAD_TMEMODD 0x62 +#define BPMEM_PRELOAD_MODE 0x63 #define BPMEM_LOADTLUT0 0x64 #define BPMEM_LOADTLUT1 0x65 #define BPMEM_TEXINVALIDATE 0x66 @@ -487,10 +487,10 @@ union TexImage1 { struct { - u32 tmem_offset : 15; // we ignore texture caching for now, we do it ourselves - u32 cache_width : 3; + u32 tmem_even : 15; // tmem line index for even LODs + u32 cache_width : 3; u32 cache_height : 3; - u32 image_type : 1; + u32 image_type : 1; // 1 if this texture is managed manually (0 means we'll autofetch the texture data whenever it changes) }; u32 hex; }; @@ -499,7 +499,7 @@ union TexImage2 { struct { - u32 tmem_offset : 15; // we ignore texture caching for now, we do it ourselves + u32 tmem_odd : 15; // tmem line index for odd LODs u32 cache_width : 3; u32 cache_height : 3; }; @@ -893,6 +893,25 @@ union UPE_Copy } }; +union BPU_PreloadTileInfo +{ + u32 hex; + struct { + u32 count : 15; + u32 type : 2; + }; +}; + +struct BPS_TmemConfig +{ + u32 preload_addr; + u32 preload_tmem_even; + u32 preload_tmem_odd; + BPU_PreloadTileInfo preload_tile_info; + u32 tlut_src; + u32 tlut_dest; + u32 texinvalidate; +}; // All of BP memory @@ -951,10 +970,8 @@ struct BPMemory u32 boundbox1;//56 u32 unknown7[2];//57,58 X10Y10 scissorOffset; //59 - u32 unknown8[10]; //5a,5b,5c,5d, 5e,5f,60,61, 62, 63 (GXTexModeSync), 0x60-0x63 have to do with preloaded textures? - u32 tlutXferSrc; //64 - u32 tlutXferDest; //65 - u32 texinvalidate;//66 + u32 unknown8[6]; //5a,5b,5c,5d, 5e,5f + BPS_TmemConfig tmem_config; // 60-66 u32 metric; //67 FieldMode fieldmode;//68 u32 unknown10[7];//69-6F diff --git a/Source/Core/VideoCommon/Src/BPStructs.cpp b/Source/Core/VideoCommon/Src/BPStructs.cpp index d06f1de8b9..6cb102555b 100644 --- a/Source/Core/VideoCommon/Src/BPStructs.cpp +++ b/Source/Core/VideoCommon/Src/BPStructs.cpp @@ -30,6 +30,7 @@ #include "VertexLoader.h" #include "VertexShaderManager.h" #include "Thread.h" +#include "HW/Memmap.h" using namespace BPFunctions; @@ -301,14 +302,14 @@ void BPWritten(const BPCmd& bp) // TODO - figure out a cleaner way. if (GetConfig(CONFIG_ISWII)) - ptr = GetPointer(bpmem.tlutXferSrc << 5); + ptr = GetPointer(bpmem.tmem_config.tlut_src << 5); else - ptr = GetPointer((bpmem.tlutXferSrc & 0xFFFFF) << 5); + ptr = GetPointer((bpmem.tmem_config.tlut_src & 0xFFFFF) << 5); if (ptr) memcpy_gc(texMem + tlutTMemAddr, ptr, tlutXferCount); else - PanicAlert("Invalid palette pointer %08x %08x %08x", bpmem.tlutXferSrc, bpmem.tlutXferSrc << 5, (bpmem.tlutXferSrc & 0xFFFFF)<< 5); + PanicAlert("Invalid palette pointer %08x %08x %08x", bpmem.tmem_config.tlut_src, bpmem.tmem_config.tlut_src << 5, (bpmem.tmem_config.tlut_src & 0xFFFFF)<< 5); // TODO(ector) : kill all textures that use this palette // Not sure if it's a good idea, though. For now, we hash texture palettes @@ -466,14 +467,22 @@ void BPWritten(const BPCmd& bp) DEBUG_LOG(VIDEO, "Uknown BP Reg 0x57: %08x", bp.newvalue); break; - case BPMEM_UNKNOWN_60: - case BPMEM_UNKNOWN_61: - case BPMEM_UNKNOWN_62: - // Cases added due to: http://code.google.com/p/dolphin-emu/issues/detail?id=360#c90 - // Are these related to BBox? + case BPMEM_PRELOAD_ADDR: + case BPMEM_PRELOAD_TMEMEVEN: + case BPMEM_PRELOAD_TMEMODD: // Used when PRELOAD_MODE is set break; - case BPMEM_TEXMODESYNC: // Always set to 0 when GX_TexModeSync() is called. + case BPMEM_PRELOAD_MODE: // Set to 0 when GX_TexModeSync() is called. + // if this is different from 0, manual TMEM management is used. + if (bp.newvalue != 0) + { + // NOTE(neobrain): Apparently tmemodd doesn't affect hardware behavior at all (libogc uses it just as a buffer and switches its contents with tmemeven whenever this is called) + BPS_TmemConfig& tmem_cfg = bpmem.tmem_config; + u8* ram_ptr = Memory::GetPointer(tmem_cfg.preload_addr << 5); + u32 tmem_addr = tmem_cfg.preload_tmem_even * TMEM_LINE_SIZE; + u32 size = tmem_cfg.preload_tile_info.count * 32; + memcpy(texMem + tmem_addr, ram_ptr, size); + } break; // ------------------------------------------------ diff --git a/Source/Core/VideoCommon/Src/TextureDecoder.h b/Source/Core/VideoCommon/Src/TextureDecoder.h index d990d21545..1901c86d27 100644 --- a/Source/Core/VideoCommon/Src/TextureDecoder.h +++ b/Source/Core/VideoCommon/Src/TextureDecoder.h @@ -20,8 +20,8 @@ #include "Hash.h" enum { - TMEM_SIZE = 1024*1024, - HALFTMEM_SIZE = 512*1024 + TMEM_SIZE = 1024*1024, + TMEM_LINE_SIZE = 32, }; extern GC_ALIGNED16(u8 texMem[TMEM_SIZE]); -- cgit v1.2.3 From eb01a110c9573ae0e822eefd3cc8c58d92774588 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sun, 29 Jan 2012 21:49:50 +0100 Subject: Implement texture preloading --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 42 ++++++++++++++++-------- Source/Core/VideoCommon/Src/TextureCacheBase.h | 5 ++- 2 files changed, 30 insertions(+), 17 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index f37e4b2dd5..7873d1474e 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -173,12 +173,12 @@ void TextureCache::ClearRenderTargets() iter = textures.begin(), tcend = textures.end(); for (; iter!=tcend; ++iter) - iter->second->type = TCET_AUTOFETCH; + iter->second->type = TCET_NORMAL; } TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, int texformat, - unsigned int tlutaddr, int tlutfmt, bool UseNativeMips, unsigned int maxlevel) + unsigned int tlutaddr, int tlutfmt, bool UseNativeMips, unsigned int maxlevel, bool from_tmem) { if (0 == address) return NULL; @@ -203,10 +203,12 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, if (isPaletteTexture) full_format = texformat | (tlutfmt << 16); - u8* ptr = Memory::GetPointer(address); const u32 texture_size = TexDecoder_GetTextureSizeInBytes(expandedWidth, expandedHeight, texformat); + u8* src_data; + if (from_tmem) src_data = &texMem[bpmem.tex[stage/4].texImage1[stage%4].tmem_even * TMEM_LINE_SIZE]; + else src_data = Memory::GetPointer(address); - tex_hash = GetHash64(ptr, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); + tex_hash = GetHash64(src_data, texture_size, g_ActiveConfig.iSafeTextureCache_ColorSamples); if (isPaletteTexture) { const u32 palette_size = TexDecoder_GetPaletteSize(texformat); @@ -225,7 +227,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, tex_hash ^= tlut_hash; } - TCacheEntryBase *entry = textures[texID]; + TCacheEntryBase *entry = textures[texID]; // TODO: Should use a different texID for preloaded textures! if (entry) { // 1. Calculate reference hash: @@ -252,7 +254,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // // TODO: Don't we need to force texture decoding to RGBA8 for dynamic EFB copies? // TODO: Actually, it should be enough if the internal texture format matches... - if ((entry->type == TCET_AUTOFETCH && width == entry->native_width && height == entry->native_height && full_format == entry->format && entry->num_mipmaps == maxlevel) + if ((entry->type == TCET_NORMAL && width == entry->native_width && height == entry->native_height && full_format == entry->format && entry->num_mipmaps == maxlevel) || (entry->type == TCET_EC_DYNAMIC && entry->native_width == width && entry->native_height == height)) { // reuse the texture @@ -283,8 +285,9 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, } } + // TODO: RGBA8 textures are stored non-continuously in tmem, that might cause problems when preloading is enabled if (pcfmt == PC_TEX_FMT_NONE) - pcfmt = TexDecoder_Decode(temp, ptr, expandedWidth, + pcfmt = TexDecoder_Decode(temp, src_data, expandedWidth, expandedHeight, texformat, tlutaddr, tlutfmt, g_ActiveConfig.backend_info.bUseRGBATextures); bool isPow2; @@ -301,13 +304,13 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, if (NULL == entry) { textures[texID] = entry = g_texture_cache->CreateTexture(width, height, expandedWidth, texLevels, pcfmt); - // Sometimes, we can get around recreating a texture if only the number of mip levels gets changes + // Sometimes, we can get around recreating a texture if only the number of mip levels changes // e.g. if our texture cache entry got too many mipmap levels we can limit the number of used levels by setting the appropriate render states // Thus, we don't update this member for every Load, but just whenever the texture gets recreated // // TODO: Won't we end up recreating textures all the time because maxlevel doesn't necessarily equal texLevels? entry->num_mipmaps = maxlevel; // TODO: Does this actually work? We can't really adjust mipmap settings per-stage... - entry->type = TCET_AUTOFETCH; + entry->type = TCET_NORMAL; GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); } @@ -315,13 +318,13 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps); entry->SetDimensions(nativeW, nativeH, width, height); entry->hash = tex_hash; - if (g_ActiveConfig.bCopyEFBToTexture) entry->type = TCET_AUTOFETCH; + if (g_ActiveConfig.bCopyEFBToTexture) entry->type = TCET_NORMAL; else if (entry->IsEfbCopy()) entry->type = TCET_EC_DYNAMIC; // load texture entry->Load(width, height, expandedWidth, 0, (texLevels == 0)); - // load mips + // load mips - TODO: Loading mipmaps from tmem is untested! if (texLevels > 1 && pcfmt != PC_TEX_FMT_NONE) { const unsigned int bsdepth = TexDecoder_GetTexelSizeInNibbles(texformat); @@ -329,20 +332,31 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, unsigned int level = 1; unsigned int mipWidth = (width + 1) >> 1; unsigned int mipHeight = (height + 1) >> 1; - ptr += texture_size; + + u8* ptr_even = NULL, *ptr_odd = NULL; + if (from_tmem) + { + ptr_even = &texMem[bpmem.tex[stage/4].texImage1[stage%4].tmem_even * TMEM_LINE_SIZE + texture_size]; + ptr_odd = &texMem[bpmem.tex[stage/4].texImage2[stage%4].tmem_odd * TMEM_LINE_SIZE]; + } + src_data += texture_size; while ((mipHeight || mipWidth) && (level < texLevels)) { + u8** ptr; + if (from_tmem) ptr = (level % 2) ? &ptr_odd : &ptr_even; + else ptr = &src_data; + const unsigned int currentWidth = (mipWidth > 0) ? mipWidth : 1; const unsigned int currentHeight = (mipHeight > 0) ? mipHeight : 1; expandedWidth = (currentWidth + bsw) & (~bsw); expandedHeight = (currentHeight + bsh) & (~bsh); - TexDecoder_Decode(temp, ptr, expandedWidth, expandedHeight, texformat, tlutaddr, tlutfmt, g_ActiveConfig.backend_info.bUseRGBATextures); + TexDecoder_Decode(temp, *ptr, expandedWidth, expandedHeight, texformat, tlutaddr, tlutfmt, g_ActiveConfig.backend_info.bUseRGBATextures); entry->Load(currentWidth, currentHeight, expandedWidth, level, false); - ptr += ((std::max(mipWidth, bsw) * std::max(mipHeight, bsh) * bsdepth) >> 1); + *ptr += ((std::max(mipWidth, bsw) * std::max(mipHeight, bsh) * bsdepth) >> 1); mipWidth >>= 1; mipHeight >>= 1; ++level; diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index 7ae650a927..2924c5871f 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -32,8 +32,7 @@ class TextureCache public: enum TexCacheEntryType { - TCET_AUTOFETCH, // Most textures, automatically fetched whenever they change -// TCET_PRELOADED, // Textures which reside in TMEM areas which are manually managed by the game + TCET_NORMAL, TCET_EC_VRAM, // EFB copy which sits in VRAM and is ready to be used TCET_EC_DYNAMIC, // EFB copy which sits in RAM and needs to be decoded before being used }; @@ -115,7 +114,7 @@ public: virtual TCacheEntryBase* CreateRenderTargetTexture(unsigned int scaled_tex_w, unsigned int scaled_tex_h) = 0; static TCacheEntryBase* Load(unsigned int stage, u32 address, unsigned int width, unsigned int height, - int format, unsigned int tlutaddr, int tlutfmt, bool UseNativeMips, unsigned int maxlevel); + int format, unsigned int tlutaddr, int tlutfmt, bool UseNativeMips, unsigned int maxlevel, bool from_tmem); static void CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, unsigned int srcFormat, const EFBRectangle& srcRect, bool isIntensity, bool scaleByHalf); -- cgit v1.2.3 From 439613b83352d59ebaff25d35151e25f616804b4 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Sat, 4 Feb 2012 13:01:52 +0100 Subject: TextureCacheBase: Remove a superfluous TODO (texture hashing takes care of that stuff) --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index 7873d1474e..cc14499330 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -227,7 +227,7 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, tex_hash ^= tlut_hash; } - TCacheEntryBase *entry = textures[texID]; // TODO: Should use a different texID for preloaded textures! + TCacheEntryBase *entry = textures[texID]; if (entry) { // 1. Calculate reference hash: -- cgit v1.2.3 From e58692653afda21f7593d340025c9d4328689244 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Fri, 17 Feb 2012 17:09:17 +0100 Subject: PixelShaderGen: Use correct texel to pixel mapping when sampling textures (D3D9 only) Fixes issue 2068. Fixes issue 5158. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 1726855d54..5e54b01129 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -1099,9 +1099,15 @@ static void WriteStage(char *&p, int n, API_TYPE ApiType) void SampleTexture(char *&p, const char *destination, const char *texcoords, const char *texswap, int texmap, API_TYPE ApiType) { if (ApiType == API_D3D11) - WRITE(p, "%s=Tex%d.Sample(samp%d,%s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap,texmap, texcoords, texmap, texswap); + WRITE(p, "%s=Tex%d.Sample(samp%d, %s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap,texmap, texcoords, texmap, texswap); + else if (ApiType & API_D3D9) + { + // D3D9 uses different pixel to texel mapping, so we need to offset our base address by half a pixel. + // Read the MSDN article "Directly Mapping Texels to Pixels (Direct3D 9)" for further info. + WRITE(p, "%s=tex2D(samp%d, (%s.xy + float2(0.5f,0.5f)) * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap, texcoords, texmap, texswap); + } else - WRITE(p, "%s=tex2D(samp%d,%s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap, texcoords, texmap, texswap); + WRITE(p, "%s=tex2D(samp%d, %s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap, texcoords, texmap, texswap); } static const char *tevAlphaFuncsTable[] = -- cgit v1.2.3 From 9adc119e3c2f845c0172b012badbbc86addad4a5 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Mon, 20 Feb 2012 17:55:18 +0100 Subject: PixelShaderGen: For custom textures and scaled EFB copies, use correct texel to pixel mapping when sampling textures (D3D9 only) This is basically the same as revision e58692653afd, just for scaled textures and with a LOT more work... --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 5 +-- Source/Core/VideoCommon/Src/PixelShaderGen.h | 19 +++++----- Source/Core/VideoCommon/Src/PixelShaderManager.cpp | 42 +++++++++++++++++++--- Source/Core/VideoCommon/Src/PixelShaderManager.h | 6 ++-- 4 files changed, 54 insertions(+), 18 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index 5e54b01129..ed504f1c82 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -560,6 +560,7 @@ const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType WRITE(p, "uniform float4 "I_KCOLORS"[4] : register(c%d);\n", C_KCOLORS); WRITE(p, "uniform float4 "I_ALPHA"[1] : register(c%d);\n", C_ALPHA); WRITE(p, "uniform float4 "I_TEXDIMS"[8] : register(c%d);\n", C_TEXDIMS); + WRITE(p, "uniform float4 "I_VTEXSCALE"[8] : register(c%d);\n", C_VTEXSCALE); WRITE(p, "uniform float4 "I_ZBIAS"[2] : register(c%d);\n", C_ZBIAS); WRITE(p, "uniform float4 "I_INDTEXSCALE"[2] : register(c%d);\n", C_INDTEXSCALE); WRITE(p, "uniform float4 "I_INDTEXMTX"[6] : register(c%d);\n", C_INDTEXMTX); @@ -1102,9 +1103,9 @@ void SampleTexture(char *&p, const char *destination, const char *texcoords, con WRITE(p, "%s=Tex%d.Sample(samp%d, %s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap,texmap, texcoords, texmap, texswap); else if (ApiType & API_D3D9) { - // D3D9 uses different pixel to texel mapping, so we need to offset our base address by half a pixel. + // D3D9 uses different pixel to texel mapping, so we need to offset our sampling address by half a pixel (assuming native and virtual texture dimensions match each other, otherwise some math is involved). // Read the MSDN article "Directly Mapping Texels to Pixels (Direct3D 9)" for further info. - WRITE(p, "%s=tex2D(samp%d, (%s.xy + float2(0.5f,0.5f)) * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap, texcoords, texmap, texswap); + WRITE(p, "%s=tex2D(samp%d, (%s.xy + 0.5f*"I_VTEXSCALE"[%d].%s) * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap, texcoords, texmap/2, (texmap&1)?"zw":"xy", texmap, texswap); } else WRITE(p, "%s=tex2D(samp%d, %s.xy * "I_TEXDIMS"[%d].xy).%s;\n", destination, texmap, texcoords, texmap, texswap); diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.h b/Source/Core/VideoCommon/Src/PixelShaderGen.h index 31242a916e..7374251c96 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.h +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.h @@ -24,26 +24,27 @@ #define I_KCOLORS "k" #define I_ALPHA "alphaRef" #define I_TEXDIMS "texdim" +#define I_VTEXSCALE "vtexscale" #define I_ZBIAS "czbias" #define I_INDTEXSCALE "cindscale" #define I_INDTEXMTX "cindmtx" #define I_FOG "cfog" #define I_PLIGHTS "cLights" -#define I_PMATERIALS "cmtrl" +#define I_PMATERIALS "cmtrl" #define C_COLORMATRIX 0 // 0 #define C_COLORS 0 // 0 #define C_KCOLORS (C_COLORS + 4) // 4 #define C_ALPHA (C_KCOLORS + 4) // 8 #define C_TEXDIMS (C_ALPHA + 1) // 9 -#define C_ZBIAS (C_TEXDIMS + 8) //17 -#define C_INDTEXSCALE (C_ZBIAS + 2) //19 -#define C_INDTEXMTX (C_INDTEXSCALE + 2) //21 -#define C_FOG (C_INDTEXMTX + 6) //27 - -#define C_PLIGHTS (C_FOG + 3) -#define C_PMATERIALS (C_PLIGHTS + 40) -#define C_PENVCONST_END (C_PMATERIALS + 4) +#define C_VTEXSCALE (C_TEXDIMS + 8) //17 - virtual texture scaling factor (e.g. custom textures, scaled EFB copies) +#define C_ZBIAS (C_VTEXSCALE + 4) //21 +#define C_INDTEXSCALE (C_ZBIAS + 2) //23 +#define C_INDTEXMTX (C_INDTEXSCALE + 2) //25 +#define C_FOG (C_INDTEXMTX + 6) //31 +#define C_PLIGHTS (C_FOG + 3) //34 +#define C_PMATERIALS (C_PLIGHTS + 40) //74 +#define C_PENVCONST_END (C_PMATERIALS + 4) //78 #define PIXELSHADERUID_MAX_VALUES 70 #define PIXELSHADERUID_MAX_VALUES_SAFE 120 diff --git a/Source/Core/VideoCommon/Src/PixelShaderManager.cpp b/Source/Core/VideoCommon/Src/PixelShaderManager.cpp index 2521f80500..3e0f6d73c4 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderManager.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderManager.cpp @@ -36,9 +36,11 @@ static bool s_bFogRangeAdjustChanged; static int nLightsChanged[2]; // min,max static float lastRGBAfull[2][4][4]; static u8 s_nTexDimsChanged; +static u8 s_nVirtualTexScalesChanged; static u8 s_nIndTexScaleChanged; static u32 lastAlpha; static u32 lastTexDims[8]; // width | height << 16 | wrap_s << 28 | wrap_t << 30 +static float lastVirtualTexScales[16]; // even fields: width ratio; odd fields: height ratio static u32 lastZBias; static int nMaterialsChanged; @@ -61,6 +63,7 @@ void PixelShaderManager::Init() { lastAlpha = 0; memset(lastTexDims, 0, sizeof(lastTexDims)); + memset(lastVirtualTexScales, 0, sizeof(lastVirtualTexScales)); lastZBias = 0; memset(lastRGBAfull, 0, sizeof(lastRGBAfull)); Dirty(); @@ -70,6 +73,7 @@ void PixelShaderManager::Dirty() { s_nColorsChanged[0] = s_nColorsChanged[1] = 15; s_nTexDimsChanged = 0xFF; + s_nVirtualTexScalesChanged = 0xFF; s_nIndTexScaleChanged = 0xFF; s_nIndTexMtxChanged = 15; s_bAlphaChanged = s_bZBiasChanged = s_bZTextureTypeChanged = s_bDepthRangeChanged = true; @@ -83,7 +87,7 @@ void PixelShaderManager::Shutdown() } -void PixelShaderManager::SetConstants() +void PixelShaderManager::SetConstants(API_TYPE api_type) { for (int i = 0; i < 2; ++i) { @@ -109,6 +113,16 @@ void PixelShaderManager::SetConstants() s_nTexDimsChanged = 0; } + if ((api_type & API_D3D9) && s_nVirtualTexScalesChanged) + { + for (int i = 0; i < 8; i += 2) + { + if (s_nVirtualTexScalesChanged & (3<>8)&0xff)/255.0f, 0, ((lastAlpha>>16)&0xff)/255.0f); @@ -338,6 +352,13 @@ void PixelShaderManager::SetPSTextureDims(int texid) SetPSConstant4fv(C_TEXDIMS + texid, fdims); } +void PixelShaderManager::SetPSVirtualTexScalePair(int texpairid) +{ + PRIM_LOG("vtexscale%d: %f %f %f %f\n", texpairid, lastVirtualTexScales[texpairid*4], lastVirtualTexScales[texpairid*4+1], + lastVirtualTexScales[texpairid*4+2], lastVirtualTexScales[texpairid*4+3]); + SetPSConstant4fv(C_VTEXSCALE + texpairid, &lastVirtualTexScales[texpairid*4]); +} + // This one is high in profiles (0.5%). TODO: Move conversion out, only store the raw color value // and update it when the shader constant is set, only. void PixelShaderManager::SetColorChanged(int type, int num, bool high) @@ -376,14 +397,25 @@ void PixelShaderManager::SetDestAlpha(const ConstantAlpha& alpha) } } -void PixelShaderManager::SetTexDims(int texmapid, u32 width, u32 height, u32 wraps, u32 wrapt) +void PixelShaderManager::SetTexDims(int texmapid, u32 width, u32 height, u32 virtual_width, u32 virtual_height, u32 wraps, u32 wrapt, API_TYPE api_type) { u32 wh = width | (height << 16) | (wraps << 28) | (wrapt << 30); - if (lastTexDims[texmapid] != wh) + + bool refresh = lastTexDims[texmapid] != wh; + if (api_type & API_D3D9) { - lastTexDims[texmapid] = wh; + refresh |= (lastVirtualTexScales[texmapid*2] != (float)width / (float)virtual_width); + refresh |= (lastVirtualTexScales[texmapid*2+1] != (float)height / (float)virtual_height); + } + + if (refresh) + { + lastTexDims[texmapid] = wh; + lastVirtualTexScales[texmapid*2] = (float)width / (float)virtual_width; + lastVirtualTexScales[texmapid*2+1] = (float)height / (float)virtual_height; s_nTexDimsChanged |= 1 << texmapid; - } + s_nVirtualTexScalesChanged |= 1 << texmapid; + } } void PixelShaderManager::SetZTextureBias(u32 bias) diff --git a/Source/Core/VideoCommon/Src/PixelShaderManager.h b/Source/Core/VideoCommon/Src/PixelShaderManager.h index 2d1c01cad6..5cbe86cae4 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderManager.h +++ b/Source/Core/VideoCommon/Src/PixelShaderManager.h @@ -26,18 +26,20 @@ class PixelShaderManager { static void SetPSTextureDims(int texid); + static void SetPSVirtualTexScalePair(int texpairid); + public: static void Init(); static void Dirty(); static void Shutdown(); - static void SetConstants(); // sets pixel shader constants + static void SetConstants(API_TYPE api_type); // sets pixel shader constants // constant management, should be called after memory is committed static void SetColorChanged(int type, int index, bool high); static void SetAlpha(const AlphaFunc& alpha); static void SetDestAlpha(const ConstantAlpha& alpha); - static void SetTexDims(int texmapid, u32 width, u32 height, u32 wraps, u32 wrapt); + static void SetTexDims(int texmapid, u32 width, u32 height, u32 virtual_width, u32 virtual_height, u32 wraps, u32 wrapt, API_TYPE api_type); static void SetZTextureBias(u32 bias); static void SetViewportChanged(); static void SetIndMatrixChanged(int matrixidx); -- cgit v1.2.3 From 3a9fed0ba285b3c2bb80de408ac1a9da9ca774b6 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Tue, 21 Feb 2012 12:51:41 +0100 Subject: PixelShaderGen: Fix a bug introduced in revision 9adc119e3c2f. D3D11: Fix shader constant mapping. --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index ed504f1c82..fe5827de05 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -560,7 +560,10 @@ const char *GeneratePixelShaderCode(DSTALPHA_MODE dstAlphaMode, API_TYPE ApiType WRITE(p, "uniform float4 "I_KCOLORS"[4] : register(c%d);\n", C_KCOLORS); WRITE(p, "uniform float4 "I_ALPHA"[1] : register(c%d);\n", C_ALPHA); WRITE(p, "uniform float4 "I_TEXDIMS"[8] : register(c%d);\n", C_TEXDIMS); - WRITE(p, "uniform float4 "I_VTEXSCALE"[8] : register(c%d);\n", C_VTEXSCALE); + if (ApiType & API_D3D9) + { + WRITE(p, "uniform float4 "I_VTEXSCALE"[4] : register(c%d);\n", C_VTEXSCALE); + } WRITE(p, "uniform float4 "I_ZBIAS"[2] : register(c%d);\n", C_ZBIAS); WRITE(p, "uniform float4 "I_INDTEXSCALE"[2] : register(c%d);\n", C_INDTEXSCALE); WRITE(p, "uniform float4 "I_INDTEXMTX"[6] : register(c%d);\n", C_INDTEXMTX); -- cgit v1.2.3 From f92d1e1e931db66d10db4cb2377bcb794dc898ee Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Fri, 24 Feb 2012 14:25:02 -0600 Subject: fix some clang compilation errors --- Source/Core/VideoCommon/Src/AVIDump.cpp | 2 +- Source/Core/VideoCommon/Src/BPMemory.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/AVIDump.cpp b/Source/Core/VideoCommon/Src/AVIDump.cpp index 469e00d9f5..670693690b 100644 --- a/Source/Core/VideoCommon/Src/AVIDump.cpp +++ b/Source/Core/VideoCommon/Src/AVIDump.cpp @@ -263,7 +263,7 @@ bool AVIDump::CreateFile() s_Stream->codec->bit_rate = 400000; s_Stream->codec->width = s_width; s_Stream->codec->height = s_height; - s_Stream->codec->time_base = (AVRational){1, VideoInterface::TargetRefreshRate}; + s_Stream->codec->time_base = (AVRational){1, static_cast(VideoInterface::TargetRefreshRate)}; s_Stream->codec->gop_size = 12; s_Stream->codec->pix_fmt = g_Config.bUseFFV1 ? PIX_FMT_BGRA : PIX_FMT_YUV420P; diff --git a/Source/Core/VideoCommon/Src/BPMemory.cpp b/Source/Core/VideoCommon/Src/BPMemory.cpp index da09893f35..5c4ed457e3 100644 --- a/Source/Core/VideoCommon/Src/BPMemory.cpp +++ b/Source/Core/VideoCommon/Src/BPMemory.cpp @@ -61,7 +61,7 @@ void BPReload() // Cases in which we DON'T want to reload the BP continue; default: - BPCmd bp = {i, 0xFFFFFF, ((u32*)&bpmem)[i]}; + BPCmd bp = {i, 0xFFFFFF, static_cast(((u32*)&bpmem)[i])}; BPWritten(bp); } } -- cgit v1.2.3 From 9e398fd418027e4932a1539f7d382b70e8acc408 Mon Sep 17 00:00:00 2001 From: skidau Date: Fri, 2 Mar 2012 18:53:41 +1100 Subject: Added an external exception check when the CPU writes to the FIFO. This allows the CPU time to service FIFO overflows. Fixes random hangs caused by FIFO overflows and desyncs like in "The Last Story" and "Battalion Wars 2". Thanks to marcosvitali for the research. Fixes issue 5209. Fixes issue 5150. Fixes issue 5055. Fixes issue 4889. Fixes issue 4061. Fixes issue 4010. Fixes issue 3902. --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 22 ---------------------- Source/Core/VideoCommon/Src/CommandProcessor.h | 1 - Source/Core/VideoCommon/Src/Fifo.cpp | 3 +-- 3 files changed, 1 insertion(+), 25 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index fc85b8e39d..dbe09aaa0e 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -60,7 +60,6 @@ volatile bool interruptSet= false; volatile bool interruptWaiting= false; volatile bool interruptTokenWaiting = false; volatile bool interruptFinishWaiting = false; -volatile bool OnOverflow = false; bool IsOnThread() { @@ -92,7 +91,6 @@ void DoState(PointerWrap &p) p.Do(interruptWaiting); p.Do(interruptTokenWaiting); p.Do(interruptFinishWaiting); - p.Do(OnOverflow); } inline void WriteLow (volatile u32& _reg, u16 lowbits) {Common::AtomicStore(_reg,(_reg & 0xFFFF0000) | lowbits);} @@ -135,7 +133,6 @@ void Init() bProcessFifoToLoWatermark = false; bProcessFifoAllDistance = false; isPossibleWaitingSetDrawDone = false; - OnOverflow = false; et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper); } @@ -449,26 +446,7 @@ void STACKALIGN GatherPipeBursted() Common::AtomicAdd(fifo.CPReadWriteDistance, GATHER_PIPE_SIZE); if (!IsOnThread()) - { RunGpu(); - } - else - { - if(fifo.CPReadWriteDistance == fifo.CPEnd - fifo.CPBase - 32) - { - if(!OnOverflow) - NOTICE_LOG(COMMANDPROCESSOR,"FIFO is almost in overflown, BreakPoint: %i", fifo.bFF_Breakpoint); - OnOverflow = true; - while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && - fifo.CPReadWriteDistance > fifo.CPEnd - fifo.CPBase - 64) - Common::YieldCPU(); - } - else - { - OnOverflow = false; - } - } - _assert_msg_(COMMANDPROCESSOR, fifo.CPReadWriteDistance <= fifo.CPEnd - fifo.CPBase, "FIFO is overflown by GatherPipe !\nCPU thread is too fast!"); diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.h b/Source/Core/VideoCommon/Src/CommandProcessor.h index db6772d66d..bb969fcb95 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.h +++ b/Source/Core/VideoCommon/Src/CommandProcessor.h @@ -35,7 +35,6 @@ extern volatile bool interruptSet; extern volatile bool interruptWaiting; extern volatile bool interruptTokenWaiting; extern volatile bool interruptFinishWaiting; -extern volatile bool OnOverflow; // internal hardware addresses enum diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index 842ff49e78..5cb3252816 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -137,8 +137,7 @@ void RunGpuLoop() CommandProcessor::SetCpStatus(); // check if we are able to run this buffer - while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && - fifo.CPReadWriteDistance && (!AtBreakpoint() || CommandProcessor::OnOverflow)) + while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint()) { if (!GpuRunningState) break; -- cgit v1.2.3 From f5d4fe0bfe13937fa3736aa6b78974ceb6e190ad Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 3 Mar 2012 20:07:20 +0100 Subject: Fix some minor bugs pointed out by PVS Studio (thanks!) --- Source/Core/VideoCommon/Src/PixelShaderGen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index fe5827de05..3ef923dc3e 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -158,7 +158,7 @@ void GetPixelShaderId(PIXELSHADERUID *uid, DSTALPHA_MODE dstAlphaMode, u32 compo } u32* ptr = &uid->values[2]; - for (int i = 0; i < bpmem.genMode.numtevstages+1; ++i) + for (unsigned int i = 0; i < bpmem.genMode.numtevstages+1; ++i) { StageHash(i, ptr); ptr += 4; // max: ptr = &uid->values[66] -- cgit v1.2.3 From b0f75f17ae19484735675fea348f7e95740f597f Mon Sep 17 00:00:00 2001 From: marcosvitali Date: Mon, 5 Mar 2012 02:40:10 -0300 Subject: This release still fixed the hangs produced by fifo overflow without sacrifice performance. For example you can test Tutorial moves at the beginning of The last history now is fluid 30/60. Shuffle2: I've delete the hacky line, I think is not necessary anymore. Additional some clean in CommandProcessor. Please test The Last Story and others games affected in the previous commits and give me a feedback. --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 39 +++++++++++++----------- Source/Core/VideoCommon/Src/CommandProcessor.h | 1 + Source/Core/VideoCommon/Src/MainBase.cpp | 5 +++ Source/Core/VideoCommon/Src/PixelEngine.cpp | 2 -- 4 files changed, 28 insertions(+), 19 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index dbe09aaa0e..38b342b76c 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -56,6 +56,7 @@ static bool bProcessFifoToLoWatermark = false; static bool bProcessFifoAllDistance = false; volatile bool isPossibleWaitingSetDrawDone = false; +volatile bool isHiWatermarkActive = false; volatile bool interruptSet= false; volatile bool interruptWaiting= false; volatile bool interruptTokenWaiting = false; @@ -85,7 +86,7 @@ void DoState(PointerWrap &p) p.Do(bProcessFifoToLoWatermark); p.Do(bProcessFifoAllDistance); - + p.Do(isHiWatermarkActive); p.Do(isPossibleWaitingSetDrawDone); p.Do(interruptSet); p.Do(interruptWaiting); @@ -133,6 +134,7 @@ void Init() bProcessFifoToLoWatermark = false; bProcessFifoAllDistance = false; isPossibleWaitingSetDrawDone = false; + isHiWatermarkActive = false; et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper); } @@ -141,7 +143,6 @@ void Read16(u16& _rReturnValue, const u32 _Address) { INFO_LOG(COMMANDPROCESSOR, "(r): 0x%08x", _Address); - ProcessFifoEvents(); switch (_Address & 0xFFF) { case STATUS_REGISTER: @@ -409,7 +410,6 @@ void Write16(const u16 _Value, const u32 _Address) if (!IsOnThread()) RunGpu(); - ProcessFifoEvents(); } void Read32(u32& _rReturnValue, const u32 _Address) @@ -425,7 +425,6 @@ void Write32(const u32 _Data, const u32 _Address) void STACKALIGN GatherPipeBursted() { - ProcessFifoEvents(); // if we aren't linked, we don't care about gather pipe data if (!m_CPCtrlReg.GPLinkEnable) { @@ -487,17 +486,18 @@ void AbortFrame() void SetOverflowStatusFromGatherPipe() { - if (!fifo.bFF_HiWatermarkInt) return; + fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark); - fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark); + isHiWatermarkActive = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt && m_CPCtrlReg.GPReadEnable; - bool interrupt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt && - m_CPCtrlReg.GPLinkEnable && m_CPCtrlReg.GPReadEnable; - - if (interrupt != interruptSet && interrupt) - CommandProcessor::UpdateInterrupts(true); - + if (isHiWatermarkActive) + { + interruptSet = true; + INFO_LOG(COMMANDPROCESSOR,"Interrupt set"); + ProcessorInterface::SetInterrupt(INT_CAUSE_CP, true); + } + } void SetCpStatus() @@ -505,7 +505,7 @@ void SetCpStatus() // overflow & underflow check fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark); fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark); - + // breakpoint if (fifo.bFF_BPEnable) @@ -540,13 +540,18 @@ void SetCpStatus() bool interrupt = (bpInt || ovfInt || undfInt) && m_CPCtrlReg.GPReadEnable; + isHiWatermarkActive = ovfInt && m_CPCtrlReg.GPReadEnable; + if (interrupt != interruptSet && !interruptWaiting) { u64 userdata = interrupt?1:0; if (IsOnThread()) { - interruptWaiting = true; - CommandProcessor::UpdateInterruptsFromVideoBackend(userdata); + if(!interrupt || bpInt || undfInt) + { + interruptWaiting = true; + CommandProcessor::UpdateInterruptsFromVideoBackend(userdata); + } } else CommandProcessor::UpdateInterrupts(userdata); @@ -631,8 +636,8 @@ void SetCpControlRegister() ProcessorInterface::Fifo_CPUEnd = fifo.CPEnd; } // If overflown happens process the fifo to LoWatemark - if (bProcessFifoToLoWatermark) - ProcessFifoToLoWatermark(); + //if (bProcessFifoToLoWatermark) + // ProcessFifoToLoWatermark(); if(fifo.bFF_GPReadEnable && !m_CPCtrlReg.GPReadEnable) { diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.h b/Source/Core/VideoCommon/Src/CommandProcessor.h index bb969fcb95..2bf965a3ba 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.h +++ b/Source/Core/VideoCommon/Src/CommandProcessor.h @@ -31,6 +31,7 @@ namespace CommandProcessor extern SCPFifoStruct fifo; //This one is shared between gfx thread and emulator thread. extern volatile bool isPossibleWaitingSetDrawDone; //This one is used for sync gfx thread and emulator thread. +extern volatile bool isHiWatermarkActive; extern volatile bool interruptSet; extern volatile bool interruptWaiting; extern volatile bool interruptTokenWaiting; diff --git a/Source/Core/VideoCommon/Src/MainBase.cpp b/Source/Core/VideoCommon/Src/MainBase.cpp index af21ebbb94..0b1258a662 100644 --- a/Source/Core/VideoCommon/Src/MainBase.cpp +++ b/Source/Core/VideoCommon/Src/MainBase.cpp @@ -250,6 +250,11 @@ bool VideoBackendHardware::Video_IsPossibleWaitingSetDrawDone() return CommandProcessor::isPossibleWaitingSetDrawDone; } +bool VideoBackendHardware::Video_IsHiWatermarkActive() +{ + return CommandProcessor::isHiWatermarkActive; +} + void VideoBackendHardware::Video_AbortFrame() { CommandProcessor::AbortFrame(); diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 456e0fb535..ec0683742f 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -180,7 +180,6 @@ void Init() void Read16(u16& _uReturnValue, const u32 _iAddress) { DEBUG_LOG(PIXELENGINE, "(r16) 0x%08x", _iAddress); - CommandProcessor::ProcessFifoEvents(); switch (_iAddress & 0xFFF) { // CPU Direct Access EFB Raster State Config @@ -334,7 +333,6 @@ void Write16(const u16 _iValue, const u32 _iAddress) break; } - CommandProcessor::ProcessFifoEvents(); } void Write32(const u32 _iValue, const u32 _iAddress) -- cgit v1.2.3 From 6fc8cdf12d41ea9f9be2d345face72ddf1fe045d Mon Sep 17 00:00:00 2001 From: LPFaint99 Date: Wed, 7 Mar 2012 20:08:14 -0800 Subject: revert r90a2096a24f4 behavior changes in PixelEngine.cpp. Thanks to Autoran1 for finding the responsible commit. Fixes Scrubbing Serena Beach Countdown in both SC and DC. Fixes issue 1498. --- Source/Core/VideoCommon/Src/PixelEngine.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 456e0fb535..8164bc41da 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -269,6 +269,10 @@ void Read16(u16& _uReturnValue, const u32 _iAddress) case PE_PERF_5L: case PE_PERF_5H: INFO_LOG(PIXELENGINE, "(r16) perf counter @ %08x", _iAddress); + // git r90a2096a24f4 (svn r3663) added the PE_PERF cases, without setting + // _uReturnValue to anything, this reverts to the previous behaviour which allows + // The timer in SMS:Scrubbing Serena Beach to countdown correctly + _uReturnValue = 1; break; default: -- cgit v1.2.3 From c2e6fdf09f33f7765526fac680d13af9ba744125 Mon Sep 17 00:00:00 2001 From: marcosvitali Date: Thu, 8 Mar 2012 02:47:55 -0300 Subject: - I've fixed possibles random hangs in DC mode. - I've fixed hangs in DC mode in (Simpsons, Monkey Island, Pokemon XD, etc) - I've implemented accurate manage of Pixel Engine Interrupts, now the GPU loop is stopped when a PE Interrupt needs to be managed and resume when Pixel Engine finish, I think now, the Fifo in DC mode is more accurate than SC mode. :) Time to close the big fifo Issue 3694 (snif), please if you have a possible fifo issue report this like a game issue. I was working with Skid_AU together, especially thanks for him. Test a lot all games, and compare the performance with the master maybe this accuracy has a cost (not a lot). I think now the fifo is very stable, overflow fixed, random hang fixed, if you have a game with a hang with this rev and not in master please report this. --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 5 +++-- Source/Core/VideoCommon/Src/Fifo.cpp | 3 ++- Source/Core/VideoCommon/Src/PixelEngine.cpp | 5 +++++ Source/Core/VideoCommon/Src/PixelEngine.h | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index 38b342b76c..4fd7e88f5e 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -596,8 +596,8 @@ void SetCpStatusRegister() // Here always there is one fifo attached to the GPU m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint; - m_CPStatusReg.ReadIdle = (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint); - m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance; + m_CPStatusReg.ReadIdle = (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ; + m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance || (IsOnThread() && PixelEngine::WaitingForPEInterrupt()); m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark; m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark; @@ -620,6 +620,7 @@ void SetCpControlRegister() if (!fifo.bFF_GPReadEnable && m_CPCtrlReg.GPReadEnable && !m_CPCtrlReg.BPEnable) { + ProcessFifoEvents(); PixelEngine::ResetSetFinish(); } diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index 5cb3252816..2e60055c97 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -22,6 +22,7 @@ #include "Atomic.h" #include "OpcodeDecoding.h" #include "CommandProcessor.h" +#include "PixelEngine.h" #include "ChunkFile.h" #include "Fifo.h" #include "HW/Memmap.h" @@ -137,7 +138,7 @@ void RunGpuLoop() CommandProcessor::SetCpStatus(); // check if we are able to run this buffer - while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint()) + while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt()) { if (!GpuRunningState) break; diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index ec0683742f..09cb9b8a5c 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -468,4 +468,9 @@ void ResetSetToken() } CommandProcessor::interruptTokenWaiting = false; } + +bool WaitingForPEInterrupt() +{ + return CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || interruptSetFinish || interruptSetToken; +} } // end of namespace PixelEngine diff --git a/Source/Core/VideoCommon/Src/PixelEngine.h b/Source/Core/VideoCommon/Src/PixelEngine.h index dd0304fbe1..5e64300ef3 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.h +++ b/Source/Core/VideoCommon/Src/PixelEngine.h @@ -80,7 +80,7 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge); void SetFinish(void); void ResetSetFinish(void); void ResetSetToken(void); -bool AllowIdleSkipping(); +bool WaitingForPEInterrupt(); // Bounding box functionality. Paper Mario (both) are a couple of the few games that use it. extern u16 bbox[4]; -- cgit v1.2.3 From 41652d6b1f391fdcd0f599463f689c0299cdc007 Mon Sep 17 00:00:00 2001 From: marcosvitali Date: Fri, 9 Mar 2012 01:33:29 -0300 Subject: I've fixed Metroid Prime 3 and 2 desync. And other games with desync because of FIFO Reset. That happens because FIFO_RW_DISTANCE_HI must be written first, for checking fifo.CPReadWriteDistance == 0, so some fifo resets was not managed in the right way. I didn't test Metroid 2 desync reported in Issue 4336 but I think is the same. About the flickering in MP2, I don't know for my is not related or yes, but you can test anyway. Fixed Issue 3902 Well now the FIFO is 99.99% finished :) --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index 4fd7e88f5e..a6e68f6298 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -388,10 +388,6 @@ void Write16(const u16 _Value, const u32 _Address) case FIFO_RW_DISTANCE_HI: WriteHigh((u32 &)fifo.CPReadWriteDistance, _Value); - DEBUG_LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_HI : %04x", _Value); - break; - case FIFO_RW_DISTANCE_LO: - WriteLow((u32 &)fifo.CPReadWriteDistance, _Value & 0xFFE0); if (fifo.CPReadWriteDistance == 0) { GPFifo::ResetGatherPipe(); @@ -401,6 +397,10 @@ void Write16(const u16 _Value, const u32 _Address) ResetVideoBuffer(); } IncrementCheckContextId(); + DEBUG_LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_HI : %04x", _Value); + break; + case FIFO_RW_DISTANCE_LO: + WriteLow((u32 &)fifo.CPReadWriteDistance, _Value & 0xFFE0); DEBUG_LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_LO : %04x", _Value); break; -- cgit v1.2.3 From 104603467be6cc15e63dfb5cadec8044edd1da46 Mon Sep 17 00:00:00 2001 From: marcosvitali Date: Fri, 9 Mar 2012 18:58:23 -0300 Subject: This commit fix games hanging because of my prior Revision c2e6fdf09f33 The external exceptions in dolphin are checking frequently but is different to real HW, so sometime the game is in a loop checking GPU STATUS, the exceptions doesn't checked, and the game hang.\ For solve this I need a trick: still waiting for the exception handler be linked but if CommandProcecsor is reading the GPStatus, resume this. This fixed "TimeSplitters: Future Perfect" broken in the Revision c2e6fdf09f33 and surely others games. --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 10 +++++++--- Source/Core/VideoCommon/Src/PixelEngine.cpp | 14 ++++++++------ Source/Core/VideoCommon/Src/PixelEngine.h | 1 + 3 files changed, 16 insertions(+), 9 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index a6e68f6298..77c02d1692 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -574,7 +574,7 @@ void ProcessFifoAllDistance() if (IsOnThread()) { while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && - fifo.CPReadWriteDistance && !AtBreakpoint()) + fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt()) Common::YieldCPU(); } bProcessFifoAllDistance = false; @@ -597,10 +597,12 @@ void SetCpStatusRegister() m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint; m_CPStatusReg.ReadIdle = (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ; - m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance || (IsOnThread() && PixelEngine::WaitingForPEInterrupt()); + m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance; m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark; m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark; - + + PixelEngine::ResumeWaitingForPEInterrupt(); + INFO_LOG(COMMANDPROCESSOR,"\t Read from STATUS_REGISTER : %04x", m_CPStatusReg.Hex); DEBUG_LOG(COMMANDPROCESSOR, "(r) status: iBP %s | fReadIdle %s | fCmdIdle %s | iOvF %s | iUndF %s" , m_CPStatusReg.Breakpoint ? "ON" : "OFF" @@ -609,6 +611,8 @@ void SetCpStatusRegister() , m_CPStatusReg.OverflowHiWatermark ? "ON" : "OFF" , m_CPStatusReg.UnderflowLoWatermark ? "ON" : "OFF" ); + + } void SetCpControlRegister() diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 09cb9b8a5c..750e8fbb40 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -356,22 +356,16 @@ void UpdateInterrupts() void UpdateTokenInterrupt(bool active) { - if(interruptSetToken != active) - { ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active); interruptSetToken = active; - } } void UpdateFinishInterrupt(bool active) { - if(interruptSetFinish != active) - { ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active); interruptSetFinish = active; if (active) State::ProcessRequestedStates(0); - } } // TODO(mb2): Refactor SetTokenINT_OnMainThread(u64 userdata, int cyclesLate). @@ -473,4 +467,12 @@ bool WaitingForPEInterrupt() { return CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || interruptSetFinish || interruptSetToken; } + +void ResumeWaitingForPEInterrupt() +{ + interruptSetFinish = false; + interruptSetToken = false; + CommandProcessor::interruptFinishWaiting = false; + CommandProcessor::interruptTokenWaiting = false; +} } // end of namespace PixelEngine diff --git a/Source/Core/VideoCommon/Src/PixelEngine.h b/Source/Core/VideoCommon/Src/PixelEngine.h index 5e64300ef3..64f959009f 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.h +++ b/Source/Core/VideoCommon/Src/PixelEngine.h @@ -81,6 +81,7 @@ void SetFinish(void); void ResetSetFinish(void); void ResetSetToken(void); bool WaitingForPEInterrupt(); +void ResumeWaitingForPEInterrupt(); // Bounding box functionality. Paper Mario (both) are a couple of the few games that use it. extern u16 bbox[4]; -- cgit v1.2.3 From fedf6055ce2d1ac9862eac8c2c112160b8d7fbe8 Mon Sep 17 00:00:00 2001 From: marcosvitali Date: Sun, 11 Mar 2012 12:40:39 -0300 Subject: I've fixed Super Monkey Ball in some cases when the game write the WriteReadDistance need to be safe like the SafeCPRead. This fix is not related with the previous commits, but the previous commits help me to see that because in the new scenery SMB was hanging. May be in the past also doesn't boot some times because of that. Please Test FZero boot also. Thanks. --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index 77c02d1692..dbf4907160 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -171,11 +171,23 @@ void Read16(u16& _rReturnValue, const u32 _Address) case FIFO_LO_WATERMARK_HI: _rReturnValue = ReadHigh(fifo.CPLoWatermark); return; case FIFO_RW_DISTANCE_LO: + if (IsOnThread()) + if(fifo.CPWritePointer >= fifo.SafeCPReadPointer) + _rReturnValue = ReadLow (fifo.CPWritePointer - fifo.SafeCPReadPointer); + else + _rReturnValue = ReadLow (fifo.CPEnd - fifo.CPWritePointer + fifo.SafeCPReadPointer); + else _rReturnValue = ReadLow (fifo.CPReadWriteDistance); DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_RW_DISTANCE_LO : %04x", _rReturnValue); return; case FIFO_RW_DISTANCE_HI: - _rReturnValue = ReadHigh(fifo.CPReadWriteDistance); + if (IsOnThread()) + if(fifo.CPWritePointer >= fifo.SafeCPReadPointer) + _rReturnValue = ReadHigh (fifo.CPWritePointer - fifo.SafeCPReadPointer); + else + _rReturnValue = ReadHigh (fifo.CPEnd - fifo.CPWritePointer + fifo.SafeCPReadPointer); + else + _rReturnValue = ReadHigh(fifo.CPReadWriteDistance); DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_RW_DISTANCE_HI : %04x", _rReturnValue); return; case FIFO_WRITE_POINTER_LO: @@ -356,6 +368,7 @@ void Write16(const u16 _Value, const u32 _Address) break; case FIFO_READ_POINTER_HI: WriteHigh((u32 &)fifo.CPReadPointer, _Value); + fifo.SafeCPReadPointer = fifo.CPReadPointer; DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_READ_POINTER_HI : %04x", _Value); break; @@ -596,7 +609,7 @@ void SetCpStatusRegister() // Here always there is one fifo attached to the GPU m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint; - m_CPStatusReg.ReadIdle = (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ; + m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ; m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance; m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark; m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark; -- cgit v1.2.3 From dc79d68e72f04f60366484f60bff1e39d7e6babe Mon Sep 17 00:00:00 2001 From: skidau Date: Tue, 13 Mar 2012 22:35:11 +1100 Subject: Added the corresponding change from r352ab2ba4394 into JITIL. Tidied some code. --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 34 ++++++++---------------- Source/Core/VideoCommon/Src/CommandProcessor.h | 1 - Source/Core/VideoCommon/Src/PixelEngine.cpp | 3 --- 3 files changed, 11 insertions(+), 27 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index dbf4907160..3744acd200 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -141,7 +141,6 @@ void Init() void Read16(u16& _rReturnValue, const u32 _Address) { - INFO_LOG(COMMANDPROCESSOR, "(r): 0x%08x", _Address); switch (_Address & 0xFFF) { @@ -177,7 +176,7 @@ void Read16(u16& _rReturnValue, const u32 _Address) else _rReturnValue = ReadLow (fifo.CPEnd - fifo.CPWritePointer + fifo.SafeCPReadPointer); else - _rReturnValue = ReadLow (fifo.CPReadWriteDistance); + _rReturnValue = ReadLow (fifo.CPReadWriteDistance); DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_RW_DISTANCE_LO : %04x", _rReturnValue); return; case FIFO_RW_DISTANCE_HI: @@ -187,7 +186,7 @@ void Read16(u16& _rReturnValue, const u32 _Address) else _rReturnValue = ReadHigh (fifo.CPEnd - fifo.CPWritePointer + fifo.SafeCPReadPointer); else - _rReturnValue = ReadHigh(fifo.CPReadWriteDistance); + _rReturnValue = ReadHigh(fifo.CPReadWriteDistance); DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_RW_DISTANCE_HI : %04x", _rReturnValue); return; case FIFO_WRITE_POINTER_LO: @@ -499,18 +498,15 @@ void AbortFrame() void SetOverflowStatusFromGatherPipe() { - - fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark); isHiWatermarkActive = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt && m_CPCtrlReg.GPReadEnable; - + if (isHiWatermarkActive) { interruptSet = true; INFO_LOG(COMMANDPROCESSOR,"Interrupt set"); - ProcessorInterface::SetInterrupt(INT_CAUSE_CP, true); + ProcessorInterface::SetInterrupt(INT_CAUSE_CP, true); } - } void SetCpStatus() @@ -520,12 +516,10 @@ void SetCpStatus() fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark); // breakpoint - if (fifo.bFF_BPEnable) { if (fifo.CPBreakpoint == fifo.CPReadPointer) - { - + { if (!fifo.bFF_Breakpoint) { INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i", fifo.CPReadPointer); @@ -607,15 +601,16 @@ void Shutdown() void SetCpStatusRegister() { // Here always there is one fifo attached to the GPU - m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint; m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ; m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance; m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark; m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark; - - PixelEngine::ResumeWaitingForPEInterrupt(); - + + // HACK to compensate for slow response to PE interrupts in Time Splitters: Future Perfect + if (IsOnThread()) + PixelEngine::ResumeWaitingForPEInterrupt(); + INFO_LOG(COMMANDPROCESSOR,"\t Read from STATUS_REGISTER : %04x", m_CPStatusReg.Hex); DEBUG_LOG(COMMANDPROCESSOR, "(r) status: iBP %s | fReadIdle %s | fCmdIdle %s | iOvF %s | iUndF %s" , m_CPStatusReg.Breakpoint ? "ON" : "OFF" @@ -624,13 +619,10 @@ void SetCpStatusRegister() , m_CPStatusReg.OverflowHiWatermark ? "ON" : "OFF" , m_CPStatusReg.UnderflowLoWatermark ? "ON" : "OFF" ); - - } void SetCpControlRegister() { - // If the new fifo is being attached We make sure there wont be SetFinish event pending. // This protection fix eternal darkness booting, because the second SetFinish event when it is booting // seems invalid or has a bug and hang the game. @@ -638,7 +630,7 @@ void SetCpControlRegister() if (!fifo.bFF_GPReadEnable && m_CPCtrlReg.GPReadEnable && !m_CPCtrlReg.BPEnable) { ProcessFifoEvents(); - PixelEngine::ResetSetFinish(); + PixelEngine::ResetSetFinish(); } fifo.bFF_BPInt = m_CPCtrlReg.BPInt; @@ -653,9 +645,6 @@ void SetCpControlRegister() ProcessorInterface::Fifo_CPUBase = fifo.CPBase; ProcessorInterface::Fifo_CPUEnd = fifo.CPEnd; } - // If overflown happens process the fifo to LoWatemark - //if (bProcessFifoToLoWatermark) - // ProcessFifoToLoWatermark(); if(fifo.bFF_GPReadEnable && !m_CPCtrlReg.GPReadEnable) { @@ -667,7 +656,6 @@ void SetCpControlRegister() fifo.bFF_GPReadEnable = m_CPCtrlReg.GPReadEnable; } - DEBUG_LOG(COMMANDPROCESSOR, "\t GPREAD %s | BP %s | Int %s | OvF %s | UndF %s | LINK %s" , fifo.bFF_GPReadEnable ? "ON" : "OFF" , fifo.bFF_BPEnable ? "ON" : "OFF" diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.h b/Source/Core/VideoCommon/Src/CommandProcessor.h index 2bf965a3ba..440c900dc3 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.h +++ b/Source/Core/VideoCommon/Src/CommandProcessor.h @@ -25,7 +25,6 @@ class PointerWrap; extern bool MT; - namespace CommandProcessor { diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 750e8fbb40..38c85cc10c 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -322,7 +322,6 @@ void Write16(const u16 _iValue, const u32 _iAddress) break; case PE_TOKEN_REG: - //LOG(PIXELENGINE,"WEIRD: program wrote token: %i",_iValue); PanicAlert("(w16) WTF? PowerPC program wrote token: %i", _iValue); //only the gx pipeline is supposed to be able to write here //g_token = _iValue; @@ -384,8 +383,6 @@ void SetToken_OnMainThread(u64 userdata, int cyclesLate) CommandProcessor::interruptTokenWaiting = false; IncrementCheckContextId(); //} - //else - // LOGV(PIXELENGINE, 1, "VIDEO Backend wrote token: %i", CommandProcessor::fifo.PEToken); } void SetFinish_OnMainThread(u64 userdata, int cyclesLate) -- cgit v1.2.3 From 20eca1bf7e2e02dd18229dc2b6b83935ce0d4047 Mon Sep 17 00:00:00 2001 From: marcosvitali Date: Sun, 18 Mar 2012 22:54:58 -0300 Subject: Ive fixed definitely Pokemon XD in dual core mode. This game is doing something not allowed. It attach to CPU the same fifo attached to the GPU in multibuffer mode. I added a check to prevent overwrite the GPU FIFO with the CPU FIFO. If the game do that on breakpoint the solution can fail. Fixed ReadWriteDistance calc when CPRead > CPWrite. Added Token and Finish cause to GP Jit checking. --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 19 +++++++++++++++++-- Source/Core/VideoCommon/Src/CommandProcessor.h | 3 ++- Source/Core/VideoCommon/Src/PixelEngine.cpp | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index 3744acd200..a4dddf537f 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -61,6 +61,7 @@ volatile bool interruptSet= false; volatile bool interruptWaiting= false; volatile bool interruptTokenWaiting = false; volatile bool interruptFinishWaiting = false; +volatile bool waitingForPEInterruptDisable = false; bool IsOnThread() { @@ -174,7 +175,7 @@ void Read16(u16& _rReturnValue, const u32 _Address) if(fifo.CPWritePointer >= fifo.SafeCPReadPointer) _rReturnValue = ReadLow (fifo.CPWritePointer - fifo.SafeCPReadPointer); else - _rReturnValue = ReadLow (fifo.CPEnd - fifo.CPWritePointer + fifo.SafeCPReadPointer); + _rReturnValue = ReadLow (fifo.CPEnd - fifo.SafeCPReadPointer + fifo.CPWritePointer - fifo.CPBase + 32); else _rReturnValue = ReadLow (fifo.CPReadWriteDistance); DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_RW_DISTANCE_LO : %04x", _rReturnValue); @@ -184,7 +185,7 @@ void Read16(u16& _rReturnValue, const u32 _Address) if(fifo.CPWritePointer >= fifo.SafeCPReadPointer) _rReturnValue = ReadHigh (fifo.CPWritePointer - fifo.SafeCPReadPointer); else - _rReturnValue = ReadHigh (fifo.CPEnd - fifo.CPWritePointer + fifo.SafeCPReadPointer); + _rReturnValue = ReadHigh (fifo.CPEnd - fifo.SafeCPReadPointer + fifo.CPWritePointer - fifo.CPBase + 32); else _rReturnValue = ReadHigh(fifo.CPReadWriteDistance); DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_RW_DISTANCE_HI : %04x", _rReturnValue); @@ -437,11 +438,25 @@ void Write32(const u32 _Data, const u32 _Address) void STACKALIGN GatherPipeBursted() { + ProcessFifoEvents(); // if we aren't linked, we don't care about gather pipe data if (!m_CPCtrlReg.GPLinkEnable) { if (!IsOnThread()) RunGpu(); + else + { + // In multibuffer mode is not allowed write in the same fifo attached to the GPU. + // Fix Pokemon XD in DC mode. + if((ProcessorInterface::Fifo_CPUEnd == fifo.CPEnd) && (ProcessorInterface::Fifo_CPUBase == fifo.CPBase) + && fifo.CPReadWriteDistance > 0) + { + waitingForPEInterruptDisable = true; + ProcessFifoAllDistance(); + waitingForPEInterruptDisable = false; + } + + } return; } diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.h b/Source/Core/VideoCommon/Src/CommandProcessor.h index 440c900dc3..5d31453537 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.h +++ b/Source/Core/VideoCommon/Src/CommandProcessor.h @@ -35,7 +35,8 @@ extern volatile bool interruptSet; extern volatile bool interruptWaiting; extern volatile bool interruptTokenWaiting; extern volatile bool interruptFinishWaiting; - +extern volatile bool waitingForPEInterruptDisable; + // internal hardware addresses enum { diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 38c85cc10c..e6dbdb8c26 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -462,7 +462,7 @@ void ResetSetToken() bool WaitingForPEInterrupt() { - return CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || interruptSetFinish || interruptSetToken; + return !CommandProcessor::waitingForPEInterruptDisable && (CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || interruptSetFinish || interruptSetToken); } void ResumeWaitingForPEInterrupt() -- cgit v1.2.3 From 6202714efdf66987e57846d3297a5972c8b75c38 Mon Sep 17 00:00:00 2001 From: NeoBrainX Date: Tue, 20 Mar 2012 22:36:21 +0100 Subject: Fix some regressions from r3b38295cbd08. --- Source/Core/VideoCommon/Src/TextureCacheBase.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'Source/Core/VideoCommon') diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index cc14499330..00ff9610be 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -172,8 +172,10 @@ void TextureCache::ClearRenderTargets() TexCache::iterator iter = textures.begin(), tcend = textures.end(); + for (; iter!=tcend; ++iter) - iter->second->type = TCET_NORMAL; + if (iter->second->type != TCET_EC_DYNAMIC) + iter->second->type = TCET_NORMAL; } TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, @@ -238,6 +240,9 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, // 2. a) For EFB copies, only the hash and the texture address need to match if (entry->IsEfbCopy() && tex_hash == entry->hash && address == entry->addr) { + if (entry->type != TCET_EC_VRAM) + entry->type = TCET_NORMAL; + // TODO: Print a warning if the format changes! In this case, we could reinterpret the internal texture object data to the new pixel format (similiar to what is already being done in Renderer::ReinterpretPixelFormat()) goto return_entry; } @@ -318,8 +323,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int stage, entry->SetGeneralParameters(address, texture_size, full_format, entry->num_mipmaps); entry->SetDimensions(nativeW, nativeH, width, height); entry->hash = tex_hash; - if (g_ActiveConfig.bCopyEFBToTexture) entry->type = TCET_NORMAL; - else if (entry->IsEfbCopy()) entry->type = TCET_EC_DYNAMIC; + if (entry->IsEfbCopy() && !g_ActiveConfig.bCopyEFBToTexture) entry->type = TCET_EC_DYNAMIC; + else entry->type = TCET_NORMAL; // load texture entry->Load(width, height, expandedWidth, 0, (texLevels == 0)); @@ -647,8 +652,11 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat if ((entry->type == TCET_EC_VRAM && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h) || (entry->type == TCET_EC_DYNAMIC && entry->native_width == tex_w && entry->native_height == tex_h)) { - scaled_tex_w = tex_w; - scaled_tex_h = tex_h; + if (entry->type == TCET_EC_DYNAMIC) + { + scaled_tex_w = tex_w; + scaled_tex_h = tex_h; + } } else { -- cgit v1.2.3