summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp
blob: 29172f91a1e40d36555ad12a702e39917d3c247e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.

#include <string>

#include "Common/FileUtil.h"
#include "Common/LinearDiskCache.h"
#include "Common/StringUtil.h"

#include "Core/ConfigManager.h"

#include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/D3DShader.h"
#include "VideoBackends/D3D/GeometryShaderCache.h"
#include "VideoBackends/D3D/Globals.h"

#include "VideoCommon/Debugger.h"
#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/VideoConfig.h"

namespace DX11
{

GeometryShaderCache::GSCache GeometryShaderCache::GeometryShaders;
const GeometryShaderCache::GSCacheEntry* GeometryShaderCache::last_entry;
GeometryShaderUid GeometryShaderCache::last_uid;
UidChecker<GeometryShaderUid,ShaderCode> GeometryShaderCache::geometry_uid_checker;

ID3D11GeometryShader* ClearGeometryShader = nullptr;
ID3D11GeometryShader* CopyGeometryShader = nullptr;

LinearDiskCache<GeometryShaderUid, u8> g_gs_disk_cache;

ID3D11GeometryShader* GeometryShaderCache::GetClearGeometryShader() { return ClearGeometryShader; }
ID3D11GeometryShader* GeometryShaderCache::GetCopyGeometryShader() { return CopyGeometryShader; }

ID3D11Buffer* gscbuf = nullptr;

// this class will load the precompiled shaders into our cache
class GeometryShaderCacheInserter : public LinearDiskCacheReader<GeometryShaderUid, u8>
{
public:
	void Read(const GeometryShaderUid &key, const u8* value, u32 value_size)
	{
		GeometryShaderCache::InsertByteCode(key, value, value_size);
	}
};

const char clear_shader_code[] = {
	"struct VSOUTPUT\n"
	"{\n"
	"float4 vPosition   : POSITION;\n"
	"float4 vColor0   : COLOR0;\n"
	"};\n"
	"struct GSOUTPUT\n"
	"{\n"
	"float4 vPosition   : POSITION;\n"
	"float4 vColor0   : COLOR0;\n"
	"uint slice    : SV_RenderTargetArrayIndex;\n"
	"};\n"
	"[maxvertexcount(6)]\n"
	"void main(triangle VSOUTPUT o[3], inout TriangleStream<GSOUTPUT> Output)\n"
	"{\n"
	"for(int slice = 0; slice < 2; slice++)\n"
	"{\n"
	"for(int i = 0; i < 3; i++)\n"
	"{\n"
	"GSOUTPUT OUT;\n"
	"OUT.vPosition = o[i].vPosition;\n"
	"OUT.vColor0 = o[i].vColor0;\n"
	"OUT.slice = slice;\n"
	"Output.Append(OUT);\n"
	"}\n"
	"Output.RestartStrip();\n"
	"}\n"
	"}\n"
};

const char copy_shader_code[] = {
	"struct VSOUTPUT\n"
	"{\n"
	"float4 vPosition : POSITION;\n"
	"float3 vTexCoord : TEXCOORD0;\n"
	"float  vTexCoord1 : TEXCOORD1;\n"
	"};\n"
	"struct GSOUTPUT\n"
	"{\n"
	"float4 vPosition : POSITION;\n"
	"float3 vTexCoord : TEXCOORD0;\n"
	"float  vTexCoord1 : TEXCOORD1;\n"
	"uint slice    : SV_RenderTargetArrayIndex;\n"
	"};\n"
	"[maxvertexcount(6)]\n"
	"void main(triangle VSOUTPUT o[3], inout TriangleStream<GSOUTPUT> Output)\n"
	"{\n"
	"for(int slice = 0; slice < 2; slice++)\n"
	"{\n"
	"for(int i = 0; i < 3; i++)\n"
	"{\n"
	"GSOUTPUT OUT;\n"
	"OUT.vPosition = o[i].vPosition;\n"
	"OUT.vTexCoord = o[i].vTexCoord;\n"
	"OUT.vTexCoord.z = slice;\n"
	"OUT.vTexCoord1 = o[i].vTexCoord1;\n"
	"OUT.slice = slice;\n"
	"Output.Append(OUT);\n"
	"}\n"
	"Output.RestartStrip();\n"
	"}\n"
	"}\n"
};

void GeometryShaderCache::Init()
{
	// used when drawing clear quads
	ClearGeometryShader = D3D::CompileAndCreateGeometryShader(clear_shader_code);
	CHECK(ClearGeometryShader != nullptr, "Create clear geometry shader");
	D3D::SetDebugObjectName((ID3D11DeviceChild*)ClearGeometryShader, "clear geometry shader");

	// used for buffer copy
	CopyGeometryShader = D3D::CompileAndCreateGeometryShader(copy_shader_code);
	CHECK(CopyGeometryShader != nullptr, "Create copy geometry shader");
	D3D::SetDebugObjectName((ID3D11DeviceChild*)CopyGeometryShader, "copy geometry shader");

	Clear();

	if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX)))
		File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX));

	std::string cache_filename = StringFromFormat("%sdx11-%s-gs.cache", File::GetUserPath(D_SHADERCACHE_IDX).c_str(),
			SConfig::GetInstance().m_LocalCoreStartupParameter.m_strUniqueID.c_str());
	GeometryShaderCacheInserter inserter;
	g_gs_disk_cache.OpenAndRead(cache_filename, inserter);

	if (g_Config.bEnableShaderDebugging)
		Clear();

	last_entry = nullptr;
}

// ONLY to be used during shutdown.
void GeometryShaderCache::Clear()
{
	for (auto& iter : GeometryShaders)
		iter.second.Destroy();
	GeometryShaders.clear();
	geometry_uid_checker.Invalidate();

	last_entry = nullptr;
}

void GeometryShaderCache::Shutdown()
{
	SAFE_RELEASE(ClearGeometryShader);
	SAFE_RELEASE(CopyGeometryShader);

	Clear();
	g_gs_disk_cache.Sync();
	g_gs_disk_cache.Close();
}

bool GeometryShaderCache::SetShader(u32 components)
{
	GeometryShaderUid uid;
	GetGeometryShaderUid(uid, components, API_D3D);
	if (g_ActiveConfig.bEnableShaderDebugging)
	{
		ShaderCode code;
		GenerateGeometryShaderCode(code, components, API_D3D);
		geometry_uid_checker.AddToIndexAndCheck(code, uid, "Geometry", "g");
	}

	// Check if the shader is already set
	if (last_entry)
	{
		if (uid == last_uid)
		{
			GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE,true);
			return (last_entry->shader != nullptr);
		}
	}

	last_uid = uid;

	// Check if the shader is already in the cache
	GSCache::iterator iter;
	iter = GeometryShaders.find(uid);
	if (iter != GeometryShaders.end())
	{
		const GSCacheEntry &entry = iter->second;
		last_entry = &entry;

		return (entry.shader != nullptr);
	}

	// Need to compile a new shader
	ShaderCode code;
	GenerateGeometryShaderCode(code, components, API_D3D);

	D3DBlob* pbytecode;
	if (!D3D::CompileGeometryShader(code.GetBuffer(), &pbytecode))
	{
		GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true);
		return false;
	}

	// Insert the bytecode into the caches
	g_gs_disk_cache.Append(uid, pbytecode->Data(), pbytecode->Size());

	bool success = InsertByteCode(uid, pbytecode->Data(), pbytecode->Size());
	pbytecode->Release();

	if (g_ActiveConfig.bEnableShaderDebugging && success)
	{
		GeometryShaders[uid].code = code.GetBuffer();
	}

	return success;
}

bool GeometryShaderCache::InsertByteCode(const GeometryShaderUid &uid, const void* bytecode, unsigned int bytecodelen)
{
	ID3D11GeometryShader* shader = D3D::CreateGeometryShaderFromByteCode(bytecode, bytecodelen);
	if (shader == nullptr)
		return false;

	// TODO: Somehow make the debug name a bit more specific
	D3D::SetDebugObjectName((ID3D11DeviceChild*)shader, "a pixel shader of GeometryShaderCache");

	// Make an entry in the table
	GSCacheEntry newentry;
	newentry.shader = shader;
	GeometryShaders[uid] = newentry;
	last_entry = &GeometryShaders[uid];

	if (!shader)
		return false;

	return true;
}

}  // DX11