Blame dom/canvas/WebGLShader.cpp

Packit f0b94e
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#include "WebGLShader.h"
Packit f0b94e
Packit f0b94e
#include "GLSLANG/ShaderLang.h"
Packit f0b94e
#include "GLContext.h"
Packit f0b94e
#include "mozilla/dom/WebGLRenderingContextBinding.h"
Packit f0b94e
#include "mozilla/MemoryReporting.h"
Packit f0b94e
#include "nsPrintfCString.h"
Packit f0b94e
#include "nsString.h"
Packit f0b94e
#include "prenv.h"
Packit f0b94e
#include "WebGLContext.h"
Packit f0b94e
#include "WebGLObjectModel.h"
Packit f0b94e
#include "WebGLShaderValidator.h"
Packit f0b94e
#include "WebGLValidateStrings.h"
Packit f0b94e
Packit f0b94e
namespace mozilla {
Packit f0b94e
Packit f0b94e
// On success, writes to out_validator and out_translatedSource.
Packit f0b94e
// On failure, writes to out_translationLog.
Packit f0b94e
static bool Translate(const nsACString& source,
Packit f0b94e
                      webgl::ShaderValidator* validator,
Packit f0b94e
                      nsACString* const out_translationLog,
Packit f0b94e
                      nsACString* const out_translatedSource) {
Packit f0b94e
  if (!validator->ValidateAndTranslate(source.BeginReading())) {
Packit f0b94e
    validator->GetInfoLog(out_translationLog);
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Success
Packit f0b94e
  validator->GetOutput(out_translatedSource);
Packit f0b94e
  return true;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
template <size_t N>
Packit f0b94e
static bool SubstringStartsWith(const std::string& testStr, size_t offset,
Packit f0b94e
                                const char (&refStr)[N]) {
Packit f0b94e
  for (size_t i = 0; i < N - 1; i++) {
Packit f0b94e
    if (testStr[offset + i] != refStr[i]) return false;
Packit f0b94e
  }
Packit f0b94e
  return true;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/* On success, writes to out_translatedSource.
Packit f0b94e
 * On failure, writes to out_translationLog.
Packit f0b94e
 *
Packit f0b94e
 * Requirements:
Packit f0b94e
 *   #version is either omitted, `#version 100`, or `version 300 es`.
Packit f0b94e
 */
Packit f0b94e
static bool TranslateWithoutValidation(const nsACString& sourceNS,
Packit f0b94e
                                       bool isWebGL2,
Packit f0b94e
                                       nsACString* const out_translationLog,
Packit f0b94e
                                       nsACString* const out_translatedSource) {
Packit f0b94e
  std::string source = sourceNS.BeginReading();
Packit f0b94e
Packit f0b94e
  size_t versionStrStart = source.find("#version");
Packit f0b94e
  size_t versionStrLen;
Packit f0b94e
  uint32_t glesslVersion;
Packit f0b94e
Packit f0b94e
  if (versionStrStart != std::string::npos) {
Packit f0b94e
    static const char versionStr100[] = "#version 100\n";
Packit f0b94e
    static const char versionStr300es[] = "#version 300 es\n";
Packit f0b94e
Packit f0b94e
    if (isWebGL2 &&
Packit f0b94e
        SubstringStartsWith(source, versionStrStart, versionStr300es)) {
Packit f0b94e
      glesslVersion = 300;
Packit f0b94e
      versionStrLen = strlen(versionStr300es);
Packit f0b94e
Packit f0b94e
    } else if (SubstringStartsWith(source, versionStrStart, versionStr100)) {
Packit f0b94e
      glesslVersion = 100;
Packit f0b94e
      versionStrLen = strlen(versionStr100);
Packit f0b94e
Packit f0b94e
    } else {
Packit f0b94e
      nsPrintfCString error("#version, if declared, must be %s.",
Packit f0b94e
                            isWebGL2 ? "`100` or `300 es`" : "`100`");
Packit f0b94e
      *out_translationLog = error;
Packit f0b94e
      return false;
Packit f0b94e
    }
Packit f0b94e
  } else {
Packit f0b94e
    versionStrStart = 0;
Packit f0b94e
    versionStrLen = 0;
Packit f0b94e
    glesslVersion = 100;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  std::string reversionedSource = source;
Packit f0b94e
  reversionedSource.erase(versionStrStart, versionStrLen);
Packit f0b94e
Packit f0b94e
  switch (glesslVersion) {
Packit f0b94e
    case 100:
Packit f0b94e
      /* According to ARB_ES2_compatibility extension glsl
Packit f0b94e
       * should accept #version 100 for ES 2 shaders. */
Packit f0b94e
      reversionedSource.insert(versionStrStart, "#version 100\n");
Packit f0b94e
      break;
Packit f0b94e
    case 300:
Packit f0b94e
      reversionedSource.insert(versionStrStart, "#version 330\n");
Packit f0b94e
      break;
Packit f0b94e
    default:
Packit f0b94e
      MOZ_CRASH("GFX: Bad `glesslVersion`.");
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  out_translatedSource->Assign(reversionedSource.c_str(),
Packit f0b94e
                               reversionedSource.length());
Packit f0b94e
  return true;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
static void GetCompilationStatusAndLog(gl::GLContext* gl, GLuint shader,
Packit f0b94e
                                       bool* const out_success,
Packit f0b94e
                                       nsACString* const out_log) {
Packit f0b94e
  GLint compileStatus = LOCAL_GL_FALSE;
Packit f0b94e
  gl->fGetShaderiv(shader, LOCAL_GL_COMPILE_STATUS, &compileStatus);
Packit f0b94e
Packit f0b94e
  // It's simpler if we always get the log.
Packit f0b94e
  GLint lenWithNull = 0;
Packit f0b94e
  gl->fGetShaderiv(shader, LOCAL_GL_INFO_LOG_LENGTH, &lenWithNull);
Packit f0b94e
Packit f0b94e
  if (lenWithNull > 1) {
Packit f0b94e
    // SetLength takes the length without the null.
Packit f0b94e
    out_log->SetLength(lenWithNull - 1);
Packit f0b94e
    gl->fGetShaderInfoLog(shader, lenWithNull, nullptr,
Packit f0b94e
                          out_log->BeginWriting());
Packit f0b94e
  } else {
Packit f0b94e
    out_log->SetLength(0);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  *out_success = (compileStatus == LOCAL_GL_TRUE);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
////////////////////////////////////////////////////////////////////////////////
Packit f0b94e
Packit f0b94e
WebGLShader::WebGLShader(WebGLContext* webgl, GLenum type)
Packit f0b94e
    : WebGLRefCountedObject(webgl),
Packit f0b94e
      mGLName(webgl->gl->fCreateShader(type)),
Packit f0b94e
      mType(type),
Packit f0b94e
      mTranslationSuccessful(false),
Packit f0b94e
      mCompilationSuccessful(false) {
Packit f0b94e
  mContext->mShaders.insertBack(this);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
WebGLShader::~WebGLShader() { DeleteOnce(); }
Packit f0b94e
Packit f0b94e
void WebGLShader::ShaderSource(const nsAString& source) {
Packit f0b94e
  const char funcName[] = "shaderSource";
Packit f0b94e
  nsString sourceWithoutComments;
Packit f0b94e
  if (!TruncateComments(source, &sourceWithoutComments)) {
Packit f0b94e
    mContext->ErrorOutOfMemory(
Packit f0b94e
        "%s: Failed to alloc for empting comment contents.", funcName);
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (!ValidateGLSLPreprocString(mContext, funcName, sourceWithoutComments))
Packit f0b94e
    return;
Packit f0b94e
Packit f0b94e
  // We checked that the source stripped of comments is in the
Packit f0b94e
  // 7-bit ASCII range, so we can skip the NS_IsAscii() check.
Packit f0b94e
  const NS_LossyConvertUTF16toASCII cleanSource(sourceWithoutComments);
Packit f0b94e
Packit f0b94e
  if (PR_GetEnv("MOZ_WEBGL_DUMP_SHADERS")) {
Packit f0b94e
    printf_stderr("////////////////////////////////////////\n");
Packit f0b94e
    printf_stderr("// MOZ_WEBGL_DUMP_SHADERS:\n");
Packit f0b94e
Packit f0b94e
    // Wow - Roll Your Own Foreach-Lines because printf_stderr has a hard-coded
Packit f0b94e
    // internal size, so long strings are truncated.
Packit f0b94e
Packit f0b94e
    const size_t maxChunkSize = 1024 - 1;                   // -1 for null-term.
Packit f0b94e
    const UniqueBuffer buf(moz_xmalloc(maxChunkSize + 1));  // +1 for null-term
Packit f0b94e
    const auto bufBegin = (char*)buf.get();
Packit f0b94e
Packit f0b94e
    size_t chunkStart = 0;
Packit f0b94e
    while (chunkStart != cleanSource.Length()) {
Packit f0b94e
      const auto chunkEnd =
Packit f0b94e
          std::min(chunkStart + maxChunkSize, size_t(cleanSource.Length()));
Packit f0b94e
      const auto chunkSize = chunkEnd - chunkStart;
Packit f0b94e
Packit f0b94e
      memcpy(bufBegin, cleanSource.BeginReading() + chunkStart, chunkSize);
Packit f0b94e
      bufBegin[chunkSize + 1] = '\0';
Packit f0b94e
Packit f0b94e
      printf_stderr("%s", bufBegin);
Packit f0b94e
      chunkStart += chunkSize;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    printf_stderr("////////////////////////////////////////\n");
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  mSource = source;
Packit f0b94e
  mCleanSource = cleanSource;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void WebGLShader::CompileShader() {
Packit f0b94e
  mValidator = nullptr;
Packit f0b94e
  mTranslationSuccessful = false;
Packit f0b94e
  mCompilationSuccessful = false;
Packit f0b94e
Packit f0b94e
  gl::GLContext* gl = mContext->gl;
Packit f0b94e
Packit f0b94e
  mValidator.reset(mContext->CreateShaderValidator(mType));
Packit f0b94e
Packit f0b94e
  bool success;
Packit f0b94e
  if (mValidator) {
Packit f0b94e
    success = Translate(mCleanSource, mValidator.get(), &mValidationLog,
Packit f0b94e
                        &mTranslatedSource);
Packit f0b94e
  } else {
Packit f0b94e
    success = TranslateWithoutValidation(mCleanSource, mContext->IsWebGL2(),
Packit f0b94e
                                         &mValidationLog, &mTranslatedSource);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (!success) return;
Packit f0b94e
Packit f0b94e
  mTranslationSuccessful = true;
Packit f0b94e
Packit f0b94e
  const char* const parts[] = {mTranslatedSource.BeginReading()};
Packit f0b94e
  gl->fShaderSource(mGLName, ArrayLength(parts), parts, nullptr);
Packit f0b94e
Packit f0b94e
  gl->fCompileShader(mGLName);
Packit f0b94e
Packit f0b94e
  GetCompilationStatusAndLog(gl, mGLName, &mCompilationSuccessful,
Packit f0b94e
                             &mCompilationLog);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void WebGLShader::GetShaderInfoLog(nsAString* out) const {
Packit f0b94e
  const nsCString& log =
Packit f0b94e
      !mTranslationSuccessful ? mValidationLog : mCompilationLog;
Packit f0b94e
  CopyASCIItoUTF16(log, *out);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
JS::Value WebGLShader::GetShaderParameter(GLenum pname) const {
Packit f0b94e
  switch (pname) {
Packit f0b94e
    case LOCAL_GL_SHADER_TYPE:
Packit f0b94e
      return JS::NumberValue(mType);
Packit f0b94e
Packit f0b94e
    case LOCAL_GL_DELETE_STATUS:
Packit f0b94e
      return JS::BooleanValue(IsDeleteRequested());
Packit f0b94e
Packit f0b94e
    case LOCAL_GL_COMPILE_STATUS:
Packit f0b94e
      return JS::BooleanValue(mCompilationSuccessful);
Packit f0b94e
Packit f0b94e
    default:
Packit f0b94e
      mContext->ErrorInvalidEnumInfo("getShaderParameter: `pname`", pname);
Packit f0b94e
      return JS::NullValue();
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void WebGLShader::GetShaderSource(nsAString* out) const {
Packit f0b94e
  out->SetIsVoid(false);
Packit f0b94e
  *out = mSource;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void WebGLShader::GetShaderTranslatedSource(nsAString* out) const {
Packit f0b94e
  out->SetIsVoid(false);
Packit f0b94e
  CopyASCIItoUTF16(mTranslatedSource, *out);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
////////////////////////////////////////////////////////////////////////////////
Packit f0b94e
Packit f0b94e
bool WebGLShader::CanLinkTo(const WebGLShader* prev,
Packit f0b94e
                            nsCString* const out_log) const {
Packit f0b94e
  if (!mValidator) return true;
Packit f0b94e
Packit f0b94e
  return mValidator->CanLinkTo(prev->mValidator.get(), out_log);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
size_t WebGLShader::CalcNumSamplerUniforms() const {
Packit f0b94e
  if (mValidator) return mValidator->CalcNumSamplerUniforms();
Packit f0b94e
Packit f0b94e
  // TODO
Packit f0b94e
  return 0;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
size_t WebGLShader::NumAttributes() const {
Packit f0b94e
  if (mValidator) return mValidator->NumAttributes();
Packit f0b94e
Packit f0b94e
  // TODO
Packit f0b94e
  return 0;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void WebGLShader::BindAttribLocation(GLuint prog, const nsCString& userName,
Packit f0b94e
                                     GLuint index) const {
Packit f0b94e
  std::string userNameStr(userName.BeginReading());
Packit f0b94e
Packit f0b94e
  const std::string* mappedNameStr = &userNameStr;
Packit f0b94e
  if (mValidator)
Packit f0b94e
    mValidator->FindAttribMappedNameByUserName(userNameStr, &mappedNameStr);
Packit f0b94e
Packit f0b94e
  mContext->gl->fBindAttribLocation(prog, index, mappedNameStr->c_str());
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool WebGLShader::FindAttribUserNameByMappedName(
Packit f0b94e
    const nsACString& mappedName, nsCString* const out_userName) const {
Packit f0b94e
  if (!mValidator) return false;
Packit f0b94e
Packit f0b94e
  const std::string mappedNameStr(mappedName.BeginReading());
Packit f0b94e
  const std::string* userNameStr;
Packit f0b94e
  if (!mValidator->FindAttribUserNameByMappedName(mappedNameStr, &userNameStr))
Packit f0b94e
    return false;
Packit f0b94e
Packit f0b94e
  *out_userName = userNameStr->c_str();
Packit f0b94e
  return true;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool WebGLShader::FindVaryingByMappedName(const nsACString& mappedName,
Packit f0b94e
                                          nsCString* const out_userName,
Packit f0b94e
                                          bool* const out_isArray) const {
Packit f0b94e
  if (!mValidator) return false;
Packit f0b94e
Packit f0b94e
  const std::string mappedNameStr(mappedName.BeginReading());
Packit f0b94e
  std::string userNameStr;
Packit f0b94e
  if (!mValidator->FindVaryingByMappedName(mappedNameStr, &userNameStr,
Packit f0b94e
                                           out_isArray))
Packit f0b94e
    return false;
Packit f0b94e
Packit f0b94e
  *out_userName = userNameStr.c_str();
Packit f0b94e
  return true;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool WebGLShader::FindUniformByMappedName(const nsACString& mappedName,
Packit f0b94e
                                          nsCString* const out_userName,
Packit f0b94e
                                          bool* const out_isArray) const {
Packit f0b94e
  if (!mValidator) return false;
Packit f0b94e
Packit f0b94e
  const std::string mappedNameStr(mappedName.BeginReading(),
Packit f0b94e
                                  mappedName.Length());
Packit f0b94e
  std::string userNameStr;
Packit f0b94e
  if (!mValidator->FindUniformByMappedName(mappedNameStr, &userNameStr,
Packit f0b94e
                                           out_isArray))
Packit f0b94e
    return false;
Packit f0b94e
Packit f0b94e
  *out_userName = userNameStr.c_str();
Packit f0b94e
  return true;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool WebGLShader::UnmapUniformBlockName(
Packit f0b94e
    const nsACString& baseMappedName, nsCString* const out_baseUserName) const {
Packit f0b94e
  if (!mValidator) {
Packit f0b94e
    *out_baseUserName = baseMappedName;
Packit f0b94e
    return true;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return mValidator->UnmapUniformBlockName(baseMappedName, out_baseUserName);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void WebGLShader::EnumerateFragOutputs(
Packit f0b94e
    std::map<nsCString, const nsCString>& out_FragOutputs) const {
Packit f0b94e
  out_FragOutputs.clear();
Packit f0b94e
Packit f0b94e
  if (!mValidator) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
  mValidator->EnumerateFragOutputs(out_FragOutputs);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void WebGLShader::MapTransformFeedbackVaryings(
Packit f0b94e
    const std::vector<nsString>& varyings,
Packit f0b94e
    std::vector<std::string>* out_mappedVaryings) const {
Packit f0b94e
  MOZ_ASSERT(mType == LOCAL_GL_VERTEX_SHADER);
Packit f0b94e
  MOZ_ASSERT(out_mappedVaryings);
Packit f0b94e
Packit f0b94e
  out_mappedVaryings->clear();
Packit f0b94e
  out_mappedVaryings->reserve(varyings.size());
Packit f0b94e
Packit f0b94e
  for (const auto& wideUserName : varyings) {
Packit f0b94e
    const NS_LossyConvertUTF16toASCII mozUserName(
Packit f0b94e
        wideUserName);  // Don't validate here.
Packit f0b94e
    const std::string userName(mozUserName.BeginReading(),
Packit f0b94e
                               mozUserName.Length());
Packit f0b94e
    const std::string* pMappedName = &userName;
Packit f0b94e
    if (mValidator) {
Packit f0b94e
      mValidator->FindVaryingMappedNameByUserName(userName, &pMappedName);
Packit f0b94e
    }
Packit f0b94e
    out_mappedVaryings->push_back(*pMappedName);
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
////////////////////////////////////////////////////////////////////////////////
Packit f0b94e
// Boilerplate
Packit f0b94e
Packit f0b94e
JSObject* WebGLShader::WrapObject(JSContext* js,
Packit f0b94e
                                  JS::Handle<JSObject*> givenProto) {
Packit f0b94e
  return dom::WebGLShaderBinding::Wrap(js, this, givenProto);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
size_t WebGLShader::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
Packit f0b94e
  size_t validatorSize = mValidator ? mallocSizeOf(mValidator.get()) : 0;
Packit f0b94e
  return mallocSizeOf(this) +
Packit f0b94e
         mSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
Packit f0b94e
         mCleanSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
Packit f0b94e
         validatorSize +
Packit f0b94e
         mValidationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
Packit f0b94e
         mTranslatedSource.SizeOfExcludingThisIfUnshared(mallocSizeOf) +
Packit f0b94e
         mCompilationLog.SizeOfExcludingThisIfUnshared(mallocSizeOf);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void WebGLShader::Delete() {
Packit f0b94e
  gl::GLContext* gl = mContext->GL();
Packit f0b94e
Packit f0b94e
  gl->fDeleteShader(mGLName);
Packit f0b94e
Packit f0b94e
  LinkedListElement<WebGLShader>::removeFrom(mContext->mShaders);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLShader)
Packit f0b94e
Packit f0b94e
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLShader, AddRef)
Packit f0b94e
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLShader, Release)
Packit f0b94e
Packit f0b94e
}  // namespace mozilla