1 // See comment in header for the purpose of the code in this file.
2 
3 #include <algorithm>
4 #include <cmath>
5 
6 #include "Common/Data/Convert/ColorConv.h"
7 #include "Common/Profiler/Profiler.h"
8 
9 #include "Core/Config.h"
10 #include "Core/MemMap.h"
11 #include "Core/Reporting.h"
12 #include "Core/System.h"
13 #include "GPU/GPUState.h"
14 
15 #include "GPU/Common/TextureCacheCommon.h"
16 #include "GPU/Software/SoftGpu.h"
17 #include "GPU/Software/Rasterizer.h"
18 #include "GPU/Software/Sampler.h"
19 
20 #if defined(_M_SSE)
21 #include <emmintrin.h>
22 #endif
23 
24 extern DSStretch g_DarkStalkerStretch;
25 // For Darkstalkers hack. Ugh.
26 extern bool currentDialogActive;
27 
28 namespace Rasterizer {
29 
30 // Through mode, with the specific Darkstalker settings.
DrawSinglePixel5551(u16 * pixel,const u32 color_in)31 inline void DrawSinglePixel5551(u16 *pixel, const u32 color_in) {
32 	u32 new_color;
33 	if ((color_in >> 24) == 255) {
34 		new_color = color_in & 0xFFFFFF;
35 	} else {
36 		const u32 old_color = RGBA5551ToRGBA8888(*pixel);
37 		const Vec4<int> dst = Vec4<int>::FromRGBA(old_color);
38 		Vec3<int> blended = AlphaBlendingResult(Vec4<int>::FromRGBA(color_in), dst);
39 		// ToRGB() always automatically clamps.
40 		new_color = blended.ToRGB();
41 	}
42 	new_color |= (*pixel & 0x8000) ? 0xff000000 : 0x00000000;
43 	*pixel = RGBA8888ToRGBA5551(new_color);
44 }
45 
ModulateRGBA(const Vec4<int> & prim_color,const Vec4<int> & texcolor)46 static inline Vec4<int> ModulateRGBA(const Vec4<int>& prim_color, const Vec4<int>& texcolor) {
47 	Vec3<int> out_rgb;
48 	int out_a;
49 
50 #if defined(_M_SSE)
51 	// We can be accurate up to 24 bit integers, should be enough.
52 	const __m128 p = _mm_cvtepi32_ps(prim_color.ivec);
53 	const __m128 t = _mm_cvtepi32_ps(texcolor.ivec);
54 	const __m128 b = _mm_mul_ps(p, t);
55 	if (gstate.isColorDoublingEnabled()) {
56 		// We double right here, only for modulate.  Other tex funcs do not color double.
57 		const __m128 doubleColor = _mm_setr_ps(2.0f / 255.0f, 2.0f / 255.0f, 2.0f / 255.0f, 1.0f / 255.0f);
58 		out_rgb.ivec = _mm_cvtps_epi32(_mm_mul_ps(b, doubleColor));
59 	} else {
60 		out_rgb.ivec = _mm_cvtps_epi32(_mm_mul_ps(b, _mm_set_ps1(1.0f / 255.0f)));
61 	}
62 	return Vec4<int>(out_rgb.ivec);
63 #else
64 	if (gstate.isColorDoublingEnabled()) {
65 		out_rgb = (prim_color.rgb() * texcolor.rgb() * 2) / 255;
66 	} else {
67 		out_rgb = prim_color.rgb() * texcolor.rgb() / 255;
68 	}
69 	out_a = (prim_color.a() * texcolor.a() / 255);
70 #endif
71 
72 	return Vec4<int>(out_rgb.r(), out_rgb.g(), out_rgb.b(), out_a);
73 
74 }
75 
DrawSprite(const VertexData & v0,const VertexData & v1)76 void DrawSprite(const VertexData& v0, const VertexData& v1) {
77 	const u8 *texptr = nullptr;
78 
79 	GETextureFormat texfmt = gstate.getTextureFormat();
80 	u32 texaddr = gstate.getTextureAddress(0);
81 	int texbufw = GetTextureBufw(0, texaddr, texfmt);
82 	if (Memory::IsValidAddress(texaddr))
83 		texptr = Memory::GetPointerUnchecked(texaddr);
84 
85 	ScreenCoords pprime(v0.screenpos.x, v0.screenpos.y, 0);
86 	Sampler::NearestFunc nearestFunc = Sampler::GetNearestFunc();  // Looks at gstate.
87 
88 	DrawingCoords pos0 = TransformUnit::ScreenToDrawing(v0.screenpos);
89 	// Include the ending pixel based on its center, not start.
90 	DrawingCoords pos1 = TransformUnit::ScreenToDrawing(v1.screenpos + ScreenCoords(7, 7, 0));
91 
92 	DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1(), 0);
93 	DrawingCoords scissorBR(gstate.getScissorX2(), gstate.getScissorY2(), 0);
94 
95 	int z = pos0.z;
96 	float fog = 1.0f;
97 
98 	bool isWhite = v1.color0 == Vec4<int>(255, 255, 255, 255);
99 
100 	if (gstate.isTextureMapEnabled()) {
101 		// 1:1 (but with mirror support) texture mapping!
102 		int s_start = v0.texturecoords.x;
103 		int t_start = v0.texturecoords.y;
104 		int ds = v1.texturecoords.x > v0.texturecoords.x ? 1 : -1;
105 		int dt = v1.texturecoords.y > v0.texturecoords.y ? 1 : -1;
106 
107 		if (ds < 0) {
108 			s_start += ds;
109 		}
110 		if (dt < 0) {
111 			t_start += dt;
112 		}
113 
114 		// First clip the right and bottom sides, since we don't need to adjust the deltas.
115 		if (pos1.x > scissorBR.x) pos1.x = scissorBR.x + 1;
116 		if (pos1.y > scissorBR.y) pos1.y = scissorBR.y + 1;
117 		// Now clip the other sides.
118 		if (pos0.x < scissorTL.x) {
119 			s_start += (scissorTL.x - pos0.x) * ds;
120 			pos0.x = scissorTL.x;
121 		}
122 		if (pos0.y < scissorTL.y) {
123 			t_start += (scissorTL.y - pos0.y) * dt;
124 			pos0.y = scissorTL.y;
125 		}
126 
127 		if (!gstate.isStencilTestEnabled() &&
128 			!gstate.isDepthTestEnabled() &&
129 			!gstate.isLogicOpEnabled() &&
130 			!gstate.isColorTestEnabled() &&
131 			!gstate.isDitherEnabled() &&
132 			gstate.isAlphaTestEnabled() &&
133 			gstate.getAlphaTestRef() == 0 &&
134 			gstate.getAlphaTestMask() == 0xFF &&
135 			gstate.isAlphaBlendEnabled() &&
136 			gstate.isTextureAlphaUsed() &&
137 			gstate.getTextureFunction() == GE_TEXFUNC_MODULATE &&
138 			gstate.getColorMask() == 0x000000 &&
139 			gstate.FrameBufFormat() == GE_FORMAT_5551) {
140 			int t = t_start;
141 			for (int y = pos0.y; y < pos1.y; y++) {
142 				int s = s_start;
143 				u16 *pixel = fb.Get16Ptr(pos0.x, y, gstate.FrameBufStride());
144 				if (isWhite) {
145 					for (int x = pos0.x; x < pos1.x; x++) {
146 						u32 tex_color = nearestFunc(s, t, texptr, texbufw, 0);
147 						if (tex_color & 0xFF000000) {
148 							DrawSinglePixel5551(pixel, tex_color);
149 						}
150 						s += ds;
151 						pixel++;
152 					}
153 				} else {
154 					for (int x = pos0.x; x < pos1.x; x++) {
155 						Vec4<int> prim_color = v1.color0;
156 						Vec4<int> tex_color = Vec4<int>::FromRGBA(nearestFunc(s, t, texptr, texbufw, 0));
157 						prim_color = ModulateRGBA(prim_color, tex_color);
158 						if (prim_color.a() > 0) {
159 							DrawSinglePixel5551(pixel, prim_color.ToRGBA());
160 						}
161 						s += ds;
162 						pixel++;
163 					}
164 				}
165 				t += dt;
166 			}
167 		} else {
168 			int t = t_start;
169 			for (int y = pos0.y; y < pos1.y; y++) {
170 				int s = s_start;
171 				// Not really that fast but faster than triangle.
172 				for (int x = pos0.x; x < pos1.x; x++) {
173 					Vec4<int> prim_color = v1.color0;
174 					Vec4<int> tex_color = Vec4<int>::FromRGBA(nearestFunc(s, t, texptr, texbufw, 0));
175 					prim_color = GetTextureFunctionOutput(prim_color, tex_color);
176 					DrawingCoords pos(x, y, z);
177 					DrawSinglePixelNonClear(pos, (u16)z, 1.0f, prim_color);
178 					s += ds;
179 				}
180 				t += dt;
181 			}
182 		}
183 	} else {
184 		if (pos1.x > scissorBR.x) pos1.x = scissorBR.x + 1;
185 		if (pos1.y > scissorBR.y) pos1.y = scissorBR.y + 1;
186 		if (pos0.x < scissorTL.x) pos0.x = scissorTL.x;
187 		if (pos0.y < scissorTL.y) pos0.y = scissorTL.y;
188 		if (!gstate.isStencilTestEnabled() &&
189 			!gstate.isDepthTestEnabled() &&
190 			!gstate.isLogicOpEnabled() &&
191 			!gstate.isColorTestEnabled() &&
192 			!gstate.isDitherEnabled() &&
193 			gstate.isAlphaTestEnabled() &&
194 			gstate.getAlphaTestRef() == 0 &&
195 			gstate.getAlphaTestMask() == 0xFF &&
196 			gstate.isAlphaBlendEnabled() &&
197 			gstate.isTextureAlphaUsed() &&
198 			gstate.getTextureFunction() == GE_TEXFUNC_MODULATE &&
199 			gstate.getColorMask() == 0x000000 &&
200 			gstate.FrameBufFormat() == GE_FORMAT_5551) {
201 			if (v1.color0.a() == 0)
202 				return;
203 
204 			for (int y = pos0.y; y < pos1.y; y++) {
205 				u16 *pixel = fb.Get16Ptr(pos0.x, y, gstate.FrameBufStride());
206 				for (int x = pos0.x; x < pos1.x; x++) {
207 					Vec4<int> prim_color = v1.color0;
208 					DrawSinglePixel5551(pixel, prim_color.ToRGBA());
209 					pixel++;
210 				}
211 			}
212 		} else {
213 			for (int y = pos0.y; y < pos1.y; y++) {
214 				for (int x = pos0.x; x < pos1.x; x++) {
215 					Vec4<int> prim_color = v1.color0;
216 					DrawingCoords pos(x, y, z);
217 					DrawSinglePixelNonClear(pos, (u16)z, fog, prim_color);
218 				}
219 			}
220 		}
221 	}
222 }
223 
224 bool g_needsClearAfterDialog = false;
225 
NoClampOrWrap(const Vec2f & tc)226 static inline bool NoClampOrWrap(const Vec2f &tc) {
227 	if (tc.x < 0 || tc.y < 0)
228 		return false;
229 	return tc.x <= gstate.getTextureWidth(0) && tc.y <= gstate.getTextureHeight(0);
230 }
231 
232 // Returns true if the normal path should be skipped.
RectangleFastPath(const VertexData & v0,const VertexData & v1)233 bool RectangleFastPath(const VertexData &v0, const VertexData &v1) {
234 	g_DarkStalkerStretch = DSStretch::Off;
235 	// Check for 1:1 texture mapping. In that case we can call DrawSprite.
236 	int xdiff = v1.screenpos.x - v0.screenpos.x;
237 	int ydiff = v1.screenpos.y - v0.screenpos.y;
238 	int udiff = (v1.texturecoords.x - v0.texturecoords.x) * 16.0f;
239 	int vdiff = (v1.texturecoords.y - v0.texturecoords.y) * 16.0f;
240 	bool coord_check =
241 		(xdiff == udiff || xdiff == -udiff) &&
242 		(ydiff == vdiff || ydiff == -vdiff);
243 	// Currently only works for TL/BR, which is the most common but not required.
244 	bool orient_check = xdiff >= 0 && ydiff >= 0;
245 	// We already have a fast path for clear in ClearRectangle.
246 	bool state_check = !gstate.isModeClear() && NoClampOrWrap(v0.texturecoords) && NoClampOrWrap(v1.texturecoords);
247 	if ((coord_check || !gstate.isTextureMapEnabled()) && orient_check && state_check) {
248 		Rasterizer::DrawSprite(v0, v1);
249 		return true;
250 	}
251 
252 	// Eliminate the stretch blit in DarkStalkers.
253 	// We compensate for that when blitting the framebuffer in SoftGpu.cpp.
254 	if (PSP_CoreParameter().compat.flags().DarkStalkersPresentHack && v0.texturecoords.x == 64.0f && v0.texturecoords.y == 16.0f && v1.texturecoords.x == 448.0f && v1.texturecoords.y == 240.0f) {
255 		// check for save/load dialog.
256 		if (!currentDialogActive) {
257 			if (v0.screenpos.x == 0x7100 && v0.screenpos.y == 0x7780 && v1.screenpos.x == 0x8f00 && v1.screenpos.y == 0x8880) {
258 				g_DarkStalkerStretch = DSStretch::Wide;
259 			} else if (v0.screenpos.x == 0x7400 && v0.screenpos.y == 0x7780 && v1.screenpos.x == 0x8C00 && v1.screenpos.y == 0x8880) {
260 				g_DarkStalkerStretch = DSStretch::Normal;
261 			} else {
262 				return false;
263 			}
264 			if (g_needsClearAfterDialog) {
265 				g_needsClearAfterDialog = false;
266 				// Afterwards, we also need to clear the actual destination. Can do a fast rectfill.
267 				gstate.textureMapEnable &= ~1;
268 				VertexData newV1 = v1;
269 				newV1.color0 = Vec4<int>(0, 0, 0, 255);
270 				Rasterizer::DrawSprite(v0, newV1);
271 				gstate.textureMapEnable |= 1;
272 			}
273 			return true;
274 		} else {
275 			g_needsClearAfterDialog = true;
276 		}
277 	}
278 	return false;
279 }
280 
DetectRectangleFromThroughModeStrip(const VertexData data[4])281 bool DetectRectangleFromThroughModeStrip(const VertexData data[4]) {
282 	// OK, now let's look at data to detect rectangles. There are a few possibilities
283 	// but we focus on Darkstalkers for now.
284 	if (data[0].screenpos.x == data[1].screenpos.x &&
285 		data[0].screenpos.y == data[2].screenpos.y &&
286 		data[2].screenpos.x == data[3].screenpos.x &&
287 		data[1].screenpos.y == data[3].screenpos.y &&
288 		data[1].screenpos.y > data[0].screenpos.y &&  // Avoid rotation handling
289 		data[2].screenpos.x > data[0].screenpos.x &&
290 		data[0].texturecoords.x == data[1].texturecoords.x &&
291 		data[0].texturecoords.y == data[2].texturecoords.y &&
292 		data[2].texturecoords.x == data[3].texturecoords.x &&
293 		data[1].texturecoords.y == data[3].texturecoords.y &&
294 		data[1].texturecoords.y > data[0].texturecoords.y &&
295 		data[2].texturecoords.x > data[0].texturecoords.x &&
296 		data[0].color0 == data[1].color0 &&
297 		data[1].color0 == data[2].color0 &&
298 		data[2].color0 == data[3].color0) {
299 		// It's a rectangle!
300 		return true;
301 	}
302 	// There's the other vertex order too...
303 	if (data[0].screenpos.x == data[2].screenpos.x &&
304 		data[0].screenpos.y == data[1].screenpos.y &&
305 		data[1].screenpos.x == data[3].screenpos.x &&
306 		data[2].screenpos.y == data[3].screenpos.y &&
307 		data[2].screenpos.y > data[0].screenpos.y &&  // Avoid rotation handling
308 		data[1].screenpos.x > data[0].screenpos.x &&
309 		data[0].texturecoords.x == data[2].texturecoords.x &&
310 		data[0].texturecoords.y == data[1].texturecoords.y &&
311 		data[1].texturecoords.x == data[3].texturecoords.x &&
312 		data[2].texturecoords.y == data[3].texturecoords.y &&
313 		data[2].texturecoords.y > data[0].texturecoords.y &&
314 		data[1].texturecoords.x > data[0].texturecoords.x &&
315 		data[0].color0 == data[1].color0 &&
316 		data[1].color0 == data[2].color0 &&
317 		data[2].color0 == data[3].color0) {
318 		// It's a rectangle!
319 		return true;
320 	}
321 	return false;
322 }
323 
324 
325 }  // namespace Rasterizer
326 
327