diff options
Diffstat (limited to 'Source/WebCore/html/canvas/WebGLRenderingContext.cpp')
-rw-r--r-- | Source/WebCore/html/canvas/WebGLRenderingContext.cpp | 230 |
1 files changed, 223 insertions, 7 deletions
diff --git a/Source/WebCore/html/canvas/WebGLRenderingContext.cpp b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp index c445e2b..a3b9699 100644 --- a/Source/WebCore/html/canvas/WebGLRenderingContext.cpp +++ b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp @@ -44,6 +44,7 @@ #include "ImageData.h" #include "IntSize.h" #include "NotImplemented.h" +#include "OESStandardDerivatives.h" #include "OESTextureFloat.h" #include "RenderBox.h" #include "RenderLayer.h" @@ -62,6 +63,7 @@ #include <wtf/ByteArray.h> #include <wtf/OwnArrayPtr.h> #include <wtf/PassOwnArrayPtr.h> +#include <wtf/text/StringBuilder.h> namespace WebCore { @@ -101,12 +103,11 @@ namespace { // Return true if a character belongs to the ASCII subset as defined in // GLSL ES 1.0 spec section 3.1. - // We make exceptions for " ' `. bool validateCharacter(unsigned char c) { // Printing characters are valid except " $ ` @ \ ' DEL. if (c >= 32 && c <= 126 - && c != '$' && c != '@' && c != '\\') + && c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' && c != '\'') return true; // Horizontal tab, line feed, vertical tab, form feed, carriage return // are also valid. @@ -115,6 +116,189 @@ namespace { return false; } + // Strips comments from shader text. This allows non-ASCII characters + // to be used in comments without potentially breaking OpenGL + // implementations not expecting characters outside the GLSL ES set. + class StripComments { + public: + StripComments(const String& str) + : m_parseState(BeginningOfLine) + , m_sourceString(str) + , m_length(str.length()) + , m_position(0) + { + parse(); + } + + String result() + { + return m_builder.toString(); + } + + private: + bool hasMoreCharacters() + { + return (m_position < m_length); + } + + void parse() + { + while (hasMoreCharacters()) { + process(current()); + // process() might advance the position. + if (hasMoreCharacters()) + advance(); + } + } + + void process(UChar); + + bool peek(UChar& character) + { + if (m_position + 1 >= m_length) + return false; + character = m_sourceString[m_position + 1]; + return true; + } + + UChar current() + { + ASSERT(m_position < m_length); + return m_sourceString[m_position]; + } + + void advance() + { + ++m_position; + } + + bool isNewline(UChar character) + { + // Don't attempt to canonicalize newline related characters. + return (character == '\n' || character == '\r'); + } + + void emit(UChar character) + { + m_builder.append(character); + } + + enum ParseState { + // Have not seen an ASCII non-whitespace character yet on + // this line. Possible that we might see a preprocessor + // directive. + BeginningOfLine, + + // Have seen at least one ASCII non-whitespace character + // on this line. + MiddleOfLine, + + // Handling a preprocessor directive. Passes through all + // characters up to the end of the line. Disables comment + // processing. + InPreprocessorDirective, + + // Handling a single-line comment. The comment text is + // replaced with a single space. + InSingleLineComment, + + // Handling a multi-line comment. Newlines are passed + // through to preserve line numbers. + InMultiLineComment + }; + + ParseState m_parseState; + String m_sourceString; + unsigned m_length; + unsigned m_position; + StringBuilder m_builder; + }; + + void StripComments::process(UChar c) + { + if (isNewline(c)) { + // No matter what state we are in, pass through newlines + // so we preserve line numbers. + emit(c); + + if (m_parseState != InMultiLineComment) + m_parseState = BeginningOfLine; + + return; + } + + UChar temp = 0; + switch (m_parseState) { + case BeginningOfLine: + if (WTF::isASCIISpace(c)) { + emit(c); + break; + } + + if (c == '#') { + m_parseState = InPreprocessorDirective; + emit(c); + break; + } + + // Transition to normal state and re-handle character. + m_parseState = MiddleOfLine; + process(c); + break; + + case MiddleOfLine: + if (c == '/' && peek(temp)) { + if (temp == '/') { + m_parseState = InSingleLineComment; + emit(' '); + advance(); + break; + } + + if (temp == '*') { + m_parseState = InMultiLineComment; + // Emit the comment start in case the user has + // an unclosed comment and we want to later + // signal an error. + emit('/'); + emit('*'); + advance(); + break; + } + } + + emit(c); + break; + + case InPreprocessorDirective: + // No matter what the character is, just pass it + // through. Do not parse comments in this state. This + // might not be the right thing to do long term, but it + // should handle the #error preprocessor directive. + emit(c); + break; + + case InSingleLineComment: + // The newline code at the top of this function takes care + // of resetting our state when we get out of the + // single-line comment. Swallow all other characters. + break; + + case InMultiLineComment: + if (c == '*' && peek(temp) && temp == '/') { + emit('*'); + emit('/'); + m_parseState = MiddleOfLine; + advance(); + break; + } + + // Swallow all other characters. Unclear whether we may + // want or need to just emit a space per character to try + // to preserve column numbers for debugging purposes. + break; + } + } } // namespace anonymous class WebGLStateRestorer { @@ -1574,6 +1758,14 @@ WebGLExtension* WebGLRenderingContext::getExtension(const String& name) if (isContextLost()) return 0; + if (equalIgnoringCase(name, "OES_standard_derivatives") + && m_context->getExtensions()->supports("GL_OES_standard_derivatives")) { + if (!m_oesStandardDerivatives) { + m_context->getExtensions()->ensureEnabled("GL_OES_standard_derivatives"); + m_oesStandardDerivatives = OESStandardDerivatives::create(); + } + return m_oesStandardDerivatives.get(); + } if (equalIgnoringCase(name, "OES_texture_float") && m_context->getExtensions()->supports("GL_OES_texture_float")) { if (!m_oesTextureFloat) { @@ -1581,7 +1773,8 @@ WebGLExtension* WebGLRenderingContext::getExtension(const String& name) m_oesTextureFloat = OESTextureFloat::create(); } return m_oesTextureFloat.get(); - } else if (equalIgnoringCase(name, "WEBKIT_lose_context")) { + } + if (equalIgnoringCase(name, "WEBKIT_lose_context")) { if (!m_webkitLoseContext) m_webkitLoseContext = WebKitLoseContext::create(this); return m_webkitLoseContext.get(); @@ -1823,6 +2016,11 @@ WebGLGetInfo WebGLRenderingContext::getParameter(GC3Denum pname, ExceptionCode& return WebGLGetInfo("WebGL 1.0 (" + m_context->getString(GraphicsContext3D::VERSION) + ")"); case GraphicsContext3D::VIEWPORT: return getWebGLIntArrayParameter(pname); + case Extensions3D::FRAGMENT_SHADER_DERIVATIVE_HINT_OES: // OES_standard_derivatives + if (m_oesStandardDerivatives) + return getUnsignedLongParameter(Extensions3D::FRAGMENT_SHADER_DERIVATIVE_HINT_OES); + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); default: m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); return WebGLGetInfo(); @@ -1990,6 +2188,8 @@ Vector<String> WebGLRenderingContext::getSupportedExtensions() Vector<String> result; if (m_context->getExtensions()->supports("GL_OES_texture_float")) result.append("OES_texture_float"); + if (m_context->getExtensions()->supports("GL_OES_standard_derivatives")) + result.append("OES_standard_derivatives"); result.append("WEBKIT_lose_context"); return result; } @@ -2234,7 +2434,17 @@ void WebGLRenderingContext::hint(GC3Denum target, GC3Denum mode) { if (isContextLost()) return; - if (target != GraphicsContext3D::GENERATE_MIPMAP_HINT) { + bool isValid = false; + switch (target) { + case GraphicsContext3D::GENERATE_MIPMAP_HINT: + isValid = true; + break; + case Extensions3D::FRAGMENT_SHADER_DERIVATIVE_HINT_OES: // OES_standard_derivatives + if (m_oesStandardDerivatives) + isValid = true; + break; + } + if (!isValid) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); return; } @@ -2547,9 +2757,10 @@ void WebGLRenderingContext::shaderSource(WebGLShader* shader, const String& stri UNUSED_PARAM(ec); if (isContextLost() || !validateWebGLObject(shader)) return; - if (!validateString(string)) + String stringWithoutComments = StripComments(string).result(); + if (!validateString(stringWithoutComments)) return; - m_context->shaderSource(objectOrZero(shader), string); + m_context->shaderSource(objectOrZero(shader), stringWithoutComments); cleanupAfterGraphicsCall(false); } @@ -4352,11 +4563,16 @@ void WebGLRenderingContext::restoreStatesAfterVertexAttrib0Simulation() int WebGLRenderingContext::getNumberOfExtensions() { - return (m_webkitLoseContext ? 1 : 0) + (m_oesTextureFloat ? 1 : 0); + return (m_oesStandardDerivatives ? 1 : 0) + (m_webkitLoseContext ? 1 : 0) + (m_oesTextureFloat ? 1 : 0); } WebGLExtension* WebGLRenderingContext::getExtensionNumber(int i) { + if (m_oesStandardDerivatives) { + if (!i) + return m_oesStandardDerivatives.get(); + --i; + } if (m_webkitLoseContext) { if (!i) return m_webkitLoseContext.get(); |