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 COMPUTER, 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 COMPUTER, 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#include "platform/graphics/angle/ANGLEPlatformBridge.h"
29
30#include "wtf/OwnPtr.h"
31#include "wtf/PassOwnPtr.h"
32
33namespace WebCore {
34
35typedef size_t ANGLEGetInfoType;
36
37inline static ANGLEGetInfoType getValidationResultValue(const ShHandle compiler, ShShaderInfo shaderInfo)
38{
39    ANGLEGetInfoType value = 0;
40    ShGetInfo(compiler, shaderInfo, &value);
41    return value;
42}
43
44static bool getSymbolInfo(ShHandle compiler, ShShaderInfo symbolType, Vector<ANGLEShaderSymbol>& symbols)
45{
46    ShShaderInfo symbolMaxNameLengthType;
47
48    switch (symbolType) {
49    case SH_ACTIVE_ATTRIBUTES:
50        symbolMaxNameLengthType = SH_ACTIVE_ATTRIBUTE_MAX_LENGTH;
51        break;
52    case SH_ACTIVE_UNIFORMS:
53        symbolMaxNameLengthType = SH_ACTIVE_UNIFORM_MAX_LENGTH;
54        break;
55    default:
56        ASSERT_NOT_REACHED();
57        return false;
58    }
59
60    ANGLEGetInfoType numSymbols = getValidationResultValue(compiler, symbolType);
61
62    ANGLEGetInfoType maxNameLength = getValidationResultValue(compiler, symbolMaxNameLengthType);
63    if (maxNameLength <= 1)
64        return false;
65
66    ANGLEGetInfoType maxMappedNameLength = getValidationResultValue(compiler, SH_MAPPED_NAME_MAX_LENGTH);
67    if (maxMappedNameLength <= 1)
68        return false;
69
70    // The maximum allowed symbol name length is 256 characters.
71    Vector<char, 256> nameBuffer(maxNameLength);
72    Vector<char, 256> mappedNameBuffer(maxMappedNameLength);
73
74    for (ANGLEGetInfoType i = 0; i < numSymbols; ++i) {
75        ANGLEShaderSymbol symbol;
76        ANGLEGetInfoType nameLength = 0;
77        switch (symbolType) {
78        case SH_ACTIVE_ATTRIBUTES:
79            symbol.symbolType = SHADER_SYMBOL_TYPE_ATTRIBUTE;
80#if ANGLE_SH_VERSION >= 112
81            ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &symbol.precision, &symbol.staticUse, nameBuffer.data(), mappedNameBuffer.data());
82#else
83            ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &symbol.precision, nameBuffer.data(), mappedNameBuffer.data());
84#endif
85            break;
86        case SH_ACTIVE_UNIFORMS:
87            symbol.symbolType = SHADER_SYMBOL_TYPE_UNIFORM;
88#if ANGLE_SH_VERSION >= 112
89            ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &symbol.precision, &symbol.staticUse, nameBuffer.data(), mappedNameBuffer.data());
90#else
91            ShGetVariableInfo(compiler, symbolType, i, &nameLength, &symbol.size, &symbol.dataType, &symbol.precision, nameBuffer.data(), mappedNameBuffer.data());
92#endif
93            break;
94        default:
95            ASSERT_NOT_REACHED();
96            return false;
97        }
98        if (!nameLength)
99            return false;
100
101        // The ShGetActive* calls above are guaranteed to produce null-terminated strings for
102        // nameBuffer and mappedNameBuffer. Also, the character set for symbol names
103        // is a subset of Latin-1 as specified by the OpenGL ES Shading Language, Section 3.1 and
104        // WebGL, Section "Characters Outside the GLSL Source Character Set".
105
106        String name = String(nameBuffer.data());
107        String mappedName = String(mappedNameBuffer.data());
108
109        // ANGLE returns array names in the format "array[0]".
110        // The only way to know if a symbol is an array is to check if it ends with "[0]".
111        // We can't check the size because regular symbols and arrays of length 1 both have a size of 1.
112        symbol.isArray = name.endsWith("[0]") && mappedName.endsWith("[0]");
113        if (symbol.isArray) {
114            // Add a symbol for the array name without the "[0]" suffix.
115            name.truncate(name.length() - 3);
116            mappedName.truncate(mappedName.length() - 3);
117        }
118
119        symbol.name = name;
120        symbol.mappedName = mappedName;
121        symbols.append(symbol);
122
123        if (symbol.isArray) {
124            // Add symbols for each array element.
125            symbol.isArray = false;
126            for (int i = 0; i < symbol.size; i++) {
127                String arrayBrackets = "[" + String::number(i) + "]";
128                symbol.name = name + arrayBrackets;
129                symbol.mappedName = mappedName + arrayBrackets;
130                symbols.append(symbol);
131            }
132        }
133    }
134    return true;
135}
136
137ANGLEPlatformBridge::ANGLEPlatformBridge(ShShaderOutput shaderOutput, ShShaderSpec shaderSpec)
138    : builtCompilers(false)
139    , m_fragmentCompiler(0)
140    , m_vertexCompiler(0)
141    , m_shaderOutput(shaderOutput)
142    , m_shaderSpec(shaderSpec)
143{
144    // This is a no-op if it's already initialized.
145    ShInitialize();
146}
147
148ANGLEPlatformBridge::~ANGLEPlatformBridge()
149{
150    cleanupCompilers();
151}
152
153void ANGLEPlatformBridge::cleanupCompilers()
154{
155    if (m_fragmentCompiler)
156        ShDestruct(m_fragmentCompiler);
157    m_fragmentCompiler = 0;
158    if (m_vertexCompiler)
159        ShDestruct(m_vertexCompiler);
160    m_vertexCompiler = 0;
161
162    builtCompilers = false;
163}
164
165void ANGLEPlatformBridge::setResources(ShBuiltInResources resources)
166{
167    // Resources are (possibly) changing - cleanup compilers if we had them already
168    cleanupCompilers();
169
170    m_resources = resources;
171}
172
173bool ANGLEPlatformBridge::compileShaderSource(const char* shaderSource, ANGLEShaderType shaderType, String& translatedShaderSource, String& shaderValidationLog, Vector<ANGLEShaderSymbol>& symbols, int extraCompileOptions)
174{
175    if (!builtCompilers) {
176        m_fragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, m_shaderSpec, m_shaderOutput, &m_resources);
177        m_vertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, m_shaderSpec, m_shaderOutput, &m_resources);
178        if (!m_fragmentCompiler || !m_vertexCompiler) {
179            cleanupCompilers();
180            return false;
181        }
182
183        builtCompilers = true;
184    }
185
186    ShHandle compiler;
187
188    if (shaderType == SHADER_TYPE_VERTEX)
189        compiler = m_vertexCompiler;
190    else
191        compiler = m_fragmentCompiler;
192
193    const char* const shaderSourceStrings[] = { shaderSource };
194
195#if ANGLE_SH_VERSION >= 111
196    bool validateSuccess = ShCompile(compiler, shaderSourceStrings, 1, SH_OBJECT_CODE | SH_VARIABLES | extraCompileOptions);
197#else
198    bool validateSuccess = ShCompile(compiler, shaderSourceStrings, 1, SH_OBJECT_CODE | SH_ATTRIBUTES_UNIFORMS | extraCompileOptions);
199#endif
200    if (!validateSuccess) {
201        int logSize = getValidationResultValue(compiler, SH_INFO_LOG_LENGTH);
202        if (logSize > 1) {
203            OwnPtr<char[]> logBuffer = adoptArrayPtr(new char[logSize]);
204            if (logBuffer) {
205                ShGetInfoLog(compiler, logBuffer.get());
206                shaderValidationLog = logBuffer.get();
207            }
208        }
209        return false;
210    }
211
212    int translationLength = getValidationResultValue(compiler, SH_OBJECT_CODE_LENGTH);
213    if (translationLength > 1) {
214        OwnPtr<char[]> translationBuffer = adoptArrayPtr(new char[translationLength]);
215        if (!translationBuffer)
216            return false;
217        ShGetObjectCode(compiler, translationBuffer.get());
218        translatedShaderSource = translationBuffer.get();
219    }
220
221    if (!getSymbolInfo(compiler, SH_ACTIVE_ATTRIBUTES, symbols))
222        return false;
223    if (!getSymbolInfo(compiler, SH_ACTIVE_UNIFORMS, symbols))
224        return false;
225
226    return true;
227}
228
229}
230