1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#if USE(3D_GRAPHICS)
29
30#include "ANGLEWebKitBridge.h"
31#include "Logging.h"
32#include <wtf/StdLibExtras.h>
33
34namespace WebCore {
35
36// Temporary typedef to support an incompatible change in the ANGLE API.
37#if !defined(ANGLE_SH_VERSION) || ANGLE_SH_VERSION < 108
38typedef int ANGLEGetInfoType;
39#else
40typedef size_t ANGLEGetInfoType;
41#endif
42
43inline static ANGLEGetInfoType getValidationResultValue(const ShHandle compiler, ShShaderInfo shaderInfo)
44{
45    ANGLEGetInfoType value = 0;
46    ShGetInfo(compiler, shaderInfo, &value);
47    return value;
48}
49
50static bool getSymbolInfo(ShHandle compiler, ShShaderInfo symbolType, Vector<ANGLEShaderSymbol>& symbols)
51{
52    ShShaderInfo symbolMaxNameLengthType;
53
54    switch (symbolType) {
55    case SH_ACTIVE_ATTRIBUTES:
56        symbolMaxNameLengthType = SH_ACTIVE_ATTRIBUTE_MAX_LENGTH;
57        break;
58    case SH_ACTIVE_UNIFORMS:
59        symbolMaxNameLengthType = SH_ACTIVE_UNIFORM_MAX_LENGTH;
60        break;
61    case SH_VARYINGS:
62        symbolMaxNameLengthType = SH_VARYING_MAX_LENGTH;
63        break;
64    default:
65        ASSERT_NOT_REACHED();
66        return false;
67    }
68
69    ANGLEGetInfoType numSymbols = getValidationResultValue(compiler, symbolType);
70
71    ANGLEGetInfoType maxNameLength = getValidationResultValue(compiler, symbolMaxNameLengthType);
72    if (maxNameLength <= 1)
73        return false;
74
75    ANGLEGetInfoType maxMappedNameLength = getValidationResultValue(compiler, SH_MAPPED_NAME_MAX_LENGTH);
76    if (maxMappedNameLength <= 1)
77        return false;
78
79    // The maximum allowed symbol name length is 256 characters.
80    Vector<char, 256> nameBuffer(maxNameLength);
81    Vector<char, 256> mappedNameBuffer(maxMappedNameLength);
82
83    for (ANGLEGetInfoType i = 0; i < numSymbols; ++i) {
84        ANGLEShaderSymbol symbol;
85        ANGLEGetInfoType nameLength = 0;
86        ShPrecisionType precision;
87        int staticUse;
88        switch (symbolType) {
89        case SH_ACTIVE_ATTRIBUTES:
90            symbol.symbolType = SHADER_SYMBOL_TYPE_ATTRIBUTE;
91            ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &precision, &staticUse, nameBuffer.data(), mappedNameBuffer.data());
92            break;
93        case SH_ACTIVE_UNIFORMS:
94            symbol.symbolType = SHADER_SYMBOL_TYPE_UNIFORM;
95            ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &precision, &staticUse, nameBuffer.data(), mappedNameBuffer.data());
96            break;
97        case SH_VARYINGS:
98            symbol.symbolType = SHADER_SYMBOL_TYPE_VARYING;
99            ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &precision, &staticUse, nameBuffer.data(), mappedNameBuffer.data());
100            break;
101        default:
102            ASSERT_NOT_REACHED();
103            return false;
104        }
105        if (!nameLength)
106            return false;
107
108        // The ShGetActive* calls above are guaranteed to produce null-terminated strings for
109        // nameBuffer and mappedNameBuffer. Also, the character set for symbol names
110        // is a subset of Latin-1 as specified by the OpenGL ES Shading Language, Section 3.1 and
111        // WebGL, Section "Characters Outside the GLSL Source Character Set".
112
113        String name = String(nameBuffer.data());
114        String mappedName = String(mappedNameBuffer.data());
115        LOG(WebGL, "Map shader symbol %s -> %s\n", name.utf8().data(), mappedName.utf8().data());
116
117        // ANGLE returns array names in the format "array[0]".
118        // The only way to know if a symbol is an array is to check if it ends with "[0]".
119        // We can't check the size because regular symbols and arrays of length 1 both have a size of 1.
120        symbol.isArray = name.endsWith("[0]") && mappedName.endsWith("[0]");
121        if (symbol.isArray) {
122            // Add a symbol for the array name without the "[0]" suffix.
123            name.truncate(name.length() - 3);
124            mappedName.truncate(mappedName.length() - 3);
125        }
126
127        symbol.name = name;
128        symbol.mappedName = mappedName;
129        symbol.precision = precision;
130        symbol.staticUse = staticUse;
131        symbols.append(symbol);
132
133        if (symbol.isArray) {
134            // Add symbols for each array element.
135            symbol.isArray = false;
136            for (int i = 0; i < symbol.size; i++) {
137                String arrayBrackets = "[" + String::number(i) + "]";
138                symbol.name = name + arrayBrackets;
139                symbol.mappedName = mappedName + arrayBrackets;
140                symbols.append(symbol);
141            }
142        }
143    }
144    return true;
145}
146
147ANGLEWebKitBridge::ANGLEWebKitBridge(ShShaderOutput shaderOutput, ShShaderSpec shaderSpec)
148    : builtCompilers(false)
149    , m_fragmentCompiler(0)
150    , m_vertexCompiler(0)
151    , m_shaderOutput(shaderOutput)
152    , m_shaderSpec(shaderSpec)
153{
154    // This is a no-op if it's already initialized.
155    ShInitialize();
156}
157
158ANGLEWebKitBridge::~ANGLEWebKitBridge()
159{
160    cleanupCompilers();
161}
162
163void ANGLEWebKitBridge::cleanupCompilers()
164{
165    if (m_fragmentCompiler)
166        ShDestruct(m_fragmentCompiler);
167    m_fragmentCompiler = 0;
168    if (m_vertexCompiler)
169        ShDestruct(m_vertexCompiler);
170    m_vertexCompiler = 0;
171
172    builtCompilers = false;
173}
174
175void ANGLEWebKitBridge::setResources(ShBuiltInResources resources)
176{
177    // Resources are (possibly) changing - cleanup compilers if we had them already
178    cleanupCompilers();
179
180    m_resources = resources;
181}
182
183bool ANGLEWebKitBridge::compileShaderSource(const char* shaderSource, ANGLEShaderType shaderType, String& translatedShaderSource, String& shaderValidationLog, Vector<ANGLEShaderSymbol>& symbols, int extraCompileOptions)
184{
185    if (!builtCompilers) {
186        m_fragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, m_shaderSpec, m_shaderOutput, &m_resources);
187        m_vertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, m_shaderSpec, m_shaderOutput, &m_resources);
188        if (!m_fragmentCompiler || !m_vertexCompiler) {
189            cleanupCompilers();
190            return false;
191        }
192
193        builtCompilers = true;
194    }
195
196    ShHandle compiler;
197
198    if (shaderType == SHADER_TYPE_VERTEX)
199        compiler = m_vertexCompiler;
200    else
201        compiler = m_fragmentCompiler;
202
203    const char* const shaderSourceStrings[] = { shaderSource };
204
205    bool validateSuccess = ShCompile(compiler, shaderSourceStrings, 1, SH_OBJECT_CODE | SH_VARIABLES | extraCompileOptions);
206    if (!validateSuccess) {
207        int logSize = getValidationResultValue(compiler, SH_INFO_LOG_LENGTH);
208        if (logSize > 1) {
209            auto logBuffer = std::make_unique<char[]>(logSize);
210            if (logBuffer) {
211                ShGetInfoLog(compiler, logBuffer.get());
212                shaderValidationLog = logBuffer.get();
213            }
214        }
215        return false;
216    }
217
218    int translationLength = getValidationResultValue(compiler, SH_OBJECT_CODE_LENGTH);
219    if (translationLength > 1) {
220        auto translationBuffer = std::make_unique<char[]>(translationLength);
221        if (!translationBuffer)
222            return false;
223        ShGetObjectCode(compiler, translationBuffer.get());
224        translatedShaderSource = translationBuffer.get();
225    }
226
227    if (!getSymbolInfo(compiler, SH_ACTIVE_ATTRIBUTES, symbols))
228        return false;
229    if (!getSymbolInfo(compiler, SH_ACTIVE_UNIFORMS, symbols))
230        return false;
231    if (!getSymbolInfo(compiler, SH_VARYINGS, symbols))
232        return false;
233
234    return true;
235}
236
237}
238
239#endif // USE(3D_GRAPHICS)
240