1/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/gpu/gl/GrGLExtensions.h"
9#include "src/gpu/gl/GrGLDefines.h"
10#include "src/gpu/gl/GrGLUtil.h"
11
12#include "src/core/SkTSearch.h"
13#include "src/core/SkTSort.h"
14#include "src/utils/SkJSONWriter.h"
15
16namespace { // This cannot be static because it is used as a template parameter.
17inline bool extension_compare(const SkString& a, const SkString& b) {
18 return strcmp(a.c_str(), b.c_str()) < 0;
19}
20} // namespace
21
22// finds the index of ext in strings or a negative result if ext is not found.
23static int find_string(const SkTArray<SkString>& strings, const char ext[]) {
24 if (strings.empty()) {
25 return -1;
26 }
27 SkString extensionStr(ext);
28 int idx = SkTSearch<SkString, extension_compare>(&strings.front(),
29 strings.count(),
30 extensionStr,
31 sizeof(SkString));
32 return idx;
33}
34
35GrGLExtensions::GrGLExtensions(const GrGLExtensions& that) {
36 *this = that;
37}
38
39GrGLExtensions& GrGLExtensions::operator=(const GrGLExtensions& that) {
40 if (this != &that) {
41 fStrings = that.fStrings;
42 fInitialized = that.fInitialized;
43 }
44 return *this;
45}
46
47static void eat_space_sep_strings(SkTArray<SkString>* out, const char in[]) {
48 if (!in) {
49 return;
50 }
51 while (true) {
52 // skip over multiple spaces between extensions
53 while (' ' == *in) {
54 ++in;
55 }
56 // quit once we reach the end of the string.
57 if ('\0' == *in) {
58 break;
59 }
60 // we found an extension
61 size_t length = strcspn(in, " ");
62 out->push_back().set(in, length);
63 in += length;
64 }
65}
66
67bool GrGLExtensions::init(GrGLStandard standard,
68 GrGLFunction<GrGLGetStringFn> getString,
69 GrGLFunction<GrGLGetStringiFn> getStringi,
70 GrGLFunction<GrGLGetIntegervFn> getIntegerv,
71 GrGLFunction<GrEGLQueryStringFn> queryString,
72 GrEGLDisplay eglDisplay) {
73 fInitialized = false;
74 fStrings.reset();
75
76 if (!getString) {
77 return false;
78 }
79
80 const GrGLubyte* verString = getString(GR_GL_VERSION);
81 GrGLVersion version = GrGLGetVersionFromString((const char*) verString);
82 if (GR_GL_INVALID_VER == version) {
83 return false;
84 }
85
86 bool indexed = false;
87 if (GR_IS_GR_GL(standard) || GR_IS_GR_GL_ES(standard)) {
88 // glGetStringi and indexed extensions were added in version 3.0 of desktop GL and ES.
89 indexed = version >= GR_GL_VER(3, 0);
90 } else if (GR_IS_GR_WEBGL(standard)) {
91 // WebGL (1.0 or 2.0) doesn't natively support glGetStringi, but enscripten adds it in
92 // https://github.com/emscripten-core/emscripten/issues/3472
93 indexed = version >= GR_GL_VER(2, 0);
94 }
95
96 if (indexed) {
97 if (!getStringi || !getIntegerv) {
98 return false;
99 }
100 GrGLint extensionCnt = 0;
101 getIntegerv(GR_GL_NUM_EXTENSIONS, &extensionCnt);
102 fStrings.push_back_n(extensionCnt);
103 for (int i = 0; i < extensionCnt; ++i) {
104 const char* ext = (const char*) getStringi(GR_GL_EXTENSIONS, i);
105 fStrings[i] = ext;
106 }
107 } else {
108 const char* extensions = (const char*) getString(GR_GL_EXTENSIONS);
109 if (!extensions) {
110 return false;
111 }
112 eat_space_sep_strings(&fStrings, extensions);
113 }
114 if (queryString) {
115 const char* extensions = queryString(eglDisplay, GR_EGL_EXTENSIONS);
116
117 eat_space_sep_strings(&fStrings, extensions);
118 }
119 if (!fStrings.empty()) {
120 SkTQSort(fStrings.begin(), fStrings.end(), extension_compare);
121 }
122 fInitialized = true;
123 return true;
124}
125
126bool GrGLExtensions::has(const char ext[]) const {
127 SkASSERT(fInitialized);
128 return find_string(fStrings, ext) >= 0;
129}
130
131bool GrGLExtensions::remove(const char ext[]) {
132 SkASSERT(fInitialized);
133 int idx = find_string(fStrings, ext);
134 if (idx < 0) {
135 return false;
136 }
137
138 // This is not terribly effecient but we really only expect this function to be called at
139 // most a handful of times when our test programs start.
140 fStrings.removeShuffle(idx);
141 if (idx != fStrings.count()) {
142 SkTInsertionSort(fStrings.begin() + idx, fStrings.size() - idx, extension_compare);
143 }
144 return true;
145}
146
147void GrGLExtensions::add(const char ext[]) {
148 int idx = find_string(fStrings, ext);
149 if (idx < 0) {
150 // This is not the most effecient approach since we end up looking at all of the
151 // extensions after the add
152 fStrings.emplace_back(ext);
153 SkTInsertionSort(fStrings.begin(), fStrings.size(), extension_compare);
154 }
155}
156
157#ifdef SK_ENABLE_DUMP_GPU
158void GrGLExtensions::dumpJSON(SkJSONWriter* writer) const {
159 writer->beginArray();
160 for (int i = 0; i < fStrings.count(); ++i) {
161 writer->appendString(fStrings[i].c_str());
162 }
163 writer->endArray();
164}
165#else
166void GrGLExtensions::dumpJSON(SkJSONWriter* writer) const { }
167#endif
168