diff options
Diffstat (limited to 'dom/canvas/WebGLShader.cpp')
-rw-r--r-- | dom/canvas/WebGLShader.cpp | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/dom/canvas/WebGLShader.cpp b/dom/canvas/WebGLShader.cpp new file mode 100644 index 0000000000..033e97fe2b --- /dev/null +++ b/dom/canvas/WebGLShader.cpp @@ -0,0 +1,190 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WebGLShader.h" + +#include "GLSLANG/ShaderLang.h" +#include "GLContext.h" +#include "mozilla/dom/WebGLRenderingContextBinding.h" +#include "mozilla/MemoryReporting.h" +#include "nsPrintfCString.h" +#include "nsString.h" +#include "prenv.h" +#include "WebGLContext.h" +#include "WebGLObjectModel.h" +#include "WebGLShaderValidator.h" +#include "WebGLValidateStrings.h" + +namespace mozilla { + +static void PrintLongString(const char* const begin, const size_t len) { + // Wow - Roll Your Own Foreach-Lines because printf_stderr has a hard-coded + // internal size, so long strings are truncated. + + const size_t chunkSize = 1000; + auto buf = std::vector<char>(chunkSize + 1); // +1 for null-term + const auto bufBegin = buf.data(); + + auto chunkBegin = begin; + const auto end = begin + len; + while (chunkBegin + chunkSize < end) { + memcpy(bufBegin, chunkBegin, chunkSize); + printf_stderr("%s", bufBegin); + chunkBegin += chunkSize; + } + printf_stderr("%s", chunkBegin); +} + +template <size_t N> +static bool SubstringStartsWith(const std::string& testStr, size_t offset, + const char (&refStr)[N]) { + for (size_t i = 0; i < N - 1; i++) { + if (testStr[offset + i] != refStr[i]) return false; + } + return true; +} + +static void GetCompilationStatusAndLog(gl::GLContext* gl, GLuint shader, + bool* const out_success, + std::string* const out_log) { + GLint compileStatus = LOCAL_GL_FALSE; + gl->fGetShaderiv(shader, LOCAL_GL_COMPILE_STATUS, &compileStatus); + + // It's simpler if we always get the log. + GLint lenWithNull = 0; + gl->fGetShaderiv(shader, LOCAL_GL_INFO_LOG_LENGTH, &lenWithNull); + if (lenWithNull < 1) { + lenWithNull = 1; + } + std::vector<char> buffer(lenWithNull); + gl->fGetShaderInfoLog(shader, buffer.size(), nullptr, buffer.data()); + *out_log = buffer.data(); + + *out_success = (compileStatus == LOCAL_GL_TRUE); +} + +//////////////////////////////////////////////////////////////////////////////// + +WebGLShader::WebGLShader(WebGLContext* webgl, GLenum type) + : WebGLContextBoundObject(webgl), + mGLName(webgl->gl->fCreateShader(type)), + mType(type) { + mCompileResults = std::make_unique<webgl::ShaderValidatorResults>(); +} + +WebGLShader::~WebGLShader() { + if (!mContext) return; + mContext->gl->fDeleteShader(mGLName); +} + +void WebGLShader::ShaderSource(const std::string& u8) { + mSource = CrushGlslToAscii(u8); +} + +void WebGLShader::CompileShader() { + mCompilationSuccessful = false; + + gl::GLContext* gl = mContext->gl; + + static const bool kDumpShaders = PR_GetEnv("MOZ_WEBGL_DUMP_SHADERS"); + if (MOZ_UNLIKELY(kDumpShaders)) { + printf_stderr("==== begin MOZ_WEBGL_DUMP_SHADERS ====\n"); + PrintLongString(mSource.c_str(), mSource.size()); + } + + { + const auto validator = mContext->CreateShaderValidator(mType); + MOZ_ASSERT(validator); + + mCompileResults = validator->ValidateAndTranslate(mSource.c_str()); + } + + mCompilationLog = mCompileResults->mInfoLog.c_str(); + const auto& success = mCompileResults->mValid; + + if (MOZ_UNLIKELY(kDumpShaders)) { + printf_stderr("\n==== \\/ \\/ \\/ ====\n"); + if (success) { + const auto& translated = mCompileResults->mObjectCode; + PrintLongString(translated.data(), translated.size()); + } else { + printf_stderr("Validation failed:\n%s", + mCompileResults->mInfoLog.c_str()); + } + printf_stderr("\n==== end ====\n"); + } + + if (!success) return; + + const std::array<const char*, 1> parts = { + mCompileResults->mObjectCode.c_str()}; + gl->fShaderSource(mGLName, parts.size(), parts.data(), nullptr); + + gl->fCompileShader(mGLName); + + GetCompilationStatusAndLog(gl, mGLName, &mCompilationSuccessful, + &mCompilationLog); +} + +//////////////////////////////////////////////////////////////////////////////// + +size_t WebGLShader::CalcNumSamplerUniforms() const { + size_t accum = 0; + for (const auto& cur : mCompileResults->mUniforms) { + const auto& type = cur.type; + if (type == LOCAL_GL_SAMPLER_2D || type == LOCAL_GL_SAMPLER_CUBE) { + accum += cur.getArraySizeProduct(); + } + } + return accum; +} + +size_t WebGLShader::NumAttributes() const { + return mCompileResults->mAttributes.size(); +} + +void WebGLShader::BindAttribLocation(GLuint prog, const std::string& userName, + GLuint index) const { + for (const auto& attrib : mCompileResults->mAttributes) { + if (attrib.name == userName) { + mContext->gl->fBindAttribLocation(prog, index, attrib.mappedName.c_str()); + return; + } + } +} + +void WebGLShader::MapTransformFeedbackVaryings( + const std::vector<std::string>& varyings, + std::vector<std::string>* out_mappedVaryings) const { + MOZ_ASSERT(mType == LOCAL_GL_VERTEX_SHADER); + MOZ_ASSERT(out_mappedVaryings); + + out_mappedVaryings->clear(); + out_mappedVaryings->reserve(varyings.size()); + + const auto& shaderVaryings = mCompileResults->mVaryings; + + for (const auto& userName : varyings) { + const auto* mappedName = &userName; + for (const auto& shaderVarying : shaderVaryings) { + if (shaderVarying.name == userName) { + mappedName = &shaderVarying.mappedName; + break; + } + } + out_mappedVaryings->push_back(*mappedName); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Boilerplate + +size_t WebGLShader::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const { + return mallocSizeOf(this) + mSource.size() + 1 + + mCompileResults->SizeOfIncludingThis(mallocSizeOf) + + mCompilationLog.size() + 1; +} + +} // namespace mozilla |