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

« back to all changes in this revision

Viewing changes to src/surface_formula.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Dmitry E. Oboukhov
  • Date: 2010-07-21 16:21:45 UTC
  • Revision ID: james.westby@ubuntu.com-20100721162145-zid0u93fm3xz73gh
Tags: upstream-1.0+dfsg1
ImportĀ upstreamĀ versionĀ 1.0+dfsg1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <iostream>
 
2
#include <map>
 
3
#include <SDL.h>
 
4
#ifndef SDL_VIDEO_OPENGL_ES
 
5
#include <GL/glew.h>
 
6
#endif
 
7
 
 
8
#include "asserts.hpp"
 
9
#include "concurrent_cache.hpp"
 
10
#include "filesystem.hpp"
 
11
#include "foreach.hpp"
 
12
#include "formula.hpp"
 
13
#include "formula_callable.hpp"
 
14
#include "formula_function.hpp"
 
15
#include "hi_res_timer.hpp"
 
16
#include "surface.hpp"
 
17
#include "surface_cache.hpp"
 
18
#include "surface_formula.hpp"
 
19
#include "unit_test.hpp"
 
20
 
 
21
using namespace graphics;
 
22
using namespace game_logic;
 
23
 
 
24
namespace {
 
25
 
 
26
typedef std::pair<surface, std::string> cache_key;
 
27
typedef concurrent_cache<cache_key, surface> cache_map;
 
28
 
 
29
cache_map& cache() {
 
30
        static cache_map instance;
 
31
        return instance;
 
32
}
 
33
 
 
34
class rgba_function : public function_expression {
 
35
public:
 
36
        explicit rgba_function(surface surf, const args_list& args)
 
37
          : function_expression("rgba", args, 4), surf_(surf) {
 
38
        }
 
39
 
 
40
private:
 
41
        variant execute(const formula_callable& variables) const {
 
42
                return variant(SDL_MapRGBA(surf_->format,
 
43
                   Uint8(args()[0]->evaluate(variables).as_int()),
 
44
                   Uint8(args()[1]->evaluate(variables).as_int()),
 
45
                   Uint8(args()[2]->evaluate(variables).as_int()),
 
46
                   Uint8(args()[3]->evaluate(variables).as_int())));
 
47
        }
 
48
        surface surf_;
 
49
};
 
50
 
 
51
class surface_formula_symbol_table : public function_symbol_table
 
52
{
 
53
public:
 
54
        explicit surface_formula_symbol_table(surface surf) : surf_(surf)
 
55
        {}
 
56
        expression_ptr create_function(
 
57
                          const std::string& fn,
 
58
                          const std::vector<expression_ptr>& args,
 
59
                                          const formula_callable_definition* callable_def) const;
 
60
private:
 
61
        surface surf_;
 
62
};
 
63
 
 
64
expression_ptr surface_formula_symbol_table::create_function(
 
65
                           const std::string& fn,
 
66
                           const std::vector<expression_ptr>& args,
 
67
                                                   const formula_callable_definition* callable_def) const
 
68
{
 
69
        if(fn == "rgba") {
 
70
                return expression_ptr(new rgba_function(surf_, args));
 
71
        } else {
 
72
                return function_symbol_table::create_function(fn, args, callable_def);
 
73
        }
 
74
}
 
75
 
 
76
class pixel_callable : public game_logic::formula_callable {
 
77
public:
 
78
        pixel_callable(const surface& surf, Uint32 pixel)
 
79
        {
 
80
                SDL_GetRGBA(pixel, surf->format, &r, &g, &b, &a);
 
81
                static const unsigned char AlphaPixel[] = {0x6f, 0x6d, 0x51};
 
82
                if(r == AlphaPixel[0] && g == AlphaPixel[1] && b == AlphaPixel[2]) {
 
83
                        a = 0;
 
84
                }
 
85
        }
 
86
 
 
87
        bool is_alpha() const { return a == 0; }
 
88
private:
 
89
        variant get_value(const std::string& key) const {
 
90
                switch(*key.c_str()) {
 
91
                case 'r': return variant(r);
 
92
                case 'g': return variant(g);
 
93
                case 'b': return variant(b);
 
94
                case 'a': return variant(a);
 
95
                default: return variant();
 
96
                }
 
97
        }
 
98
 
 
99
        Uint8 r, g, b, a;
 
100
};
 
101
 
 
102
void run_formula(surface surf, const std::string& algo)
 
103
{
 
104
        const hi_res_timer timer("run_formula");
 
105
 
 
106
        const int ticks = SDL_GetTicks();
 
107
        surface_formula_symbol_table table(surf);
 
108
        game_logic::formula f(algo, &table);
 
109
        bool locked = false;
 
110
        if(SDL_MUSTLOCK(surf.get())) {
 
111
                const int res = SDL_LockSurface(surf.get());
 
112
                if(res == 0) {
 
113
                        locked = true;
 
114
                }
 
115
        }
 
116
 
 
117
        std::map<Uint32, Uint32> pixel_map;
 
118
 
 
119
        Uint32* pixels = reinterpret_cast<Uint32*>(surf->pixels);
 
120
        Uint32* end_pixels = pixels + surf->w*surf->h;
 
121
 
 
122
        Uint32 AlphaPixel = SDL_MapRGBA(surf->format, 0x6f, 0x6d, 0x51, 0x0);
 
123
 
 
124
        int skip = 0;
 
125
        while(pixels != end_pixels) {
 
126
                if(((*pixels)&(~surf->format->Amask)) == AlphaPixel) {
 
127
                        ++pixels;
 
128
                        continue;
 
129
                }
 
130
                std::map<Uint32, Uint32>::const_iterator itor = pixel_map.find(*pixels);
 
131
                if(itor == pixel_map.end()) {
 
132
                        pixel_callable p(surf, *pixels);
 
133
                        Uint32 result = f.execute(p).as_int();
 
134
                        pixel_map[*pixels] = result;
 
135
                        *pixels = result;
 
136
                } else {
 
137
                        *pixels = itor->second;
 
138
                }
 
139
                ++pixels;
 
140
        }
 
141
 
 
142
        if(locked) {
 
143
                SDL_UnlockSurface(surf.get());
 
144
        }
 
145
}
 
146
 
 
147
}
 
148
 
 
149
surface get_surface_formula(surface input, const std::string& algo)
 
150
{
 
151
        if(algo.empty()) {
 
152
                return input;
 
153
        }
 
154
 
 
155
        cache_key key(input, algo);
 
156
        surface surf = cache().get(key);
 
157
        if(surf.get() == NULL) {
 
158
                surf = input.clone();
 
159
                run_formula(surf, algo);
 
160
                cache().put(key, surf);
 
161
        }
 
162
 
 
163
        return surf;
 
164
}
 
165
 
 
166
namespace {
 
167
typedef std::map<std::pair<std::string, GLuint>, GLuint> shader_object_map;
 
168
shader_object_map shader_object_cache;
 
169
 
 
170
typedef std::map<std::pair<std::vector<std::string>,std::vector<std::string> >, GLuint> shader_map;
 
171
shader_map shader_cache;
 
172
 
 
173
void check_shader_errors(const std::string& fname, GLuint shader)
 
174
{
 
175
#ifndef SDL_VIDEO_OPENGL_ES
 
176
        GLint value = 0;
 
177
        glGetShaderiv(shader, GL_COMPILE_STATUS, &value);
 
178
        if(value == GL_FALSE) {
 
179
                char buf[1024*16];
 
180
                GLint len;
 
181
                glGetShaderInfoLog(shader, sizeof(buf), &len, buf);
 
182
                std::string errors(buf, buf + len);
 
183
                ASSERT_LOG(false, "COMPILE ERROR IN SHADER " << fname << ": " << errors);
 
184
        }
 
185
#endif
 
186
}
 
187
 
 
188
GLuint compile_shader(const std::string& shader_file, GLuint type)
 
189
{
 
190
        GLuint& id = shader_object_cache[std::make_pair(shader_file, type)];
 
191
        if(id) {
 
192
                return id;
 
193
        }
 
194
#ifndef SDL_VIDEO_OPENGL_ES
 
195
        id = glCreateShader(type);
 
196
 
 
197
        const std::string file_data = sys::read_file("data/shaders/" + shader_file);
 
198
 
 
199
        const char* file_str = file_data.c_str();
 
200
        glShaderSource(id, 1, &file_str, NULL);
 
201
 
 
202
        glCompileShader(id);
 
203
        check_shader_errors(shader_file, id);
 
204
 
 
205
        return id;
 
206
#else
 
207
        return 0;
 
208
#endif
 
209
}
 
210
 
 
211
}
 
212
 
 
213
GLuint get_gl_shader(const std::vector<std::string>& vertex_shader_file,
 
214
                     const std::vector<std::string>& fragment_shader_file)
 
215
{
 
216
#ifndef SDL_VIDEO_OPENGL_ES
 
217
        if(vertex_shader_file.empty() || fragment_shader_file.empty()) {
 
218
                return 0;
 
219
        }
 
220
 
 
221
        shader_map::iterator itor = shader_cache.find(std::make_pair(vertex_shader_file, fragment_shader_file));
 
222
        if(itor != shader_cache.end()) {
 
223
                return itor->second;
 
224
        }
 
225
 
 
226
        std::vector<GLuint> shader_objects;
 
227
        foreach(const std::string& shader_file, vertex_shader_file) {
 
228
                shader_objects.push_back(compile_shader(shader_file, GL_VERTEX_SHADER));
 
229
        }
 
230
 
 
231
        foreach(const std::string& shader_file, fragment_shader_file) {
 
232
                shader_objects.push_back(compile_shader(shader_file, GL_FRAGMENT_SHADER));
 
233
        }
 
234
 
 
235
        GLuint program_id = glCreateProgram();
 
236
 
 
237
        foreach(GLuint shader_id, shader_objects) {
 
238
                glAttachShader(program_id, shader_id);
 
239
        }
 
240
 
 
241
        glLinkProgram(program_id);
 
242
 
 
243
        GLint link_status = 0;
 
244
        glGetProgramiv(program_id, GL_LINK_STATUS, &link_status);
 
245
        if(link_status != GL_TRUE) {
 
246
                char buf[1024*16];
 
247
                GLint len;
 
248
                glGetProgramInfoLog(program_id, sizeof(buf), &len, buf);
 
249
                std::string errors(buf, buf + len);
 
250
                ASSERT_LOG(false, "LINK ERROR IN SHADER PROGRAM: " << errors);
 
251
        }
 
252
 
 
253
        shader_cache[std::make_pair(vertex_shader_file, fragment_shader_file)] = program_id;
 
254
 
 
255
        glUseProgram(0);
 
256
 
 
257
        return program_id;
 
258
#else
 
259
        return 0;
 
260
#endif
 
261
}
 
262
 
 
263
BENCHMARK(surface_formula)
 
264
{
 
265
        surface s(graphics::surface_cache::get("characters/frogatto-spritesheet1.png"));
 
266
        assert(s.get());
 
267
 
 
268
        surface target(SDL_CreateRGBSurface(SDL_SWSURFACE,s->w,s->h,32,SURFACE_MASK));
 
269
        SDL_BlitSurface(s.get(), NULL, target.get(), NULL);
 
270
 
 
271
        const std::string algo("rgba(b,r,g,a)");
 
272
        BENCHMARK_LOOP {
 
273
                run_formula(target, algo);
 
274
        }
 
275
}
 
276
 
 
277
BENCHMARK(pixel_table)
 
278
{
 
279
        //This is some hard coded test data. It gives the set of pixels in
 
280
        //the input image, and the pixels we want to map to.
 
281
        const Uint32 PixelsFrom[] = {0xFF00FFFF, 0xFFFFFFFF, 0x9772FF13, 0xFF002145, 0x00FFFFFF, 0x94FF28FF };
 
282
        const Uint32 PixelsTo[] = {0x00FF0000, 0xFF00FFFF, 0xFFFFFFFF, 0x9772FF13, 0xFF002145, 0x00FFFFFF };
 
283
        const int NumColors = sizeof(PixelsFrom)/sizeof(*PixelsFrom);
 
284
 
 
285
        //Set up an image of a million pixels in size. Set all values to values
 
286
        //in the 'pixels from' range.
 
287
        std::vector<Uint32> image(1000000);
 
288
        for(int n = 0; n != image.size(); ++n) {
 
289
                image[n] = PixelsFrom[n%NumColors];
 
290
        }
 
291
 
 
292
        //set up our table mapping pixels from -> pixels to.
 
293
        typedef std::map<Uint32, Uint32> PixelTable;
 
294
        PixelTable table;
 
295
        for(int n = 0; n != NumColors; ++n) {
 
296
                table.insert(std::pair<Uint32, Uint32>(PixelsFrom[n], PixelsTo[n]));
 
297
        }
 
298
 
 
299
        //now go over the image and map all source pixels to their destinations.
 
300
        //this is the part that we want to benchmark.
 
301
        BENCHMARK_LOOP {
 
302
                for(int n = 0; n != image.size(); ++n) {
 
303
                        image[n] = table[image[n]];
 
304
                }
 
305
        }
 
306
}