1 // Copyright 2012 Intel Corporation
2 //
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // - Redistributions of source code must retain the above copyright notice, this
9 //   list of conditions and the following disclaimer.
10 //
11 // - Redistributions in binary form must reproduce the above copyright notice,
12 //   this list of conditions and the following disclaimer in the documentation
13 //   and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "wcore_attrib_list.h"
31 #include "wcore_config_attrs.h"
32 #include "wcore_error.h"
33 #include "wcore_util.h"
34 
35 enum {
36     DEFAULT_ACCUM_BUFFER = false,
37     DEFAULT_DOUBLE_BUFFERED = true,
38     DEFAULT_SAMPLE_BUFFERS = false,
39 };
40 
41 static bool
check_keys(const int32_t attrib_list[])42 check_keys(const int32_t attrib_list[])
43 {
44     if (attrib_list == NULL)
45         return true;
46 
47     for (int32_t i = 0; attrib_list[i]; i += 2) {
48         int32_t key = attrib_list[i];
49 
50         switch (key) {
51             case WAFFLE_CONTEXT_API:
52             case WAFFLE_CONTEXT_MAJOR_VERSION:
53             case WAFFLE_CONTEXT_MINOR_VERSION:
54             case WAFFLE_CONTEXT_PROFILE:
55             case WAFFLE_CONTEXT_FORWARD_COMPATIBLE:
56             case WAFFLE_CONTEXT_DEBUG:
57             case WAFFLE_CONTEXT_ROBUST_ACCESS:
58             case WAFFLE_RED_SIZE:
59             case WAFFLE_GREEN_SIZE:
60             case WAFFLE_BLUE_SIZE:
61             case WAFFLE_ALPHA_SIZE:
62             case WAFFLE_DEPTH_SIZE:
63             case WAFFLE_STENCIL_SIZE:
64             case WAFFLE_SAMPLES:
65             case WAFFLE_SAMPLE_BUFFERS:
66             case WAFFLE_DOUBLE_BUFFERED:
67             case WAFFLE_ACCUM_BUFFER:
68                 break;
69             default:
70                 wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
71                              "unrecognized attribute 0x%x at attrib_list[%d]",
72                              key, i);
73                 return false;
74         }
75     }
76 
77     return true;
78 }
79 
80 static bool
parse_bool(const int32_t attrib_list[],int32_t attrib_name,bool * value,bool default_value)81 parse_bool(const int32_t attrib_list[], int32_t attrib_name,
82            bool *value, bool default_value)
83 {
84     int32_t raw_value;
85 
86     wcore_attrib_list32_get_with_default(attrib_list, attrib_name,
87                                        &raw_value, default_value);
88 
89     if (raw_value == WAFFLE_DONT_CARE) {
90         *value = default_value;
91     } else if (raw_value == true || raw_value == false) {
92         *value = raw_value;
93     } else {
94         wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
95                     "%s has bad value 0x%x. "
96                     "Must be true(1), false(0), or WAFFLE_DONT_CARE(-1)",
97                     wcore_enum_to_string(attrib_name), raw_value);
98         return false;
99     }
100 
101     return true;
102 }
103 
104 static bool
parse_context_api(struct wcore_config_attrs * attrs,const int32_t attrib_list[])105 parse_context_api(struct wcore_config_attrs *attrs,
106                   const int32_t attrib_list[])
107 {
108     bool found;
109 
110     found = wcore_attrib_list32_get(attrib_list,
111                                   WAFFLE_CONTEXT_API, &attrs->context_api);
112     if (!found) {
113         wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
114                      "required attribute WAFFLE_CONTEXT_API is missing");
115         return false;
116     }
117 
118     switch (attrs->context_api) {
119         case WAFFLE_CONTEXT_OPENGL:
120         case WAFFLE_CONTEXT_OPENGL_ES1:
121         case WAFFLE_CONTEXT_OPENGL_ES2:
122         case WAFFLE_CONTEXT_OPENGL_ES3:
123             break;
124         default:
125             wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
126                          "WAFFLE_CONTEXT_API has bad value %#x",
127                          attrs->context_api);
128             return false;
129     }
130 
131     return true;
132 }
133 
134 static bool
set_context_version_default(struct wcore_config_attrs * attrs)135 set_context_version_default(struct wcore_config_attrs *attrs)
136 {
137     switch (attrs->context_api) {
138         case WAFFLE_CONTEXT_OPENGL:
139             attrs->context_major_version = 1;
140             attrs->context_minor_version = 0;
141             return true;
142         case WAFFLE_CONTEXT_OPENGL_ES1:
143             attrs->context_major_version = 1;
144             attrs->context_minor_version = 0;
145             return true;
146         case WAFFLE_CONTEXT_OPENGL_ES2:
147             attrs->context_major_version = 2;
148             attrs->context_minor_version = 0;
149             return true;
150         case WAFFLE_CONTEXT_OPENGL_ES3:
151             attrs->context_major_version = 3;
152             attrs->context_minor_version = 0;
153             return true;
154         default:
155             assert(false);
156             return false;
157     }
158 }
159 
160 static bool
parse_context_version(struct wcore_config_attrs * attrs,const int32_t attrib_list[])161 parse_context_version(struct wcore_config_attrs *attrs,
162                       const int32_t attrib_list[])
163 {
164     wcore_attrib_list32_get(attrib_list, WAFFLE_CONTEXT_MAJOR_VERSION,
165                             &attrs->context_major_version);
166     wcore_attrib_list32_get(attrib_list, WAFFLE_CONTEXT_MINOR_VERSION,
167                             &attrs->context_minor_version);
168 
169     if (attrs->context_major_version < 1) {
170         wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
171                      "WAFFLE_CONTEXT_MAJOR_VERSION must be >= 1");
172         return false;
173     }
174 
175     if (attrs->context_minor_version < 0) {
176         wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
177                      "WAFFLE_CONTEXT_MINOR_VERSION must be >= 0");
178         return false;
179     }
180     return true;
181 }
182 
183 static bool
set_context_profile_default(struct wcore_config_attrs * attrs)184 set_context_profile_default(struct wcore_config_attrs *attrs)
185 {
186     switch (attrs->context_api) {
187         case WAFFLE_CONTEXT_OPENGL:
188             if (wcore_config_attrs_version_ge(attrs, 32)) {
189                 attrs->context_profile = WAFFLE_CONTEXT_CORE_PROFILE;
190             }
191             else {
192                 attrs->context_profile = WAFFLE_NONE;
193             }
194             break;
195         case WAFFLE_CONTEXT_OPENGL_ES1:
196         case WAFFLE_CONTEXT_OPENGL_ES2:
197         case WAFFLE_CONTEXT_OPENGL_ES3:
198             attrs->context_profile = WAFFLE_NONE;
199             break;
200         default:
201             assert(false);
202             return false;
203     }
204 
205     return true;
206 }
207 
208 static bool
parse_context_profile(struct wcore_config_attrs * attrs,const int32_t attrib_list[])209 parse_context_profile(struct wcore_config_attrs *attrs,
210                       const int32_t attrib_list[])
211 {
212     wcore_attrib_list32_get(attrib_list, WAFFLE_CONTEXT_PROFILE,
213                             &attrs->context_profile);
214 
215     switch (attrs->context_api) {
216         case WAFFLE_CONTEXT_OPENGL:
217             if (wcore_config_attrs_version_ge(attrs, 32)
218                 && attrs->context_profile != WAFFLE_CONTEXT_CORE_PROFILE
219                 && attrs->context_profile != WAFFLE_CONTEXT_COMPATIBILITY_PROFILE) {
220                 wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
221                              "for OpenGL >= 3.2, "
222                              "WAFFLE_CONTEXT_PROFILE must be either "
223                              "WAFFLE_CONTEXT_CORE_PROFILE or "
224                              "WAFFLE_CONTEXT_COMPATIBILITY_PROFILE");
225                 return false;
226             }
227             else if (wcore_config_attrs_version_lt(attrs, 32)
228                      && attrs->context_profile != WAFFLE_NONE) {
229                 wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
230                              "for OpenGL < 3.2, WAFFLE_CONTEXT_PROFILE must be "
231                              "WAFFLE_NONE");
232                 return false;
233             }
234             break;
235         case WAFFLE_CONTEXT_OPENGL_ES1:
236         case WAFFLE_CONTEXT_OPENGL_ES2:
237         case WAFFLE_CONTEXT_OPENGL_ES3:
238             if (attrs->context_profile != WAFFLE_NONE) {
239                 wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
240                              "for OpenGL ES, WAFFLE_CONTEXT_PROFILE must be "
241                              "WAFFLE_NONE");
242                 return false;
243             }
244             break;
245         default:
246             assert(false);
247             return false;
248     }
249 
250     return true;
251 }
252 
253 static bool
parse_context_forward_compatible(struct wcore_config_attrs * attrs,const int32_t attrib_list[])254 parse_context_forward_compatible(struct wcore_config_attrs *attrs,
255                                  const int32_t attrib_list[])
256 {
257     if (!parse_bool(attrib_list, WAFFLE_CONTEXT_FORWARD_COMPATIBLE,
258                     &attrs->context_forward_compatible, false)) {
259         return false;
260     }
261 
262     if (!attrs->context_forward_compatible) {
263         /* It's always valid to request a non-forward-compatible context. */
264         return true;
265     }
266 
267     if (attrs->context_api != WAFFLE_CONTEXT_OPENGL) {
268         wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
269                      "To request a forward-compatible context, the context "
270                      "API must be WAFFLE_CONTEXT_OPENGL");
271         return false;
272 
273     }
274 
275     if (wcore_config_attrs_version_lt(attrs, 30)) {
276         wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
277                      "To request a forward-compatible context, the context "
278                      "version must be at least 3.0");
279         return false;
280     }
281 
282     return true;
283 }
284 
285 static bool
set_misc_defaults(struct wcore_config_attrs * attrs)286 set_misc_defaults(struct wcore_config_attrs *attrs)
287 {
288     // Per the GLX [1] and EGL [2] specs, the default value of each size
289     // attribute and `samples` is 0.
290     //
291     // [1] GLX 1.4 spec (2005.12.16), Table 3.4
292     // [2] EGL 1.4 spec (2011.04.06), Table 3.4
293 
294     attrs->context_debug        = false;
295     attrs->context_robust       = false;
296 
297     attrs->rgba_size            = 0;
298     attrs->red_size             = 0;
299     attrs->green_size           = 0;
300     attrs->blue_size            = 0;
301     attrs->alpha_size           = 0;
302     attrs->depth_size           = 0;
303     attrs->stencil_size         = 0;
304     attrs->sample_buffers       = 0;
305     attrs->samples              = 0;
306     attrs->double_buffered      = true;
307     attrs->accum_buffer         = false;
308 
309     return true;
310 }
311 
312 static bool
parse_misc(struct wcore_config_attrs * attrs,const int32_t attrib_list[])313 parse_misc(struct wcore_config_attrs *attrs,
314            const int32_t attrib_list[])
315 {
316     for (int32_t i = 0; attrib_list[i]; i += 2) {
317         int32_t key = attrib_list[i + 0];
318         int32_t value = attrib_list[i + 1];
319 
320         switch (key) {
321 
322             #define CASE_INT(enum_name, struct_memb)                           \
323                 case enum_name:                                                \
324                     if (value < -1) {                                          \
325                         wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,               \
326                                      #enum_name " has bad value %d", value);   \
327                                      return false;                             \
328                     }                                                          \
329                     else {                                                     \
330                         /*                                                     \
331                          * Pass WAFFLE_DONT_CARE to platform module.           \
332                          *                                                     \
333                          * From the GLX 1.4 (2005.12.16) spec:                 \
334                          *     GLX_DONT_CARE may be specified for all          \
335                          *     attributes except GLX_LEVEL.                    \
336                          *                                                     \
337                          * From the EGL 1.4 (2011.04.06) spec:                 \
338                          *     EGL_DONT_CARE may be specified for all          \
339                          *     attributes except EGL_LEVEL and                 \
340                          *     EGL_MATCH_NATIVE_PIXMAP.                        \
341                          */                                                    \
342                         attrs->struct_memb = value;                            \
343                     }                                                          \
344                     break;
345 
346             #define CASE_BOOL(enum_name, struct_memb, default_value)               \
347                 case enum_name:                                                    \
348                     if (!parse_bool(attrib_list, enum_name,                        \
349                                     &attrs->struct_memb, default_value)) {         \
350                         return false;                                              \
351                     }                                                              \
352                 break;
353 
354             case WAFFLE_CONTEXT_API:
355             case WAFFLE_CONTEXT_MAJOR_VERSION:
356             case WAFFLE_CONTEXT_MINOR_VERSION:
357             case WAFFLE_CONTEXT_PROFILE:
358             case WAFFLE_CONTEXT_FORWARD_COMPATIBLE:
359                 // These keys have already been parsed.
360                 break;
361 
362             CASE_INT(WAFFLE_RED_SIZE, red_size)
363             CASE_INT(WAFFLE_GREEN_SIZE, green_size)
364             CASE_INT(WAFFLE_BLUE_SIZE, blue_size)
365             CASE_INT(WAFFLE_ALPHA_SIZE, alpha_size)
366             CASE_INT(WAFFLE_DEPTH_SIZE, depth_size)
367             CASE_INT(WAFFLE_STENCIL_SIZE, stencil_size)
368             CASE_INT(WAFFLE_SAMPLES, samples)
369 
370             CASE_BOOL(WAFFLE_CONTEXT_DEBUG, context_debug, false);
371             CASE_BOOL(WAFFLE_CONTEXT_ROBUST_ACCESS, context_robust, false);
372             CASE_BOOL(WAFFLE_SAMPLE_BUFFERS, sample_buffers, DEFAULT_SAMPLE_BUFFERS);
373             CASE_BOOL(WAFFLE_DOUBLE_BUFFERED, double_buffered, DEFAULT_DOUBLE_BUFFERED);
374             CASE_BOOL(WAFFLE_ACCUM_BUFFER, accum_buffer, DEFAULT_ACCUM_BUFFER);
375 
376             default:
377                 wcore_error_internal("%s", "bad attribute key should have "
378                                      "been found by check_keys()");
379                 return false;
380 
381 
382             #undef CASE_INT
383             #undef CASE_BOOL
384         }
385     }
386 
387     // Calculate rgb_size.
388     attrs->rgb_size = 0;
389     if (attrs->red_size != WAFFLE_DONT_CARE)
390         attrs->rgb_size += attrs->red_size;
391     if (attrs->green_size != WAFFLE_DONT_CARE)
392         attrs->rgb_size += attrs->green_size;
393     if (attrs->blue_size != WAFFLE_DONT_CARE)
394         attrs->rgb_size += attrs->blue_size;
395 
396     // Calculate rgba_size.
397     attrs->rgba_size = attrs->rgb_size;
398     if (attrs->alpha_size != WAFFLE_DONT_CARE)
399         attrs->rgba_size += attrs->alpha_size;
400 
401     return true;
402 }
403 
404 static bool
check_final(struct wcore_config_attrs * attrs)405 check_final(struct wcore_config_attrs *attrs)
406 {
407     if (attrs->context_api == WAFFLE_CONTEXT_OPENGL
408         && attrs->context_profile == WAFFLE_CONTEXT_CORE_PROFILE
409         && wcore_config_attrs_version_ge(attrs, 32)
410         && attrs->accum_buffer) {
411         wcore_errorf(WAFFLE_ERROR_BAD_ATTRIBUTE,
412                      "%s", "WAFFLE_ACCUM_BUFFER must be false for"
413                      "OpenGL Core profile");
414         return false;
415     }
416 
417     return true;
418 }
419 
420 bool
wcore_config_attrs_parse(const int32_t waffle_attrib_list[],struct wcore_config_attrs * attrs)421 wcore_config_attrs_parse(
422       const int32_t waffle_attrib_list[],
423       struct wcore_config_attrs *attrs)
424 {
425     memset(attrs, 0, sizeof(*attrs));
426 
427     if (!check_keys(waffle_attrib_list))
428         return false;
429 
430     if (!parse_context_api(attrs, waffle_attrib_list))
431         return false;
432 
433     if (!set_context_version_default(attrs))
434         return false;
435 
436     if (!parse_context_version(attrs, waffle_attrib_list))
437         return false;
438 
439     if (!set_context_profile_default(attrs))
440         return false;
441 
442     if (!parse_context_profile(attrs, waffle_attrib_list))
443         return false;
444 
445     if (!parse_context_forward_compatible(attrs, waffle_attrib_list))
446         return false;
447 
448     if (!set_misc_defaults(attrs))
449         return false;
450 
451     if (!parse_misc(attrs, waffle_attrib_list))
452         return false;
453 
454     if (!check_final(attrs))
455         return false;
456 
457     return true;
458 }
459 
460 bool
wcore_config_attrs_version_eq(const struct wcore_config_attrs * attrs,int merged_version)461 wcore_config_attrs_version_eq(
462       const struct wcore_config_attrs *attrs,
463       int merged_version)
464 {
465     return
466         attrs->context_major_version == (merged_version / 10) &&
467         attrs->context_minor_version == (merged_version % 10);
468 }
469 
470 bool
wcore_config_attrs_version_gt(const struct wcore_config_attrs * attrs,int merged_version)471 wcore_config_attrs_version_gt(
472       const struct wcore_config_attrs *attrs,
473       int merged_version)
474 {
475     return
476         attrs->context_major_version > (merged_version / 10) ||
477         (attrs->context_major_version == (merged_version / 10) &&
478          attrs->context_minor_version > (merged_version % 10));
479 }
480 
481 bool
wcore_config_attrs_version_ge(const struct wcore_config_attrs * attrs,int merged_version)482 wcore_config_attrs_version_ge(
483       const struct wcore_config_attrs *attrs,
484       int merged_version)
485 {
486     return
487         attrs->context_major_version > (merged_version / 10) ||
488         (attrs->context_major_version == (merged_version / 10) &&
489          attrs->context_minor_version >= (merged_version % 10));
490 }
491 
492 bool
wcore_config_attrs_version_lt(const struct wcore_config_attrs * attrs,int merged_version)493 wcore_config_attrs_version_lt(
494       const struct wcore_config_attrs *attrs,
495       int merged_version)
496 {
497     return !wcore_config_attrs_version_ge(attrs, merged_version);
498 }
499 
500 bool
wcore_config_attrs_version_le(const struct wcore_config_attrs * attrs,int merged_version)501 wcore_config_attrs_version_le(
502       const struct wcore_config_attrs *attrs,
503       int merged_version)
504 {
505     return !wcore_config_attrs_version_gt(attrs, merged_version);
506 }
507