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