~ubuntu-branches/ubuntu/precise/frogatto/precise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
#include <iostream>
#include <map>
#include <SDL.h>
#ifndef SDL_VIDEO_OPENGL_ES
#include <GL/glew.h>
#endif

#include "asserts.hpp"
#include "concurrent_cache.hpp"
#include "filesystem.hpp"
#include "foreach.hpp"
#include "formula.hpp"
#include "formula_callable.hpp"
#include "formula_function.hpp"
#include "hi_res_timer.hpp"
#include "surface.hpp"
#include "surface_cache.hpp"
#include "surface_formula.hpp"
#include "unit_test.hpp"

using namespace graphics;
using namespace game_logic;

namespace {

typedef std::pair<surface, std::string> cache_key;
typedef concurrent_cache<cache_key, surface> cache_map;

cache_map& cache() {
	static cache_map instance;
	return instance;
}

class rgba_function : public function_expression {
public:
	explicit rgba_function(surface surf, const args_list& args)
	  : function_expression("rgba", args, 4), surf_(surf) {
	}

private:
	variant execute(const formula_callable& variables) const {
		return variant(SDL_MapRGBA(surf_->format,
		   Uint8(args()[0]->evaluate(variables).as_int()),
		   Uint8(args()[1]->evaluate(variables).as_int()),
		   Uint8(args()[2]->evaluate(variables).as_int()),
		   Uint8(args()[3]->evaluate(variables).as_int())));
	}
	surface surf_;
};

class surface_formula_symbol_table : public function_symbol_table
{
public:
	explicit surface_formula_symbol_table(surface surf) : surf_(surf)
	{}
	expression_ptr create_function(
	                  const std::string& fn,
	                  const std::vector<expression_ptr>& args,
					  const formula_callable_definition* callable_def) const;
private:
	surface surf_;
};

expression_ptr surface_formula_symbol_table::create_function(
                           const std::string& fn,
                           const std::vector<expression_ptr>& args,
						   const formula_callable_definition* callable_def) const
{
	if(fn == "rgba") {
		return expression_ptr(new rgba_function(surf_, args));
	} else {
		return function_symbol_table::create_function(fn, args, callable_def);
	}
}

class pixel_callable : public game_logic::formula_callable {
public:
	pixel_callable(const surface& surf, Uint32 pixel)
	{
		SDL_GetRGBA(pixel, surf->format, &r, &g, &b, &a);
		static const unsigned char AlphaPixel[] = {0x6f, 0x6d, 0x51};
		if(r == AlphaPixel[0] && g == AlphaPixel[1] && b == AlphaPixel[2]) {
			a = 0;
		}
	}

	bool is_alpha() const { return a == 0; }
private:
	variant get_value(const std::string& key) const {
		switch(*key.c_str()) {
		case 'r': return variant(r);
		case 'g': return variant(g);
		case 'b': return variant(b);
		case 'a': return variant(a);
		default: return variant();
		}
	}

	Uint8 r, g, b, a;
};

void run_formula(surface surf, const std::string& algo)
{
	const hi_res_timer timer("run_formula");

	const int ticks = SDL_GetTicks();
	surface_formula_symbol_table table(surf);
	game_logic::formula f(algo, &table);
	bool locked = false;
	if(SDL_MUSTLOCK(surf.get())) {
		const int res = SDL_LockSurface(surf.get());
		if(res == 0) {
			locked = true;
		}
	}

	std::map<Uint32, Uint32> pixel_map;

	Uint32* pixels = reinterpret_cast<Uint32*>(surf->pixels);
	Uint32* end_pixels = pixels + surf->w*surf->h;

	Uint32 AlphaPixel = SDL_MapRGBA(surf->format, 0x6f, 0x6d, 0x51, 0x0);

	int skip = 0;
	while(pixels != end_pixels) {
		if(((*pixels)&(~surf->format->Amask)) == AlphaPixel) {
			++pixels;
			continue;
		}
		std::map<Uint32, Uint32>::const_iterator itor = pixel_map.find(*pixels);
		if(itor == pixel_map.end()) {
			pixel_callable p(surf, *pixels);
			Uint32 result = f.execute(p).as_int();
			pixel_map[*pixels] = result;
			*pixels = result;
		} else {
			*pixels = itor->second;
		}
		++pixels;
	}

	if(locked) {
		SDL_UnlockSurface(surf.get());
	}
}

}

surface get_surface_formula(surface input, const std::string& algo)
{
	if(algo.empty()) {
		return input;
	}

	cache_key key(input, algo);
	surface surf = cache().get(key);
	if(surf.get() == NULL) {
		surf = input.clone();
		run_formula(surf, algo);
		cache().put(key, surf);
	}

	return surf;
}

namespace {
typedef std::map<std::pair<std::string, GLuint>, GLuint> shader_object_map;
shader_object_map shader_object_cache;

typedef std::map<std::pair<std::vector<std::string>,std::vector<std::string> >, GLuint> shader_map;
shader_map shader_cache;

void check_shader_errors(const std::string& fname, GLuint shader)
{
#ifndef SDL_VIDEO_OPENGL_ES
	GLint value = 0;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &value);
	if(value == GL_FALSE) {
		char buf[1024*16];
		GLint len;
		glGetShaderInfoLog(shader, sizeof(buf), &len, buf);
		std::string errors(buf, buf + len);
		ASSERT_LOG(false, "COMPILE ERROR IN SHADER " << fname << ": " << errors);
	}
#endif
}

GLuint compile_shader(const std::string& shader_file, GLuint type)
{
	GLuint& id = shader_object_cache[std::make_pair(shader_file, type)];
	if(id) {
		return id;
	}
#ifndef SDL_VIDEO_OPENGL_ES
	id = glCreateShader(type);

	const std::string file_data = sys::read_file("data/shaders/" + shader_file);

	const char* file_str = file_data.c_str();
	glShaderSource(id, 1, &file_str, NULL);

	glCompileShader(id);
	check_shader_errors(shader_file, id);

	return id;
#else
	return 0;
#endif
}

}

GLuint get_gl_shader(const std::vector<std::string>& vertex_shader_file,
                     const std::vector<std::string>& fragment_shader_file)
{
#ifndef SDL_VIDEO_OPENGL_ES
	if(vertex_shader_file.empty() || fragment_shader_file.empty()) {
		return 0;
	}

	shader_map::iterator itor = shader_cache.find(std::make_pair(vertex_shader_file, fragment_shader_file));
	if(itor != shader_cache.end()) {
		return itor->second;
	}

	std::vector<GLuint> shader_objects;
	foreach(const std::string& shader_file, vertex_shader_file) {
		shader_objects.push_back(compile_shader(shader_file, GL_VERTEX_SHADER));
	}

	foreach(const std::string& shader_file, fragment_shader_file) {
		shader_objects.push_back(compile_shader(shader_file, GL_FRAGMENT_SHADER));
	}

	GLuint program_id = glCreateProgram();

	foreach(GLuint shader_id, shader_objects) {
		glAttachShader(program_id, shader_id);
	}

	glLinkProgram(program_id);

	GLint link_status = 0;
	glGetProgramiv(program_id, GL_LINK_STATUS, &link_status);
	if(link_status != GL_TRUE) {
		char buf[1024*16];
		GLint len;
		glGetProgramInfoLog(program_id, sizeof(buf), &len, buf);
		std::string errors(buf, buf + len);
		ASSERT_LOG(false, "LINK ERROR IN SHADER PROGRAM: " << errors);
	}

	shader_cache[std::make_pair(vertex_shader_file, fragment_shader_file)] = program_id;

	glUseProgram(0);

	return program_id;
#else
	return 0;
#endif
}

BENCHMARK(surface_formula)
{
	surface s(graphics::surface_cache::get("characters/frogatto-spritesheet1.png"));
	assert(s.get());

	surface target(SDL_CreateRGBSurface(SDL_SWSURFACE,s->w,s->h,32,SURFACE_MASK));
	SDL_BlitSurface(s.get(), NULL, target.get(), NULL);

	const std::string algo("rgba(b,r,g,a)");
	BENCHMARK_LOOP {
		run_formula(target, algo);
	}
}

BENCHMARK(pixel_table)
{
	//This is some hard coded test data. It gives the set of pixels in
	//the input image, and the pixels we want to map to.
	const Uint32 PixelsFrom[] = {0xFF00FFFF, 0xFFFFFFFF, 0x9772FF13, 0xFF002145, 0x00FFFFFF, 0x94FF28FF };
	const Uint32 PixelsTo[] = {0x00FF0000, 0xFF00FFFF, 0xFFFFFFFF, 0x9772FF13, 0xFF002145, 0x00FFFFFF };
	const int NumColors = sizeof(PixelsFrom)/sizeof(*PixelsFrom);

	//Set up an image of a million pixels in size. Set all values to values
	//in the 'pixels from' range.
	std::vector<Uint32> image(1000000);
	for(int n = 0; n != image.size(); ++n) {
		image[n] = PixelsFrom[n%NumColors];
	}

	//set up our table mapping pixels from -> pixels to.
	typedef std::map<Uint32, Uint32> PixelTable;
	PixelTable table;
	for(int n = 0; n != NumColors; ++n) {
		table.insert(std::pair<Uint32, Uint32>(PixelsFrom[n], PixelsTo[n]));
	}

	//now go over the image and map all source pixels to their destinations.
	//this is the part that we want to benchmark.
	BENCHMARK_LOOP {
		for(int n = 0; n != image.size(); ++n) {
			image[n] = table[image[n]];
		}
	}
}