DX11: Introduce a StateManager class, might improve performance a little.
[dolphin.git] / Source / Plugins / Plugin_VideoDX11 / Src / PixelShaderCache.cpp
blobf9ece84176aaebb9f71f978f4d3c94db25f1f00b
1 // Copyright (C) 2003 Dolphin Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official SVN repository and contact information can be found at
16 // http://code.google.com/p/dolphin-emu/
18 #include "Common.h"
19 #include "FileUtil.h"
20 #include "LinearDiskCache.h"
22 #include "Globals.h"
23 #include "D3DBase.h"
24 #include "D3Dcompiler.h"
25 #include "D3DShader.h"
26 #include "Statistics.h"
27 #include "VideoConfig.h"
28 #include "PixelShaderGen.h"
29 #include "PixelShaderManager.h"
30 #include "PixelShaderCache.h"
31 #include "VertexLoader.h"
32 #include "BPMemory.h"
33 #include "XFMemory.h"
34 #include "ImageWrite.h"
36 extern int frameCount;
38 PixelShaderCache::PSCache PixelShaderCache::PixelShaders;
39 const PixelShaderCache::PSCacheEntry* PixelShaderCache::last_entry;
41 LinearDiskCache g_ps_disk_cache;
43 ID3D11PixelShader* s_ColorMatrixProgram = NULL;
44 ID3D11PixelShader* s_ColorCopyProgram = NULL;
45 ID3D11PixelShader* s_DepthMatrixProgram = NULL;
46 ID3D11PixelShader* s_ClearProgram = NULL;
48 const char clear_program_code[] = {
49 "void main(\n"
50 "out float4 ocol0 : SV_Target,\n"
51 "in float4 pos : SV_Position,\n"
52 "in float4 incol0 : COLOR0){\n"
53 "ocol0 = incol0;\n"
54 "}\n"
57 const char color_copy_program_code[] = {
58 "sampler samp0 : register(s0);\n"
59 "Texture2D Tex0 : register(t0);\n"
60 "void main(\n"
61 "out float4 ocol0 : SV_Target,\n"
62 "in float4 pos : SV_Position,\n"
63 "in float2 uv0 : TEXCOORD0){\n"
64 "ocol0 = Tex0.Sample(samp0,uv0);\n"
65 "}\n"
68 const char color_matrix_program_code[] = {
69 "sampler samp0 : register(s0);\n"
70 "Texture2D Tex0 : register(t0);\n"
71 "uniform float4 cColMatrix[5] : register(c0);\n"
72 "void main(\n"
73 "out float4 ocol0 : SV_Target,\n"
74 "in float4 pos : SV_Position,\n"
75 " in float2 uv0 : TEXCOORD0){\n"
76 "float4 texcol = Tex0.Sample(samp0,uv0);\n"
77 "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n"
78 "}\n"
81 const char depth_matrix_program[] = {
82 "sampler samp0 : register(s0);\n"
83 "Texture2D Tex0 : register(t0);\n"
84 "uniform float4 cColMatrix[5] : register(c0);\n"
85 "void main(\n"
86 "out float4 ocol0 : SV_Target,\n"
87 " in float4 pos : SV_Position,\n"
88 " in float2 uv0 : TEXCOORD0){\n"
89 "float4 texcol = Tex0.Sample(samp0,uv0);\n"
90 "float4 EncodedDepth = frac((texcol.r * (16777215.0f/16777216.0f)) * float4(1.0f,255.0f,255.0f*255.0f,255.0f*255.0f*255.0f));\n"
91 "texcol = float4((EncodedDepth.rgb * (16777216.0f/16777215.0f)),1.0f);\n"
92 "ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n"
93 "}\n"
96 ID3D11PixelShader* PixelShaderCache::GetColorMatrixProgram()
98 return s_ColorMatrixProgram;
101 ID3D11PixelShader* PixelShaderCache::GetDepthMatrixProgram()
103 return s_DepthMatrixProgram;
106 ID3D11PixelShader* PixelShaderCache::GetColorCopyProgram()
108 return s_ColorCopyProgram;
111 ID3D11PixelShader* PixelShaderCache::GetClearProgram()
113 return s_ClearProgram;
116 // HACK to avoid some invasive VideoCommon changes
117 // these values are hardcoded, they depend on internal D3DCompile behavior; TODO: Solve this with D3DReflect or something
118 // offset given in floats, table index is float4
119 unsigned int ps_constant_offset_table[] = {
120 0, 4, 8, 12, // C_COLORS, 16
121 16, 20, 24, 28, // C_KCOLORS, 16
122 32, // C_ALPHA, 4
123 36, 40, 44, 48, 52, 56, 60, 64, // C_TEXDIMS, 32
124 68, 72, // C_ZBIAS, 8
125 76, 80, // C_INDTEXSCALE, 8
126 84, 88, 92, 96, 100, 104, // C_INDTEXMTX, 24
127 108, 112, // C_FOG, 8
129 void SetPSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4)
131 D3D::gfxstate->psconstants[ps_constant_offset_table[const_number] ] = f1;
132 D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]+1] = f2;
133 D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]+2] = f3;
134 D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]+3] = f4;
135 D3D::gfxstate->pscbufchanged = true;
138 void SetPSConstant4fv(unsigned int const_number, const float* f)
140 memcpy(&D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]], f, sizeof(float)*4);
141 D3D::gfxstate->pscbufchanged = true;
144 void SetMultiPSConstant4fv(unsigned int const_number, unsigned int count, const float* f)
146 memcpy(&D3D::gfxstate->psconstants[ps_constant_offset_table[const_number]], f, sizeof(float)*4*count);
147 D3D::gfxstate->pscbufchanged = true;
150 // this class will load the precompiled shaders into our cache
151 class PixelShaderCacheInserter : public LinearDiskCacheReader {
152 public:
153 void Read(const u8* key, int key_size, const u8* value, int value_size)
155 PIXELSHADERUID uid;
156 if (key_size != sizeof(uid)) {
157 ERROR_LOG(VIDEO, "Wrong key size in pixel shader cache");
158 return;
160 memcpy(&uid, key, key_size);
161 PixelShaderCache::InsertByteCode(uid, (void*)value, value_size);
165 void PixelShaderCache::Init()
167 // used when drawing clear quads
168 s_ClearProgram = D3D::CompileAndCreatePixelShader(clear_program_code, sizeof(clear_program_code));
169 CHECK(s_ClearProgram!=NULL, "Create clear pixel shader");
170 D3D::SetDebugObjectName((ID3D11DeviceChild*)s_ClearProgram, "clear pixel shader");
172 // used when copying/resolving the color buffer
173 s_ColorCopyProgram = D3D::CompileAndCreatePixelShader(color_copy_program_code, sizeof(color_copy_program_code));
174 CHECK(s_ColorCopyProgram!=NULL, "Create color copy pixel shader");
175 D3D::SetDebugObjectName((ID3D11DeviceChild*)s_ClearProgram, "color copy pixel shader");
177 // used for color conversion
178 s_ColorMatrixProgram = D3D::CompileAndCreatePixelShader(color_matrix_program_code, sizeof(color_matrix_program_code));
179 CHECK(s_ColorMatrixProgram!=NULL, "Create color matrix pixel shader");
180 D3D::SetDebugObjectName((ID3D11DeviceChild*)s_ClearProgram, "color matrix pixel shader");
182 // used for depth copy
183 s_DepthMatrixProgram = D3D::CompileAndCreatePixelShader(depth_matrix_program, sizeof(depth_matrix_program));
184 CHECK(s_DepthMatrixProgram!=NULL, "Create depth matrix pixel shader");
185 D3D::SetDebugObjectName((ID3D11DeviceChild*)s_ClearProgram, "depth matrix pixel shader");
187 Clear();
189 if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX)))
190 File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX));
192 char cache_filename[MAX_PATH];
193 sprintf(cache_filename, "%sdx11-%s-ps.cache", File::GetUserPath(D_SHADERCACHE_IDX), globals->unique_id);
194 PixelShaderCacheInserter inserter;
195 g_ps_disk_cache.OpenAndRead(cache_filename, &inserter);
198 // ONLY to be used during shutdown.
199 void PixelShaderCache::Clear()
201 for (PSCache::iterator iter = PixelShaders.begin(); iter != PixelShaders.end(); iter++)
202 iter->second.Destroy();
203 PixelShaders.clear();
206 void PixelShaderCache::Shutdown()
208 SAFE_RELEASE(s_ColorMatrixProgram);
209 SAFE_RELEASE(s_ColorCopyProgram);
210 SAFE_RELEASE(s_DepthMatrixProgram);
211 SAFE_RELEASE(s_ClearProgram);
213 Clear();
214 g_ps_disk_cache.Sync();
215 g_ps_disk_cache.Close();
218 bool PixelShaderCache::SetShader(bool dstAlpha)
220 PIXELSHADERUID uid;
221 GetPixelShaderId(&uid, PixelShaderManager::GetTextureMask(), dstAlpha);
223 // check if the shader is already set
224 if (uid == last_pixel_shader_uid && PixelShaders[uid].frameCount == frameCount)
226 PSCache::const_iterator iter = PixelShaders.find(uid);
227 return (iter != PixelShaders.end() && iter->second.shader);
230 memcpy(&last_pixel_shader_uid, &uid, sizeof(PIXELSHADERUID));
232 // check if the shader is already in the cache
233 PSCache::iterator iter;
234 iter = PixelShaders.find(uid);
235 if (iter != PixelShaders.end())
237 iter->second.frameCount = frameCount;
238 const PSCacheEntry &entry = iter->second;
239 last_entry = &entry;
241 D3D::gfxstate->SetPShader(entry.shader);
242 return (entry.shader != NULL);
245 // need to compile a new shader
246 const char* code = GeneratePixelShaderCode(PixelShaderManager::GetTextureMask(), dstAlpha, API_D3D11);
248 D3DBlob* pbytecode;
249 if (!D3D::CompilePixelShader(code, strlen(code), &pbytecode))
251 PanicAlert("Failed to compile Pixel Shader:\n\n%s", code);
252 return false;
255 // insert the bytecode into the caches
256 g_ps_disk_cache.Append((u8*)&uid, sizeof(uid), (const u8*)pbytecode->Data(), pbytecode->Size());
257 g_ps_disk_cache.Sync();
259 bool result = InsertByteCode(uid, pbytecode->Data(), pbytecode->Size());
260 D3D::gfxstate->SetPShader(last_entry->shader);
261 pbytecode->Release();
262 return result;
265 bool PixelShaderCache::InsertByteCode(const PIXELSHADERUID &uid, void* bytecode, unsigned int bytecodelen)
267 ID3D11PixelShader* shader = D3D::CreatePixelShaderFromByteCode(bytecode, bytecodelen);
268 if (shader == NULL)
270 PanicAlert("Failed to create pixel shader at %s %d\n", __FILE__, __LINE__);
271 return false;
273 // TODO: Somehow make the debug name a bit more specific
274 D3D::SetDebugObjectName((ID3D11DeviceChild*)shader, "a pixel shader of PixelShaderCache");
276 // make an entry in the table
277 PSCacheEntry newentry;
278 newentry.shader = shader;
279 newentry.frameCount = frameCount;
280 PixelShaders[uid] = newentry;
281 last_entry = &PixelShaders[uid];
283 INCSTAT(stats.numPixelShadersCreated);
284 SETSTAT(stats.numPixelShadersAlive, PixelShaders.size());
286 return true;