1 // Copyright (c) 2014- PPSSPP Project.
2 
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 or later versions.
6 
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.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 #include <map>
19 #include <d3d9.h>
20 
21 #include "Common/GPU/D3D9/D3D9ShaderCompiler.h"
22 #include "Common/GPU/thin3d.h"
23 #include "Common/Log.h"
24 #include "Common/StringUtils.h"
25 #include "Core/Reporting.h"
26 #include "GPU/Directx9/TextureCacheDX9.h"
27 #include "GPU/Directx9/DepalettizeShaderDX9.h"
28 #include "GPU/Common/DepalettizeShaderCommon.h"
29 
30 namespace DX9 {
31 
32 static const int DEPAL_TEXTURE_OLD_AGE = 120;
33 
34 #ifdef _WIN32
35 #define SHADERLOG
36 #endif
37 
38 static const char *depalVShaderHLSL = R"(
39 struct VS_IN {
40   float3 a_position : POSITION;
41   float2 a_texcoord0 : TEXCOORD0;
42 };
43 struct VS_OUT {
44   float4 Position : POSITION;
45   float2 Texcoord : TEXCOORD0;
46 };
47 VS_OUT main(VS_IN input) {
48   VS_OUT output;
49   output.Texcoord = input.a_texcoord0;
50   output.Position = float4(input.a_position, 1.0);
51   return output;
52 }
53 )";
54 
55 DepalShaderCacheDX9::DepalShaderCacheDX9(Draw::DrawContext *draw) {
56 	device_ = (LPDIRECT3DDEVICE9)draw->GetNativeObject(Draw::NativeObject::DEVICE);
57 	std::string errorMessage;
58 	if (!CompileVertexShaderD3D9(device_, depalVShaderHLSL, &vertexShader_, &errorMessage)) {
59 		ERROR_LOG(G3D, "error compling depal vshader: %s", errorMessage.c_str());
60 	}
61 }
62 
63 DepalShaderCacheDX9::~DepalShaderCacheDX9() {
64 	Clear();
65 	if (vertexShader_) {
66 		vertexShader_->Release();
67 	}
68 }
69 
70 LPDIRECT3DTEXTURE9 DepalShaderCacheDX9::GetClutTexture(GEPaletteFormat clutFormat, u32 clutHash, u32 *rawClut) {
71 	u32 clutId = GetClutID(clutFormat, clutHash);
72 
73 	auto oldtex = texCache_.find(clutId);
74 	if (oldtex != texCache_.end()) {
75 		oldtex->second->lastFrame = gpuStats.numFlips;
76 		return oldtex->second->texture;
77 	}
78 
79 	D3DFORMAT dstFmt = DX9::getClutDestFormat(clutFormat);
80 	int texturePixels = clutFormat == GE_CMODE_32BIT_ABGR8888 ? 256 : 512;
81 
82 	DepalTextureDX9 *tex = new DepalTextureDX9();
83 
84 	// Create texture
85 	D3DPOOL pool = D3DPOOL_MANAGED;
86 	int usage = 0;
87 	if (device_) {
88 		pool = D3DPOOL_DEFAULT;
89 		usage = D3DUSAGE_DYNAMIC;  // TODO: Switch to using a staging texture?
90 	}
91 
92 	HRESULT hr = device_->CreateTexture(texturePixels, 1, 1, usage, dstFmt, pool, &tex->texture, NULL);
93 	if (FAILED(hr)) {
94 		ERROR_LOG(G3D, "Failed to create D3D texture for depal");
95 		delete tex;
96 		return nullptr;
97 	}
98 
99 	D3DLOCKED_RECT rect;
100 	hr = tex->texture->LockRect(0, &rect, NULL, 0);
101 	if (FAILED(hr)) {
102 		ERROR_LOG(G3D, "Failed to lock D3D texture for depal");
103 		delete tex;
104 		return nullptr;
105 	}
106 	// Regardless of format, the CLUT should always be 1024 bytes.
107 	memcpy(rect.pBits, rawClut, 1024);
108 	tex->texture->UnlockRect(0);
109 
110 	device_->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
111 	device_->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
112 	device_->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_POINT);
113 	device_->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
114 
115 	tex->lastFrame = gpuStats.numFlips;
116 	texCache_[clutId] = tex;
117 	return tex->texture;
118 }
119 
120 void DepalShaderCacheDX9::Clear() {
121 	for (auto shader = cache_.begin(); shader != cache_.end(); ++shader) {
122 		shader->second->pixelShader->Release();
123 		delete shader->second;
124 	}
125 	cache_.clear();
126 	for (auto tex = texCache_.begin(); tex != texCache_.end(); ++tex) {
127 		tex->second->texture->Release();
128 		delete tex->second;
129 	}
130 	texCache_.clear();
131 }
132 
133 void DepalShaderCacheDX9::Decimate() {
134 	for (auto tex = texCache_.begin(); tex != texCache_.end();) {
135 		if (tex->second->lastFrame + DEPAL_TEXTURE_OLD_AGE < gpuStats.numFlips) {
136 			tex->second->texture->Release();
137 			delete tex->second;
138 			texCache_.erase(tex++);
139 		} else {
140 			++tex;
141 		}
142 	}
143 }
144 
145 LPDIRECT3DPIXELSHADER9 DepalShaderCacheDX9::GetDepalettizePixelShader(uint32_t clutMode, GEBufferFormat pixelFormat) {
146 	u32 id = GenerateShaderID(clutMode, pixelFormat);
147 
148 	auto shader = cache_.find(id);
149 	if (shader != cache_.end()) {
150 		return shader->second->pixelShader;
151 	}
152 
153 	char *buffer = new char[2048];
154 
155 	GenerateDepalShader(buffer, pixelFormat, HLSL_D3D9);
156 
157 	LPDIRECT3DPIXELSHADER9 pshader;
158 	std::string errorMessage;
159 	if (!CompilePixelShaderD3D9(device_, buffer, &pshader, &errorMessage)) {
160 		ERROR_LOG(G3D, "Failed to compile depal pixel shader: %s\n\n%s", buffer, errorMessage.c_str());
161 		delete[] buffer;
162 		return nullptr;
163 	}
164 
165 	DepalShaderDX9 *depal = new DepalShaderDX9();
166 	depal->pixelShader = pshader;
167 	depal->code = buffer;
168 
169 	cache_[id] = depal;
170 
171 	delete[] buffer;
172 
173 	return depal->pixelShader;
174 }
175 
176 std::vector<std::string> DepalShaderCacheDX9::DebugGetShaderIDs(DebugShaderType type) {
177 	std::vector<std::string> ids;
178 	for (auto &iter : cache_) {
179 		ids.push_back(StringFromFormat("%08x", iter.first));
180 	}
181 	return ids;
182 }
183 
184 std::string DepalShaderCacheDX9::DebugGetShaderString(std::string idstr, DebugShaderType type, DebugShaderStringType stringType) {
185 	uint32_t id;
186 	sscanf(idstr.c_str(), "%08x", &id);
187 	auto iter = cache_.find(id);
188 	if (iter == cache_.end())
189 		return "";
190 	switch (stringType) {
191 	case SHADER_STRING_SHORT_DESC:
192 		return idstr;
193 	case SHADER_STRING_SOURCE_CODE:
194 		return iter->second->code;
195 	default:
196 		return "";
197 	}
198 }
199 
200 }  // namespace
201