1 #include <assert.h>
2 #include <sys/stat.h>
3 #include "math/Matrix4.h"
4 #include "GSH_Direct3D9.h"
5 #include "../../gs/GsPixelFormats.h"
6 
SetupTextureUpdaters()7 void CGSH_Direct3D9::SetupTextureUpdaters()
8 {
9 	for(unsigned int i = 0; i < PSM_MAX; i++)
10 	{
11 		m_textureUpdater[i] = &CGSH_Direct3D9::TexUpdater_Invalid;
12 	}
13 
14 	m_textureUpdater[PSMCT32] = &CGSH_Direct3D9::TexUpdater_Psm32;
15 	m_textureUpdater[PSMCT24] = &CGSH_Direct3D9::TexUpdater_Psm32;
16 	m_textureUpdater[PSMCT16] = &CGSH_Direct3D9::TexUpdater_Psm16<CGsPixelFormats::CPixelIndexorPSMCT16>;
17 	m_textureUpdater[PSMCT16S] = &CGSH_Direct3D9::TexUpdater_Psm16<CGsPixelFormats::CPixelIndexorPSMCT16S>;
18 	m_textureUpdater[PSMT8] = &CGSH_Direct3D9::TexUpdater_Psm48<CGsPixelFormats::CPixelIndexorPSMT8>;
19 	m_textureUpdater[PSMT4] = &CGSH_Direct3D9::TexUpdater_Psm48<CGsPixelFormats::CPixelIndexorPSMT4>;
20 	m_textureUpdater[PSMT8H] = &CGSH_Direct3D9::TexUpdater_Psm48H<24, 0xFF>;
21 	m_textureUpdater[PSMT4HL] = &CGSH_Direct3D9::TexUpdater_Psm48H<24, 0x0F>;
22 	m_textureUpdater[PSMT4HH] = &CGSH_Direct3D9::TexUpdater_Psm48H<28, 0x0F>;
23 }
24 
LoadTexture(const TEX0 & tex0,uint32 maxMip,const MIPTBP1 & miptbp1,const MIPTBP2 & miptbp2)25 CGSH_Direct3D9::TEXTURE_INFO CGSH_Direct3D9::LoadTexture(const TEX0& tex0, uint32 maxMip, const MIPTBP1& miptbp1, const MIPTBP2& miptbp2)
26 {
27 	TEXTURE_INFO result;
28 
29 	{
30 		auto textureMatrix = CMatrix4::MakeIdentity();
31 		m_device->SetVertexShaderConstantF(VS_INDEX_TEXMATRIX, textureMatrix.coeff, 4);
32 	}
33 
34 	for(const auto& candidateFramebuffer : m_framebuffers)
35 	{
36 		if(candidateFramebuffer->m_basePtr == tex0.GetBufPtr() &&
37 		   candidateFramebuffer->m_width == tex0.GetBufWidth() &&
38 		   IsCompatibleFramebufferPSM(candidateFramebuffer->m_psm, tex0.nPsm) &&
39 		   candidateFramebuffer->m_canBeUsedAsTexture)
40 		{
41 			float scaleRatioX = static_cast<float>(tex0.GetWidth()) / static_cast<float>(candidateFramebuffer->m_width);
42 			float scaleRatioY = static_cast<float>(tex0.GetHeight()) / static_cast<float>(candidateFramebuffer->m_height);
43 
44 			{
45 				auto textureMatrix = CMatrix4::MakeScale(scaleRatioX, scaleRatioY, 1);
46 				m_device->SetVertexShaderConstantF(VS_INDEX_TEXMATRIX, textureMatrix.coeff, 4);
47 			}
48 
49 			result.texture = candidateFramebuffer->m_renderTarget;
50 			result.isRenderTarget = true;
51 			result.renderTargetWidth = candidateFramebuffer->m_width;
52 			result.renderTargetHeight = candidateFramebuffer->m_height;
53 
54 			return result;
55 		}
56 	}
57 
58 	HRESULT resultCode = S_OK;
59 
60 	auto texture = m_textureCache.Search(tex0);
61 	if(!texture)
62 	{
63 		uint32 width = tex0.GetWidth();
64 		uint32 height = tex0.GetHeight();
65 
66 		D3DFORMAT textureFormat = D3DFMT_A8R8G8B8;
67 		switch(tex0.nPsm)
68 		{
69 		case PSMCT32:
70 			textureFormat = D3DFMT_A8R8G8B8;
71 			break;
72 		case PSMCT24:
73 			textureFormat = D3DFMT_X8R8G8B8;
74 			break;
75 		case PSMCT16:
76 		case PSMCT16S:
77 			textureFormat = D3DFMT_A1R5G5B5;
78 			break;
79 		case PSMT8:
80 		case PSMT4:
81 		case PSMT8H:
82 		case PSMT4HL:
83 		case PSMT4HH:
84 			textureFormat = D3DFMT_L8;
85 			break;
86 		default:
87 			assert(false);
88 			break;
89 		}
90 
91 		{
92 			TexturePtr textureHandle;
93 			resultCode = m_device->CreateTexture(width, height, 1 + maxMip, D3DUSAGE_DYNAMIC, textureFormat, D3DPOOL_DEFAULT, &textureHandle, NULL);
94 			assert(SUCCEEDED(resultCode));
95 			m_textureCache.Insert(tex0, std::move(textureHandle));
96 		}
97 
98 		texture = m_textureCache.Search(tex0);
99 		texture->m_cachedArea.Invalidate(0, RAMSIZE);
100 	}
101 
102 	auto& cachedArea = texture->m_cachedArea;
103 
104 	if(cachedArea.HasDirtyPages())
105 	{
106 		D3DLOCKED_RECT lockedRect;
107 		resultCode = texture->m_textureHandle->LockRect(0, &lockedRect, nullptr, 0);
108 		assert(SUCCEEDED(resultCode));
109 
110 		auto texturePageSize = CGsPixelFormats::GetPsmPageSize(tex0.nPsm);
111 		auto areaRect = cachedArea.GetAreaPageRect();
112 
113 		for(unsigned int dirtyPageIndex = 0; dirtyPageIndex < CGsCachedArea::MAX_DIRTYPAGES; dirtyPageIndex++)
114 		{
115 			if(!cachedArea.IsPageDirty(dirtyPageIndex)) continue;
116 
117 			uint32 pageX = dirtyPageIndex % areaRect.width;
118 			uint32 pageY = dirtyPageIndex / areaRect.width;
119 			uint32 texX = pageX * texturePageSize.first;
120 			uint32 texY = pageY * texturePageSize.second;
121 			uint32 texWidth = texturePageSize.first;
122 			uint32 texHeight = texturePageSize.second;
123 			if(texX >= tex0.GetWidth()) continue;
124 			if(texY >= tex0.GetHeight()) continue;
125 			if((texX + texWidth) > tex0.GetWidth())
126 			{
127 				texWidth = tex0.GetWidth() - texX;
128 			}
129 			if((texY + texHeight) > tex0.GetHeight())
130 			{
131 				texHeight = tex0.GetHeight() - texY;
132 			}
133 			((this)->*(m_textureUpdater[tex0.nPsm]))(&lockedRect, tex0.GetBufPtr(), tex0.nBufWidth, texX, texY, texWidth, texHeight);
134 		}
135 
136 		cachedArea.ClearDirtyPages();
137 
138 		resultCode = texture->m_textureHandle->UnlockRect(0);
139 		assert(SUCCEEDED(resultCode));
140 
141 		//Update mipmap levels
142 		for(uint32 mipLevel = 1; mipLevel <= maxMip; mipLevel++)
143 		{
144 			uint32 mipLevelWidth = std::max<uint32>(tex0.GetWidth() >> mipLevel, 1);
145 			uint32 mipLevelHeight = std::max<uint32>(tex0.GetHeight() >> mipLevel, 1);
146 
147 			uint32 mipLevelBufferPointer = 0;
148 			uint32 mipLevelBufferWidth = 0;
149 			switch(mipLevel)
150 			{
151 			case 1:
152 				mipLevelBufferPointer = miptbp1.GetTbp1();
153 				mipLevelBufferWidth = miptbp1.GetTbw1();
154 				break;
155 			case 2:
156 				mipLevelBufferPointer = miptbp1.GetTbp2();
157 				mipLevelBufferWidth = miptbp1.GetTbw2();
158 				break;
159 			case 3:
160 				mipLevelBufferPointer = miptbp1.GetTbp3();
161 				mipLevelBufferWidth = miptbp1.GetTbw3();
162 				break;
163 			case 4:
164 				mipLevelBufferPointer = miptbp2.GetTbp4();
165 				mipLevelBufferWidth = miptbp2.GetTbw4();
166 				break;
167 			case 5:
168 				mipLevelBufferPointer = miptbp2.GetTbp5();
169 				mipLevelBufferWidth = miptbp2.GetTbw5();
170 				break;
171 			case 6:
172 				mipLevelBufferPointer = miptbp2.GetTbp6();
173 				mipLevelBufferWidth = miptbp2.GetTbw6();
174 				break;
175 			}
176 
177 			if(mipLevelBufferWidth == 0) break;
178 
179 			resultCode = texture->m_textureHandle->LockRect(mipLevel, &lockedRect, nullptr, 0);
180 			assert(SUCCEEDED(resultCode));
181 
182 			((this)->*(m_textureUpdater[tex0.nPsm]))(&lockedRect, mipLevelBufferPointer, mipLevelBufferWidth / 64, 0, 0, mipLevelWidth, mipLevelHeight);
183 
184 			resultCode = texture->m_textureHandle->UnlockRect(mipLevel);
185 			assert(SUCCEEDED(resultCode));
186 		}
187 	}
188 
189 	result.texture = texture->m_textureHandle;
190 	return result;
191 }
192 
Color_Ps2ToDx9(uint32 color)193 uint32 CGSH_Direct3D9::Color_Ps2ToDx9(uint32 color)
194 {
195 	return D3DCOLOR_ARGB(
196 	    (color >> 24) & 0xFF,
197 	    (color >> 0) & 0xFF,
198 	    (color >> 8) & 0xFF,
199 	    (color >> 16) & 0xFF);
200 }
201 
TexUpdater_Invalid(D3DLOCKED_RECT *,uint32,uint32,unsigned int,unsigned int,unsigned int,unsigned int)202 void CGSH_Direct3D9::TexUpdater_Invalid(D3DLOCKED_RECT*, uint32, uint32, unsigned int, unsigned int, unsigned int, unsigned int)
203 {
204 	assert(false);
205 }
206 
TexUpdater_Psm32(D3DLOCKED_RECT * lockedRect,uint32 bufPtr,uint32 bufWidth,unsigned int texX,unsigned int texY,unsigned int texWidth,unsigned int texHeight)207 void CGSH_Direct3D9::TexUpdater_Psm32(D3DLOCKED_RECT* lockedRect, uint32 bufPtr, uint32 bufWidth, unsigned int texX, unsigned int texY, unsigned int texWidth, unsigned int texHeight)
208 {
209 	CGsPixelFormats::CPixelIndexorPSMCT32 indexor(m_pRAM, bufPtr, bufWidth);
210 
211 	auto dstPitch = lockedRect->Pitch / 4;
212 	auto dst = reinterpret_cast<uint32*>(lockedRect->pBits);
213 	dst += texX + (texY * dstPitch);
214 
215 	for(unsigned int y = 0; y < texHeight; y++)
216 	{
217 		for(unsigned int x = 0; x < texWidth; x++)
218 		{
219 			uint32 color = indexor.GetPixel(texX + x, texY + y);
220 			dst[x] = Color_Ps2ToDx9(color);
221 		}
222 
223 		dst += dstPitch;
224 	}
225 }
226 
227 template <typename IndexorType>
TexUpdater_Psm16(D3DLOCKED_RECT * lockedRect,uint32 bufPtr,uint32 bufWidth,unsigned int texX,unsigned int texY,unsigned int texWidth,unsigned int texHeight)228 void CGSH_Direct3D9::TexUpdater_Psm16(D3DLOCKED_RECT* lockedRect, uint32 bufPtr, uint32 bufWidth, unsigned int texX, unsigned int texY, unsigned int texWidth, unsigned int texHeight)
229 {
230 	IndexorType indexor(m_pRAM, bufPtr, bufWidth);
231 
232 	auto dstPitch = lockedRect->Pitch / 2;
233 	auto dst = reinterpret_cast<uint16*>(lockedRect->pBits);
234 	dst += texX + (texY * dstPitch);
235 
236 	for(unsigned int y = 0; y < texHeight; y++)
237 	{
238 		for(unsigned int x = 0; x < texWidth; x++)
239 		{
240 			auto pixel = indexor.GetPixel(texX + x, texY + y);
241 			auto cvtPixel =
242 			    (((pixel & 0x001F) >> 0) << 10) | //R
243 			    (((pixel & 0x03E0) >> 5) << 5) |  //G
244 			    (((pixel & 0x7C00) >> 10) << 0) | //B
245 			    (((pixel & 0x8000) >> 15) << 15); //A
246 			dst[x] = cvtPixel;
247 		}
248 
249 		dst += dstPitch;
250 	}
251 }
252 
253 template <typename IndexorType>
TexUpdater_Psm48(D3DLOCKED_RECT * lockedRect,uint32 bufPtr,uint32 bufWidth,unsigned int texX,unsigned int texY,unsigned int texWidth,unsigned int texHeight)254 void CGSH_Direct3D9::TexUpdater_Psm48(D3DLOCKED_RECT* lockedRect, uint32 bufPtr, uint32 bufWidth, unsigned int texX, unsigned int texY, unsigned int texWidth, unsigned int texHeight)
255 {
256 	IndexorType indexor(m_pRAM, bufPtr, bufWidth);
257 
258 	auto dstPitch = lockedRect->Pitch;
259 	auto dst = reinterpret_cast<uint8*>(lockedRect->pBits);
260 	dst += texX + (texY * dstPitch);
261 
262 	for(unsigned int y = 0; y < texHeight; y++)
263 	{
264 		for(unsigned int x = 0; x < texWidth; x++)
265 		{
266 			uint8 pixel = indexor.GetPixel(texX + x, texY + y);
267 			dst[x] = pixel;
268 		}
269 
270 		dst += dstPitch;
271 	}
272 }
273 
274 template <uint32 shiftAmount, uint32 mask>
TexUpdater_Psm48H(D3DLOCKED_RECT * lockedRect,uint32 bufPtr,uint32 bufWidth,unsigned int texX,unsigned int texY,unsigned int texWidth,unsigned int texHeight)275 void CGSH_Direct3D9::TexUpdater_Psm48H(D3DLOCKED_RECT* lockedRect, uint32 bufPtr, uint32 bufWidth, unsigned int texX, unsigned int texY, unsigned int texWidth, unsigned int texHeight)
276 {
277 	CGsPixelFormats::CPixelIndexorPSMCT32 indexor(m_pRAM, bufPtr, bufWidth);
278 
279 	auto dstPitch = lockedRect->Pitch;
280 	auto dst = reinterpret_cast<uint8*>(lockedRect->pBits);
281 	dst += texX + (texY * dstPitch);
282 
283 	for(unsigned int y = 0; y < texHeight; y++)
284 	{
285 		for(unsigned int x = 0; x < texWidth; x++)
286 		{
287 			uint32 pixel = indexor.GetPixel(texX + x, texY + y);
288 			pixel = (pixel >> shiftAmount) & mask;
289 			dst[x] = static_cast<uint8>(pixel);
290 		}
291 
292 		dst += dstPitch;
293 	}
294 }
295 
296 //------------------------------------------------------------------------
297 //CLUT Stuff
298 //------------------------------------------------------------------------
299 
GetClutTexture(const TEX0 & tex0)300 CGSH_Direct3D9::TexturePtr CGSH_Direct3D9::GetClutTexture(const TEX0& tex0)
301 {
302 	HRESULT result = S_OK;
303 
304 	std::array<uint32, 256> convertedClut;
305 	MakeLinearCLUT(tex0, convertedClut);
306 
307 	unsigned int entryCount = CGsPixelFormats::IsPsmIDTEX4(tex0.nPsm) ? 16 : 256;
308 	auto clutTexture = CGsPixelFormats::IsPsmIDTEX4(tex0.nPsm) ? m_clutTexture4 : m_clutTexture8;
309 
310 	{
311 		D3DLOCKED_RECT rect;
312 		result = clutTexture->LockRect(0, &rect, NULL, D3DLOCK_DISCARD);
313 		assert(SUCCEEDED(result));
314 
315 		auto dst = reinterpret_cast<uint32*>(rect.pBits);
316 		unsigned int nDstPitch = rect.Pitch / 4;
317 
318 		for(uint32 i = 0; i < entryCount; i++)
319 		{
320 			dst[i] = Color_Ps2ToDx9(convertedClut[i]);
321 		}
322 
323 		result = clutTexture->UnlockRect(0);
324 		assert(SUCCEEDED(result));
325 	}
326 
327 	return clutTexture;
328 }
329