1 // Copyright (c) 2013- 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 #pragma once
19 #include <unordered_map>
20 
21 #include "Common/CommonTypes.h"
22 #include "Common/Swap.h"
23 #include "GPU/Math3D.h"
24 #include "GPU/ge_constants.h"
25 #include "Core/Config.h"
26 
27 #define HALF_CEIL(x) (x + 1) / 2 // Integer ceil = (int)ceil((float)x / 2.0f)
28 
29 // PSP compatible format so we can use the end of the pipeline in beziers etc
30 struct SimpleVertex {
31 	float uv[2];
32 	union {
33 		u8 color[4];
34 		u32_le color_32;
35 	};
36 	Vec3Packedf nrm;
37 	Vec3Packedf pos;
38 };
39 
40 class SimpleBufferManager;
41 
42 namespace Spline {
43 
44 void BuildIndex(u16 *indices, int &count, int num_u, int num_v, GEPatchPrimType prim_type, int total = 0);
45 
46 enum SplineQuality {
47 	LOW_QUALITY = 0,
48 	MEDIUM_QUALITY = 1,
49 	HIGH_QUALITY = 2,
50 };
51 
52 class Bezier3DWeight;
53 class Spline3DWeight;
54 
55 // We decode all vertices into a common format for easy interpolation and stuff.
56 // Not fast but can be optimized later.
57 
58 struct SurfaceInfo {
59 	int tess_u, tess_v;
60 	int num_points_u, num_points_v;
61 	int num_patches_u, num_patches_v;
62 	int type_u, type_v;
63 	GEPatchPrimType primType;
64 	bool patchFacing;
65 
66 	void Init() {
67 		// If specified as 0, uses 1.
68 		if (tess_u < 1) tess_u = 1;
69 		if (tess_v < 1) tess_v = 1;
70 
71 		switch (g_Config.iSplineBezierQuality) {
72 		case LOW_QUALITY:
73 			tess_u = 2;
74 			tess_v = 2;
75 			break;
76 		case MEDIUM_QUALITY:
77 			// Don't cut below 2, though.
78 			if (tess_u > 2) tess_u = HALF_CEIL(tess_u);
79 			if (tess_v > 2) tess_v = HALF_CEIL(tess_v);
80 			break;
81 		}
82 	}
83 };
84 
85 struct BezierSurface : public SurfaceInfo {
86 	using WeightType = Bezier3DWeight;
87 
88 	int num_verts_per_patch;
89 
90 	void Init(int maxVertices) {
91 		SurfaceInfo::Init();
92 		// Downsample until it fits, in case crazy tessellation factors are sent.
93 		while ((tess_u + 1) * (tess_v + 1) * num_patches_u * num_patches_v > maxVertices) {
94 			tess_u--;
95 			tess_v--;
96 		}
97 		num_verts_per_patch = (tess_u + 1) * (tess_v + 1);
98 	}
99 
100 	int GetTessStart(int patch) const { return 0; }
101 
102 	int GetPointIndex(int patch_u, int patch_v) const { return patch_v * 3 * num_points_u + patch_u * 3; }
103 
104 	int GetIndexU(int patch_u, int tile_u) const { return tile_u; }
105 	int GetIndexV(int patch_v, int tile_v) const { return tile_v; }
106 
107 	int GetIndex(int index_u, int index_v, int patch_u, int patch_v) const {
108 		int patch_index = patch_v * num_patches_u + patch_u;
109 		return index_v * (tess_u + 1) + index_u + num_verts_per_patch * patch_index;
110 	}
111 
112 	void BuildIndex(u16 *indices, int &count) const {
113 		for (int patch_u = 0; patch_u < num_patches_u; ++patch_u) {
114 			for (int patch_v = 0; patch_v < num_patches_v; ++patch_v) {
115 				int patch_index = patch_v * num_patches_u + patch_u;
116 				int total = patch_index * num_verts_per_patch;
117 				Spline::BuildIndex(indices + count, count, tess_u, tess_v, primType, total);
118 			}
119 		}
120 	}
121 };
122 
123 struct SplineSurface : public SurfaceInfo {
124 	using WeightType = Spline3DWeight;
125 
126 	int num_vertices_u;
127 
128 	void Init(int maxVertices) {
129 		SurfaceInfo::Init();
130 		// Downsample until it fits, in case crazy tessellation factors are sent.
131 		while ((num_patches_u * tess_u + 1) * (num_patches_v * tess_v + 1) > maxVertices) {
132 			tess_u--;
133 			tess_v--;
134 		}
135 		num_vertices_u = num_patches_u * tess_u + 1;
136 	}
137 
138 	int GetTessStart(int patch) const { return (patch == 0) ? 0 : 1; }
139 
140 	int GetPointIndex(int patch_u, int patch_v) const { return patch_v * num_points_u + patch_u; }
141 
142 	int GetIndexU(int patch_u, int tile_u) const { return patch_u * tess_u + tile_u; }
143 	int GetIndexV(int patch_v, int tile_v) const { return patch_v * tess_v + tile_v; }
144 
145 	int GetIndex(int index_u, int index_v, int patch_u, int patch_v) const {
146 		return index_v * num_vertices_u + index_u;
147 	}
148 
149 	void BuildIndex(u16 *indices, int &count) const {
150 		Spline::BuildIndex(indices, count, num_patches_u * tess_u, num_patches_v * tess_v, primType);
151 	}
152 };
153 
154 struct Weight {
155 	float basis[4], deriv[4];
156 };
157 
158 template<class T>
159 class WeightCache : public T {
160 private:
161 	std::unordered_map<u32, Weight*> weightsCache;
162 public:
163 	Weight* operator [] (u32 key) {
164 		Weight *&weights = weightsCache[key];
165 		if (!weights)
166 			weights = T::CalcWeightsAll(key);
167 		return weights;
168 	}
169 
170 	void Clear() {
171 		for (auto it : weightsCache)
172 			delete[] it.second;
173 		weightsCache.clear();
174 	}
175 };
176 
177 struct Weight2D {
178 	const Weight *u, *v;
179 	int size_u, size_v;
180 
181 	template<class T>
182 	Weight2D(WeightCache<T> &cache, u32 key_u, u32 key_v) {
183 		u = cache[key_u];
184 		v = (key_u != key_v) ? cache[key_v] : u; // Use same weights if u == v
185 	}
186 };
187 
188 struct ControlPoints {
189 	Vec3f *pos;
190 	Vec2f *tex;
191 	Vec4f *col;
192 	u32_le defcolor;
193 
194 	ControlPoints() {}
195 	ControlPoints(const SimpleVertex *const *points, int size, SimpleBufferManager &managedBuf);
196 	void Convert(const SimpleVertex *const *points, int size);
197 };
198 
199 struct OutputBuffers {
200 	SimpleVertex *vertices;
201 	u16 *indices;
202 	int count;
203 };
204 
205 template<class Surface>
206 void SoftwareTessellation(OutputBuffers &output, const Surface &surface, u32 origVertType, const ControlPoints &points);
207 
208 } // namespace Spline
209 
210 // Define function object for TemplateParameterDispatcher
211 #define TEMPLATE_PARAMETER_DISPATCHER_FUNCTION(NAME, FUNCNAME, FUNCTYPE) \
212 struct NAME { \
213 	template<bool ...Params> \
214 	static FUNCTYPE GetFunc() { \
215 		return &FUNCNAME<Params...>; \
216 	} \
217 };
218 
219 template<typename Func, int NumParams, class Dispatcher>
220 class TemplateParameterDispatcher {
221 
222 	/* Store all combinations of template functions into an array */
223 	template<int LoopCount, int Index = 0, bool ...Params>
224 	struct Initializer {
225 		static void Init(Func funcs[]) {
226 			Initializer<LoopCount - 1, (Index << 1) + 1, true, Params...>::Init(funcs); // true
227 			Initializer<LoopCount - 1, (Index << 1) + 0, false, Params...>::Init(funcs); // false
228 		}
229 	};
230  	/* Specialized for terminates the recursive loop */
231 	template<int Index, bool ...Params>
232 	struct Initializer<0, Index, Params...> {
233 		static void Init(Func funcs[]) {
234 			funcs[Index] = Dispatcher::template GetFunc<Params...>(); // Resolve the nested dependent name as template function.
235 		}
236 	};
237 
238 private:
239 	Func funcs[1 << NumParams]; /* Function pointers array */
240 public:
241 	TemplateParameterDispatcher() {
242 		Initializer<NumParams>::Init(funcs);
243 	}
244 
245 	Func GetFunc(const bool params[]) const {
246  		/* Convert bool parameters to index of the array */
247 		int index = 0;
248 		for (int i = 0; i < NumParams; ++i)
249 			index |= params[i] << i;
250 
251 		return funcs[index];
252 	}
253 };
254