~glcompbench-dev/glcompbench/libmatrix-util

« back to all changes in this revision

Viewing changes to src/libmatrix/shader-source.cc

  • Committer: Jesse Barker
  • Date: 2012-01-26 19:36:28 UTC
  • Revision ID: jesse.barker@linaro.org-20120126193628-r88v9tbu0kzzkyqb
Update local libmatrix version to reflect lp:libmatrix revno 32.  Preserve local
integration changes to the Program object (as well as credit to Alexandros for
having made them originally).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// Copyright (c) 2010-2012 Linaro Limited
 
3
//
 
4
// All rights reserved. This program and the accompanying materials
 
5
// are made available under the terms of the MIT License which accompanies
 
6
// this distribution, and is available at
 
7
// http://www.opensource.org/licenses/mit-license.php
 
8
//
 
9
// Contributors:
 
10
//     Alexandros Frantzis <alexandros.frantzis@linaro.org>
 
11
//     Jesse Barker <jesse.barker@linaro.org>
 
12
//
 
13
#include <istream>
 
14
#include <memory>
 
15
 
 
16
#include "shader-source.h"
 
17
#include "log.h"
 
18
#include "vec.h"
 
19
#include "util.h"
 
20
 
 
21
/**
 
22
 * Holds default precision values for all shader types
 
23
 * (even the unknown type, which is hardwired to default precision values)
 
24
 */
 
25
std::vector<ShaderSource::Precision>
 
26
ShaderSource::default_precision_(ShaderSource::ShaderTypeUnknown + 1);
 
27
 
 
28
/**
 
29
 * Loads the contents of a file into a string.
 
30
 *
 
31
 * @param filename the name of the file
 
32
 * @param str the string to put the contents of the file into
 
33
 */
 
34
bool
 
35
ShaderSource::load_file(const std::string& filename, std::string& str)
 
36
{
 
37
    std::auto_ptr<std::istream> is_ptr(Util::get_resource(filename));
 
38
    std::istream& inputFile(*is_ptr);
 
39
 
 
40
    if (!inputFile)
 
41
    {
 
42
        Log::error("Failed to open \"%s\"\n", filename.c_str());
 
43
        return false;
 
44
    }
 
45
 
 
46
    std::string curLine;
 
47
    while (getline(inputFile, curLine))
 
48
    {
 
49
        str += curLine;
 
50
        str += '\n';
 
51
    }
 
52
 
 
53
    return true;
 
54
}
 
55
 
 
56
 
 
57
/**
 
58
 * Appends a string to the shader source.
 
59
 *
 
60
 * @param str the string to append
 
61
 */
 
62
void
 
63
ShaderSource::append(const std::string &str)
 
64
{
 
65
    source_ << str;
 
66
}
 
67
 
 
68
/**
 
69
 * Appends the contents of a file to the shader source.
 
70
 *
 
71
 * @param filename the name of the file to append
 
72
 */
 
73
void
 
74
ShaderSource::append_file(const std::string &filename)
 
75
{
 
76
    std::string source;
 
77
    if (load_file(filename, source))
 
78
        source_ << source;
 
79
}
 
80
 
 
81
/**
 
82
 * Replaces a string in the source with another string.
 
83
 *
 
84
 * @param remove the string to replace
 
85
 * @param insert the string to replace with
 
86
 */
 
87
void
 
88
ShaderSource::replace(const std::string &remove, const std::string &insert)
 
89
{
 
90
    std::string::size_type pos = 0;
 
91
    std::string str(source_.str());
 
92
 
 
93
    while ((pos = str.find(remove, pos)) != std::string::npos) {
 
94
        str.replace(pos, remove.size(), insert);
 
95
        pos++;
 
96
    }
 
97
 
 
98
    source_.clear();
 
99
    source_.str(str);
 
100
}
 
101
 
 
102
/**
 
103
 * Replaces a string in the source with the contents of a file.
 
104
 *
 
105
 * @param remove the string to replace
 
106
 * @param filename the name of the file to read from
 
107
 */
 
108
void
 
109
ShaderSource::replace_with_file(const std::string &remove, const std::string &filename)
 
110
{
 
111
    std::string source;
 
112
    if (load_file(filename, source))
 
113
        replace(remove, source);
 
114
}
 
115
 
 
116
/**
 
117
 * Adds a string (usually containing a constant definition) at
 
118
 * global (per shader) scope.
 
119
 *
 
120
 * The string is placed after any default precision qualifiers.
 
121
 *
 
122
 * @param str the string to add
 
123
 */
 
124
void
 
125
ShaderSource::add_global(const std::string &str)
 
126
{
 
127
    std::string::size_type pos = 0;
 
128
    std::string source(source_.str());
 
129
 
 
130
    /* Find the last precision qualifier */
 
131
    pos = source.rfind("precision");
 
132
 
 
133
    if (pos != std::string::npos) {
 
134
        /*
 
135
         * Find the next #endif line of a preprocessor block that contains
 
136
         * the precision qualifier.
 
137
         */
 
138
        std::string::size_type pos_if = source.find("#if", pos);
 
139
        std::string::size_type pos_endif = source.find("#endif", pos);
 
140
 
 
141
        if (pos_endif != std::string::npos && pos_endif < pos_if)
 
142
            pos = pos_endif;
 
143
 
 
144
        /* Go to the next line */
 
145
        pos = source.find("\n", pos);
 
146
        if (pos != std::string::npos)
 
147
            pos++;
 
148
    }
 
149
    else
 
150
        pos = 0;
 
151
 
 
152
    source.insert(pos, str);
 
153
 
 
154
    source_.clear();
 
155
    source_.str(source);
 
156
}
 
157
 
 
158
/**
 
159
 * Adds a string (usually containing a constant definition) at
 
160
 * global (per shader) scope.
 
161
 *
 
162
 * The string is placed after any default precision qualifiers.
 
163
 *
 
164
 * @param function the function to add the string into
 
165
 * @param str the string to add
 
166
 */
 
167
void
 
168
ShaderSource::add_local(const std::string &str, const std::string &function)
 
169
{
 
170
    std::string::size_type pos = 0;
 
171
    std::string source(source_.str());
 
172
 
 
173
    /* Find the function */
 
174
    pos = source.find(function);
 
175
    pos = source.find('{', pos);
 
176
 
 
177
    /* Go to the next line */
 
178
    pos = source.find("\n", pos);
 
179
    if (pos != std::string::npos)
 
180
        pos++;
 
181
 
 
182
    source.insert(pos, str);
 
183
 
 
184
    source_.clear();
 
185
    source_.str(source);
 
186
}
 
187
 
 
188
/**
 
189
 * Adds a string (usually containing a constant definition) to a shader source
 
190
 *
 
191
 * If the function parameter is empty, the string will be added to global
 
192
 * scope, after any precision definitions.
 
193
 *
 
194
 * @param str the string to add
 
195
 * @param function if not empty, the function to add the string into
 
196
 */
 
197
void
 
198
ShaderSource::add(const std::string &str, const std::string &function)
 
199
{
 
200
    if (!function.empty())
 
201
        add_local(str, function);
 
202
    else
 
203
        add_global(str);
 
204
}
 
205
 
 
206
/**
 
207
 * Adds a float constant definition.
 
208
 *
 
209
 * @param name the name of the constant
 
210
 * @param f the value of the constant
 
211
 * @param function if not empty, the function to put the definition in
 
212
 */
 
213
void
 
214
ShaderSource::add_const(const std::string &name, float f,
 
215
                        const std::string &function)
 
216
{
 
217
    std::stringstream ss;
 
218
 
 
219
    ss << "const float " << name << " = " << std::fixed << f << ";" << std::endl;
 
220
 
 
221
    add(ss.str(), function);
 
222
}
 
223
 
 
224
/**
 
225
 * Adds a float array constant definition.
 
226
 *
 
227
 * Note that various GLSL versions (including ES) don't support
 
228
 * array constants.
 
229
 *
 
230
 * @param name the name of the constant
 
231
 * @param v the value of the constant
 
232
 * @param function if not empty, the function to put the definition in
 
233
 */
 
234
void
 
235
ShaderSource::add_const(const std::string &name, std::vector<float> &array,
 
236
                        const std::string &function)
 
237
{
 
238
    std::stringstream ss;
 
239
 
 
240
    ss << "const float " << name << "[" << array.size() << "] = {" << std::fixed;
 
241
    for(std::vector<float>::const_iterator iter = array.begin();
 
242
        iter != array.end();
 
243
        iter++)
 
244
    {
 
245
        ss << *iter;
 
246
        if (iter + 1 != array.end())
 
247
            ss << ", " << std::endl;
 
248
    }
 
249
 
 
250
    ss << "};" << std::endl;
 
251
 
 
252
    add(ss.str(), function);
 
253
}
 
254
 
 
255
/**
 
256
 * Adds a vec2 constant definition.
 
257
 *
 
258
 * @param name the name of the constant
 
259
 * @param v the value of the constant
 
260
 * @param function if not empty, the function to put the definition in
 
261
 */
 
262
void
 
263
ShaderSource::add_const(const std::string &name, const LibMatrix::vec2 &v,
 
264
                        const std::string &function)
 
265
{
 
266
    std::stringstream ss;
 
267
 
 
268
    ss << "const vec2 " << name << " = vec2(" << std::fixed;
 
269
    ss << v.x() << ", " << v.y() << ");" << std::endl;
 
270
 
 
271
    add(ss.str(), function);
 
272
}
 
273
 
 
274
/**
 
275
 * Adds a vec3 constant definition.
 
276
 *
 
277
 * @param name the name of the constant
 
278
 * @param v the value of the constant
 
279
 * @param function if not empty, the function to put the definition in
 
280
 */
 
281
void
 
282
ShaderSource::add_const(const std::string &name, const LibMatrix::vec3 &v,
 
283
                        const std::string &function)
 
284
{
 
285
    std::stringstream ss;
 
286
 
 
287
    ss << "const vec3 " << name << " = vec3(" << std::fixed;
 
288
    ss << v.x() << ", " << v.y() << ", " << v.z() << ");" << std::endl;
 
289
 
 
290
    add(ss.str(), function);
 
291
}
 
292
 
 
293
/**
 
294
 * Adds a vec4 constant definition.
 
295
 *
 
296
 * @param name the name of the constant
 
297
 * @param v the value of the constant
 
298
 * @param function if not empty, the function to put the definition in
 
299
 */
 
300
void
 
301
ShaderSource::add_const(const std::string &name, const LibMatrix::vec4 &v,
 
302
                        const std::string &function)
 
303
{
 
304
    std::stringstream ss;
 
305
 
 
306
    ss << "const vec4 " << name << " = vec4(" << std::fixed;
 
307
    ss << v.x() << ", " << v.y() << ", " << v.z() << ", " << v.w() << ");" << std::endl;
 
308
 
 
309
    add(ss.str(), function);
 
310
}
 
311
 
 
312
/**
 
313
 * Adds a mat3 constant definition.
 
314
 *
 
315
 * @param name the name of the constant
 
316
 * @param v the value of the constant
 
317
 * @param function if not empty, the function to put the definition in
 
318
 */
 
319
void
 
320
ShaderSource::add_const(const std::string &name, const LibMatrix::mat3 &m,
 
321
                        const std::string &function)
 
322
{
 
323
    std::stringstream ss;
 
324
 
 
325
    ss << "const mat3 " << name << " = mat3(" << std::fixed;
 
326
    ss << m[0][0] << ", " << m[1][0] << ", " << m[2][0] << "," << std::endl;
 
327
    ss << m[0][1] << ", " << m[1][1] << ", " << m[2][1] << "," << std::endl;
 
328
    ss << m[0][2] << ", " << m[1][2] << ", " << m[2][2] << std::endl;
 
329
    ss << ");" << std::endl;
 
330
 
 
331
    add(ss.str(), function);
 
332
}
 
333
 
 
334
/**
 
335
 * Adds a float array declaration and initialization.
 
336
 *
 
337
 * @param name the name of the array
 
338
 * @param array the array values
 
339
 * @param init_function the function to put the initialization in
 
340
 * @param decl_function if not empty, the function to put the declaration in
 
341
 */
 
342
void
 
343
ShaderSource::add_array(const std::string &name, std::vector<float> &array,
 
344
                        const std::string &init_function,
 
345
                        const std::string &decl_function)
 
346
{
 
347
    if (init_function.empty() || name.empty())
 
348
        return;
 
349
 
 
350
    std::stringstream ss;
 
351
    ss << "float " << name << "[" << array.size() << "];" << std::endl;
 
352
 
 
353
    std::string decl(ss.str());
 
354
 
 
355
    ss.clear();
 
356
    ss.str("");
 
357
    ss << std::fixed;
 
358
 
 
359
    for(std::vector<float>::const_iterator iter = array.begin();
 
360
        iter != array.end();
 
361
        iter++)
 
362
    {
 
363
        ss << name << "[" << iter - array.begin() << "] = " << *iter << ";" << std::endl;
 
364
    }
 
365
 
 
366
    add(ss.str(), init_function);
 
367
 
 
368
    add(decl, decl_function);
 
369
}
 
370
 
 
371
/**
 
372
 * Gets the ShaderType for this ShaderSource.
 
373
 *
 
374
 * If the ShaderType is unknown, an attempt is made to infer
 
375
 * the type from the shader source contents.
 
376
 *
 
377
 * @return the ShaderType
 
378
 */
 
379
ShaderSource::ShaderType
 
380
ShaderSource::type()
 
381
{
 
382
    /* Try to infer the type from the source contents */
 
383
    if (type_ == ShaderSource::ShaderTypeUnknown) {
 
384
        std::string source(source_.str());
 
385
 
 
386
        if (source.find("gl_FragColor") != std::string::npos)
 
387
            type_ = ShaderSource::ShaderTypeFragment;
 
388
        else if (source.find("gl_Position") != std::string::npos)
 
389
            type_ = ShaderSource::ShaderTypeVertex;
 
390
        else
 
391
            Log::debug("Cannot infer shader type from contents. Leaving it Unknown.\n");
 
392
    }
 
393
 
 
394
    return type_;
 
395
}
 
396
 
 
397
/**
 
398
 * Helper function that emits a precision statement.
 
399
 *
 
400
 * @param ss the stringstream to add the statement to
 
401
 * @param val the precision value
 
402
 * @param type_str the variable type to apply the precision value to
 
403
 */
 
404
void
 
405
ShaderSource::emit_precision(std::stringstream& ss, ShaderSource::PrecisionValue val,
 
406
                             const std::string& type_str)
 
407
{
 
408
    static const char *precision_map[] = {
 
409
        "lowp", "mediump", "highp", NULL
 
410
    };
 
411
 
 
412
    if (val == ShaderSource::PrecisionValueHigh) {
 
413
        if (type_ == ShaderSource::ShaderTypeFragment)
 
414
            ss << "#ifdef GL_FRAGMENT_PRECISION_HIGH" << std::endl;
 
415
 
 
416
        ss << "precision highp " << type_str << ";" << std::endl;
 
417
 
 
418
        if (type_ == ShaderSource::ShaderTypeFragment) {
 
419
            ss << "#else" << std::endl;
 
420
            ss << "precision mediump " << type_str << ";" << std::endl;
 
421
            ss << "#endif" << std::endl;
 
422
        }
 
423
    }
 
424
    else if (val >= 0 && val < ShaderSource::PrecisionValueDefault) {
 
425
        ss << "precision " << precision_map[val] << " ";
 
426
        ss << type_str << ";" << std::endl;
 
427
    }
 
428
 
 
429
    /* There is no default precision in the fragment shader, so set it to mediump */
 
430
    if (val == ShaderSource::PrecisionValueDefault
 
431
        && type_str == "float" && type_ == ShaderSource::ShaderTypeFragment)
 
432
    {
 
433
        ss << "precision mediump float;" << std::endl;
 
434
    }
 
435
}
 
436
 
 
437
/**
 
438
 * Gets a string containing the complete shader source.
 
439
 *
 
440
 * Precision statements are applied at this point.
 
441
 *
 
442
 * @return the shader source
 
443
 */
 
444
std::string
 
445
ShaderSource::str()
 
446
{
 
447
    /* Decide which precision values to use */
 
448
    ShaderSource::Precision precision;
 
449
 
 
450
    /* Ensure we have tried to infer the type from the contents */
 
451
    type();
 
452
 
 
453
    if (precision_has_been_set_)
 
454
        precision = precision_;
 
455
    else
 
456
        precision = default_precision(type_);
 
457
 
 
458
    /* Create the precision statements */
 
459
    std::stringstream ss;
 
460
 
 
461
    emit_precision(ss, precision.int_precision, "int");
 
462
    emit_precision(ss, precision.float_precision, "float");
 
463
    emit_precision(ss, precision.sampler2d_precision, "sampler2D");
 
464
    emit_precision(ss, precision.samplercube_precision, "samplerCube");
 
465
 
 
466
    std::string precision_str(ss.str());
 
467
    if (!precision_str.empty()) {
 
468
        precision_str.insert(0, "#ifdef GL_ES\n");
 
469
        precision_str.insert(precision_str.size(), "#endif\n");
 
470
    }
 
471
 
 
472
    return precision_str + source_.str();
 
473
}
 
474
 
 
475
/**
 
476
 * Sets the precision that will be used for this shader.
 
477
 *
 
478
 * This overrides any default values set with ShaderSource::default_*_precision().
 
479
 *
 
480
 * @param precision the precision to set
 
481
 */
 
482
void
 
483
ShaderSource::precision(const ShaderSource::Precision& precision)
 
484
{
 
485
    precision_ = precision;
 
486
    precision_has_been_set_ = true;
 
487
}
 
488
 
 
489
/**
 
490
 * Gets the precision that will be used for this shader.
 
491
 *
 
492
 * @return the precision
 
493
 */
 
494
const ShaderSource::Precision&
 
495
ShaderSource::precision()
 
496
{
 
497
    return precision_;
 
498
}
 
499
 
 
500
/**
 
501
 * Sets the default precision that will be used for a shaders type.
 
502
 *
 
503
 * If type is ShaderTypeUnknown the supplied precision is used for all
 
504
 * shader types.
 
505
 *
 
506
 * This can be overriden per ShaderSource object by using ::precision().
 
507
 *
 
508
 * @param precision the default precision to set
 
509
 * @param type the ShaderType to use the precision for
 
510
 */
 
511
void
 
512
ShaderSource::default_precision(const ShaderSource::Precision& precision,
 
513
                                ShaderSource::ShaderType type)
 
514
{
 
515
    if (type < 0 || type > ShaderSource::ShaderTypeUnknown)
 
516
        type = ShaderSource::ShaderTypeUnknown;
 
517
 
 
518
    if (type == ShaderSource::ShaderTypeUnknown) {
 
519
        for (size_t i = 0; i < ShaderSource::ShaderTypeUnknown; i++)
 
520
            default_precision_[i] = precision;
 
521
    }
 
522
    else {
 
523
        default_precision_[type] = precision;
 
524
    }
 
525
}
 
526
 
 
527
/**
 
528
 * Gets the default precision that will be used for a shader type.
 
529
 *
 
530
 * It is valid to use a type of ShaderTypeUnknown. This will always
 
531
 * return a Precision with default values.
 
532
 *
 
533
 * @param type the ShaderType to get the precision of
 
534
 *
 
535
 * @return the precision
 
536
 */
 
537
const ShaderSource::Precision&
 
538
ShaderSource::default_precision(ShaderSource::ShaderType type)
 
539
{
 
540
    if (type < 0 || type > ShaderSource::ShaderTypeUnknown)
 
541
        type = ShaderSource::ShaderTypeUnknown;
 
542
 
 
543
    return default_precision_[type];
 
544
}
 
545
 
 
546
/****************************************
 
547
 * ShaderSource::Precision constructors *
 
548
 ****************************************/
 
549
 
 
550
/**
 
551
 * Creates a ShaderSource::Precision with default precision values.
 
552
 */
 
553
ShaderSource::Precision::Precision() :
 
554
    int_precision(ShaderSource::PrecisionValueDefault),
 
555
    float_precision(ShaderSource::PrecisionValueDefault),
 
556
    sampler2d_precision(ShaderSource::PrecisionValueDefault),
 
557
    samplercube_precision(ShaderSource::PrecisionValueDefault)
 
558
{
 
559
}
 
560
 
 
561
/**
 
562
 * Creates a ShaderSource::Precision using the supplied precision values.
 
563
 */
 
564
ShaderSource::Precision::Precision(ShaderSource::PrecisionValue int_p,
 
565
                                   ShaderSource::PrecisionValue float_p,
 
566
                                   ShaderSource::PrecisionValue sampler2d_p,
 
567
                                   ShaderSource::PrecisionValue samplercube_p) :
 
568
    int_precision(int_p), float_precision(float_p),
 
569
    sampler2d_precision(sampler2d_p), samplercube_precision(samplercube_p)
 
570
{
 
571
}
 
572
 
 
573
/**
 
574
 * Creates a ShaderSource::Precision from a string representation of
 
575
 * precision values.
 
576
 *
 
577
 * The string format is:
 
578
 * "<int>,<float>,<sampler2d>,<samplercube>"
 
579
 *
 
580
 * Each precision value is one of "high", "medium", "low" or "default".
 
581
 *
 
582
 * @param precision_values the string representation of the precision values
 
583
 */
 
584
ShaderSource::Precision::Precision(const std::string& precision_values) :
 
585
    int_precision(ShaderSource::PrecisionValueDefault),
 
586
    float_precision(ShaderSource::PrecisionValueDefault),
 
587
    sampler2d_precision(ShaderSource::PrecisionValueDefault),
 
588
    samplercube_precision(ShaderSource::PrecisionValueDefault)
 
589
{
 
590
    std::vector<std::string> elems;
 
591
 
 
592
    Util::split(precision_values, ',', elems);
 
593
 
 
594
    for (size_t i = 0; i < elems.size() && i < 4; i++) {
 
595
        const std::string& pstr(elems[i]);
 
596
        ShaderSource::PrecisionValue pval;
 
597
 
 
598
        if (pstr == "high")
 
599
            pval = ShaderSource::PrecisionValueHigh;
 
600
        else if (pstr == "medium")
 
601
            pval = ShaderSource::PrecisionValueMedium;
 
602
        else if (pstr == "low")
 
603
            pval = ShaderSource::PrecisionValueLow;
 
604
        else
 
605
            pval = ShaderSource::PrecisionValueDefault;
 
606
 
 
607
        switch(i) {
 
608
            case 0: int_precision = pval; break;
 
609
            case 1: float_precision = pval; break;
 
610
            case 2: sampler2d_precision = pval; break;
 
611
            case 3: samplercube_precision = pval; break;
 
612
            default: break;
 
613
        }
 
614
    }
 
615
}