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
DepalShaderCacheDX9(Draw::DrawContext * draw)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
~DepalShaderCacheDX9()63 DepalShaderCacheDX9::~DepalShaderCacheDX9() {
64 Clear();
65 if (vertexShader_) {
66 vertexShader_->Release();
67 }
68 }
69
GetClutTexture(GEPaletteFormat clutFormat,u32 clutHash,u32 * rawClut)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
Clear()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
Decimate()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
GetDepalettizePixelShader(uint32_t clutMode,GEBufferFormat pixelFormat)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
DebugGetShaderIDs(DebugShaderType type)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
DebugGetShaderString(std::string idstr,DebugShaderType type,DebugShaderStringType stringType)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