Bug 1048724 - Sort out Transform Feedback Varyings. r=jgilbert
authorDan Glastonbury <dglastonbury@mozilla.com>
Tue, 21 Apr 2015 11:02:34 +1000
changeset 241026 ee381f0c4b9351d1b4c84aaf9b5331bb44293fa1
parent 241025 1835de92a1bd0e0c652fbd92b5deaebd7b769162
child 241027 022beda1707ce51d7ffe1e03bd0002e9a3c95bd8
push id58997
push userdglastonbury@mozilla.com
push dateFri, 24 Apr 2015 22:45:07 +0000
treeherdermozilla-inbound@ee381f0c4b93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1048724
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1048724 - Sort out Transform Feedback Varyings. r=jgilbert
dom/canvas/WebGL2ContextTransformFeedback.cpp
dom/canvas/WebGLContextBuffers.cpp
dom/canvas/WebGLProgram.cpp
dom/canvas/WebGLProgram.h
dom/canvas/WebGLShader.cpp
dom/canvas/WebGLShader.h
--- a/dom/canvas/WebGL2ContextTransformFeedback.cpp
+++ b/dom/canvas/WebGL2ContextTransformFeedback.cpp
@@ -200,57 +200,22 @@ WebGL2Context::TransformFeedbackVaryings
                                          GLenum bufferMode)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("transformFeedbackVaryings: program", program))
         return;
 
-    GLsizei count = varyings.Length();
-    GLchar** tmpVaryings = (GLchar**) moz_xmalloc(count * sizeof(GLchar*));
-
-    for (GLsizei n = 0; n < count; n++) {
-        tmpVaryings[n] = (GLchar*) ToNewCString(varyings[n]);
-    }
-
-    GLuint progname = program->mGLName;
-    MakeContextCurrent();
-    gl->fTransformFeedbackVaryings(progname, count, tmpVaryings, bufferMode);
-
-    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, tmpVaryings);
+    program->TransformFeedbackVaryings(varyings, bufferMode);
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGL2Context::GetTransformFeedbackVarying(WebGLProgram* program, GLuint index)
 {
     if (IsContextLost())
         return nullptr;
 
     if (!ValidateObject("getTransformFeedbackVarying: program", program))
         return nullptr;
 
-    MakeContextCurrent();
-
-    GLint len = 0;
-    GLuint progname = program->mGLName;
-    gl->fGetProgramiv(progname, LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &len);
-    if (!len)
-        return nullptr;
-
-    UniquePtr<char[]> name(new char[len]);
-    GLint tfsize = 0;
-    GLuint tftype = 0;
-
-    gl->fGetTransformFeedbackVarying(progname, index, len, &len, &tfsize, &tftype, name.get());
-    if (len == 0 || tfsize == 0 || tftype == 0)
-        return nullptr;
-
-    MOZ_CRASH("todo");
-    /*
-    // Reverse lookup of name
-    nsCString reverseMappedName;
-    prog->ReverveMapIdentifier(nsDependentCString(name), &reverseMappedName);
-
-    nsRefPtr<WebGLActiveInfo> result = new WebGLActiveInfo(tfsize, tftype, nsDependentCString(name.get()));
-    return result.forget();
-    */
+    return program->GetTransformFeedbackVarying(index);
 }
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -88,21 +88,23 @@ WebGLContext::BindBufferBase(GLenum targ
         return;
 
     // ValidateBufferTarget
     switch (target) {
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
         if (index >= mGLMaxTransformFeedbackSeparateAttribs)
             return ErrorInvalidValue("bindBufferBase: index should be less than "
                                      "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
+        break;
 
     case LOCAL_GL_UNIFORM_BUFFER:
         if (index >= mGLMaxUniformBufferBindings)
             return ErrorInvalidValue("bindBufferBase: index should be less than "
                                      "MAX_UNIFORM_BUFFER_BINDINGS");
+        break;
 
     default:
         return ErrorInvalidEnumInfo("bindBufferBase: target", target);
     }
 
     if (!ValidateBufferForTarget(target, buffer, "bindBufferBase"))
         return;
 
@@ -126,21 +128,24 @@ WebGLContext::BindBufferRange(GLenum tar
         return;
 
     // ValidateBufferTarget
     switch (target) {
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
         if (index >= mGLMaxTransformFeedbackSeparateAttribs)
             return ErrorInvalidValue("bindBufferRange: index should be less than "
                                      "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
+        break;
 
     case LOCAL_GL_UNIFORM_BUFFER:
         if (index >= mGLMaxUniformBufferBindings)
             return ErrorInvalidValue("bindBufferRange: index should be less than "
                                      "MAX_UNIFORM_BUFFER_BINDINGS");
+        break;
+
     default:
         return ErrorInvalidEnumInfo("bindBufferRange: target", target);
     }
 
     if (!ValidateBufferForTarget(target, buffer, "bindBufferRange"))
         return;
 
     WebGLContextUnchecked::BindBufferRange(target, index, buffer, offset, size);
@@ -490,16 +495,17 @@ WebGLContext::GetBufferSlotByTarget(GLen
 WebGLRefPtr<WebGLBuffer>&
 WebGLContext::GetBufferSlotByTargetIndexed(GLenum target, GLuint index)
 {
     /* This function assumes that target has been validated for either WebGL1 or WebGL. */
     switch (target) {
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
         MOZ_ASSERT(index < mGLMaxTransformFeedbackSeparateAttribs);
         return mBoundTransformFeedbackBuffers[index];
+
     case LOCAL_GL_UNIFORM_BUFFER:
         MOZ_ASSERT(index < mGLMaxUniformBufferBindings);
         return mBoundUniformBuffers[index];
 
     default:
         MOZ_CRASH("Should not get here.");
     }
 }
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -225,17 +225,17 @@ QueryProgramInfo(WebGLProgram* prog, gl:
 
     // Uniform Blocks
 
     if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
         GLuint numActiveUniformBlocks = 0;
         gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCKS,
                           (GLint*)&numActiveUniformBlocks);
 
-        for (GLuint i = 0; i < numActiveAttribs; i++) {
+        for (GLuint i = 0; i < numActiveUniformBlocks; i++) {
             nsAutoCString mappedName;
             mappedName.SetLength(maxUniformBlockLenWithNull - 1);
 
             GLint lengthWithoutNull;
             gl->fGetActiveUniformBlockiv(prog->mGLName, i, LOCAL_GL_UNIFORM_BLOCK_NAME_LENGTH, &lengthWithoutNull);
             gl->fGetActiveUniformBlockName(prog->mGLName, i, maxUniformBlockLenWithNull, &lengthWithoutNull, mappedName.BeginWriting());
             mappedName.SetLength(lengthWithoutNull);
 
@@ -290,16 +290,17 @@ CreateProgram(gl::GLContext* gl)
 {
     gl->MakeCurrent();
     return gl->fCreateProgram();
 }
 
 WebGLProgram::WebGLProgram(WebGLContext* webgl)
     : WebGLContextBoundObject(webgl)
     , mGLName(CreateProgram(webgl->GL()))
+    , mTransformFeedbackBufferMode(LOCAL_GL_NONE)
 {
     mContext->mPrograms.insertBack(this);
 }
 
 void
 WebGLProgram::Delete()
 {
     gl::GLContext* gl = mContext->GL();
@@ -427,16 +428,17 @@ WebGLProgram::GetActiveAttrib(GLuint ind
     nsRefPtr<WebGLActiveInfo> ret =  activeList[index];
     return ret.forget();
 }
 
 already_AddRefed<WebGLActiveInfo>
 WebGLProgram::GetActiveUniform(GLuint index) const
 {
     if (!mMostRecentLinkInfo) {
+        // According to the spec, this can return null.
         nsRefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
         return ret.forget();
     }
 
     const auto& activeList = mMostRecentLinkInfo->activeUniforms;
 
     if (index >= activeList.size()) {
         mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
@@ -527,20 +529,22 @@ WebGLProgram::GetProgramParameter(GLenum
 {
     gl::GLContext* gl = mContext->gl;
     gl->MakeCurrent();
 
     if (mContext->IsWebGL2()) {
         switch (pname) {
         case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
             return JS::Int32Value(GetProgramiv(gl, mGLName, pname));
-        }
+
+        case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS:
+            return JS::Int32Value(mTransformFeedbackVaryings.size());
+       }
     }
 
-
     switch (pname) {
     case LOCAL_GL_ATTACHED_SHADERS:
     case LOCAL_GL_ACTIVE_UNIFORMS:
     case LOCAL_GL_ACTIVE_ATTRIBUTES:
         return JS::Int32Value(GetProgramiv(gl, mGLName, pname));
 
     case LOCAL_GL_DELETE_STATUS:
         return JS::BooleanValue(IsDeleteRequested());
@@ -814,16 +818,25 @@ WebGLProgram::LinkProgram()
     // This can't be done trivially, because we have to deal with mapped attrib names.
     for (auto itr = mBoundAttribLocs.begin(); itr != mBoundAttribLocs.end(); ++itr) {
         const nsCString& name = itr->first;
         GLuint index = itr->second;
 
         mVertShader->BindAttribLocation(mGLName, name, index);
     }
 
+    if (!mTransformFeedbackVaryings.empty()) {
+        // Bind the transform feedback varyings.
+        // This can't be done trivially, because we have to deal with mapped names too.
+        mVertShader->ApplyTransformFeedbackVaryings(mGLName,
+                                                    mTransformFeedbackVaryings,
+                                                    mTransformFeedbackBufferMode,
+                                                    &mTempMappedVaryings);
+    }
+
     if (LinkAndUpdate())
         return true;
 
     // Failed link.
     if (mContext->ShouldGenerateWarnings()) {
         // report shader/program infoLogs as warnings.
         // note that shader compilation errors can be deferred to linkProgram,
         // which is why we can't do anything in compileShader. In practice we could
@@ -891,16 +904,21 @@ WebGLProgram::LinkAndUpdate()
     gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&logLenWithNull);
     if (logLenWithNull > 1) {
         mLinkLog.SetLength(logLenWithNull - 1);
         gl->fGetProgramInfoLog(mGLName, logLenWithNull, nullptr, mLinkLog.BeginWriting());
     } else {
         mLinkLog.SetLength(0);
     }
 
+    // Post link, temporary mapped varying names for transform feedback can be discarded.
+    // The memory can only be deleted after log is queried or the link status will fail.
+    std::vector<std::string> empty;
+    empty.swap(mTempMappedVaryings);
+
     GLint ok = 0;
     gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
     if (!ok)
         return false;
 
     mMostRecentLinkInfo = QueryProgramInfo(this, gl);
 
     MOZ_ASSERT(mMostRecentLinkInfo);
@@ -929,16 +947,81 @@ WebGLProgram::FindUniformByMappedName(co
         return true;
 
     if (mFragShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
         return true;
 
     return false;
 }
 
+void
+WebGLProgram::TransformFeedbackVaryings(const dom::Sequence<nsString>& varyings,
+                                        GLenum bufferMode)
+{
+    if (bufferMode != LOCAL_GL_INTERLEAVED_ATTRIBS &&
+        bufferMode != LOCAL_GL_SEPARATE_ATTRIBS)
+    {
+        mContext->ErrorInvalidEnum("transformFeedbackVaryings: `bufferMode` %s is "
+                                   "invalid. Must be one of gl.INTERLEAVED_ATTRIBS or "
+                                   "gl.SEPARATE_ATTRIBS.",
+                                   mContext->EnumName(bufferMode));
+        return;
+    }
+
+    size_t varyingsCount = varyings.Length();
+    if (bufferMode == LOCAL_GL_SEPARATE_ATTRIBS &&
+        varyingsCount >= mContext->mGLMaxTransformFeedbackSeparateAttribs)
+    {
+        mContext->ErrorInvalidValue("transformFeedbackVaryings: Number of `varyings` exc"
+                                    "eeds gl.MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS.");
+        return;
+    }
+
+    std::vector<nsCString> asciiVaryings;
+    for (size_t i = 0; i < varyingsCount; i++) {
+        if (!ValidateGLSLVariableName(varyings[i], mContext, "transformFeedbackVaryings"))
+            return;
+
+        NS_LossyConvertUTF16toASCII asciiName(varyings[i]);
+        asciiVaryings.push_back(asciiName);
+    }
+
+    // All validated. Translate the strings and store them until
+    // program linking.
+    mTransformFeedbackBufferMode = bufferMode;
+    mTransformFeedbackVaryings.swap(asciiVaryings);
+}
+
+already_AddRefed<WebGLActiveInfo>
+WebGLProgram::GetTransformFeedbackVarying(GLuint index)
+{
+    // No docs in the WebGL 2 spec for this function. Taking the language for
+    // getActiveAttrib, which states that the function returns null on any error.
+    if (!IsLinked()) {
+        mContext->ErrorInvalidOperation("getTransformFeedbackVarying: `program` must be "
+                                        "linked.");
+        return nullptr;
+    }
+
+    if (index >= mTransformFeedbackVaryings.size()) {
+        mContext->ErrorInvalidValue("getTransformFeedbackVarying: `index` is greater or "
+                                    "equal to TRANSFORM_FEEDBACK_VARYINGS.");
+        return nullptr;
+    }
+
+    const nsCString& varyingUserName = mTransformFeedbackVaryings[index];
+
+    WebGLActiveInfo* info;
+    LinkInfo()->FindAttrib(varyingUserName, (const WebGLActiveInfo**) &info);
+    MOZ_ASSERT(info);
+
+    nsRefPtr<WebGLActiveInfo> ret(info);
+    return ret.forget();
+}
+
 bool
 WebGLProgram::FindUniformBlockByMappedName(const nsACString& mappedName,
                                            nsCString* const out_userName,
                                            bool* const out_isArray) const
 {
     if (mVertShader->FindUniformBlockByMappedName(mappedName, out_userName, out_isArray))
         return true;
 
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -168,16 +168,20 @@ public:
                                         nsDependentCString* const out_userName) const;
     bool FindUniformByMappedName(const nsACString& mappedName,
                                  nsCString* const out_userName,
                                  bool* const out_isArray) const;
     bool FindUniformBlockByMappedName(const nsACString& mappedName,
                                       nsCString* const out_userName,
                                       bool* const out_isArray) const;
 
+    void TransformFeedbackVaryings(const dom::Sequence<nsString>& varyings,
+                                   GLenum bufferMode);
+    already_AddRefed<WebGLActiveInfo> GetTransformFeedbackVarying(GLuint index);
+
     bool IsLinked() const { return mMostRecentLinkInfo; }
 
     const webgl::LinkedProgramInfo* LinkInfo() const {
         return mMostRecentLinkInfo.get();
     }
 
     WebGLContext* GetParentObject() const {
         return Context();
@@ -194,15 +198,20 @@ private:
 
 public:
     const GLuint mGLName;
 
 private:
     WebGLRefPtr<WebGLShader> mVertShader;
     WebGLRefPtr<WebGLShader> mFragShader;
     std::map<nsCString, GLuint> mBoundAttribLocs;
+    std::vector<nsCString> mTransformFeedbackVaryings;
+    GLenum mTransformFeedbackBufferMode;
     nsCString mLinkLog;
     RefPtr<const webgl::LinkedProgramInfo> mMostRecentLinkInfo;
+    // Storage for transform feedback varyings before link.
+    // (Work around for bug seen on nVidia drivers.)
+    std::vector<std::string> mTempMappedVaryings;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_PROGRAM_H_
--- a/dom/canvas/WebGLShader.cpp
+++ b/dom/canvas/WebGLShader.cpp
@@ -349,16 +349,54 @@ bool
 WebGLShader::FindUniformBlockByMappedName(const nsACString& mappedName,
                                           nsCString* const out_userName,
                                           bool* const out_isArray) const
 {
     // TODO: Extract block information from shader validator.
     return false;
 }
 
+void
+WebGLShader::ApplyTransformFeedbackVaryings(GLuint prog,
+                                            const std::vector<nsCString>& varyings,
+                                            GLenum bufferMode,
+                                            std::vector<std::string>* out_mappedVaryings) const
+{
+    MOZ_ASSERT(mType == LOCAL_GL_VERTEX_SHADER);
+    MOZ_ASSERT(!varyings.empty());
+    MOZ_ASSERT(out_mappedVaryings);
+
+    const size_t varyingsCount = varyings.size();
+    std::vector<std::string> mappedVaryings;
+
+    for (size_t i = 0; i < varyingsCount; i++) {
+        const nsCString& userName = varyings[i];
+        std::string userNameStr(userName.BeginReading());
+
+        const std::string* mappedNameStr = &userNameStr;
+        // TODO: Are vertex->fragment shader varyings listed under attribs?
+        if (mValidator)
+            mValidator->FindAttribMappedNameByUserName(userNameStr, &mappedNameStr);
+
+        mappedVaryings.push_back(*mappedNameStr);
+    }
+
+    // Temporary, tight packed array of string pointers into mappedVaryings.
+    std::vector<const GLchar*> strings;
+    strings.resize(varyingsCount);
+    for (size_t i = 0; i < varyingsCount; i++) {
+        strings[i] = mappedVaryings[i].c_str();
+    }
+
+    mContext->MakeContextCurrent();
+    mContext->gl->fTransformFeedbackVaryings(prog, varyingsCount, &strings[0], bufferMode);
+
+    out_mappedVaryings->swap(mappedVaryings);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Boilerplate
 
 JSObject*
 WebGLShader::WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto)
 {
     return dom::WebGLShaderBinding::Wrap(js, this, aGivenProto);
 }
--- a/dom/canvas/WebGLShader.h
+++ b/dom/canvas/WebGLShader.h
@@ -54,16 +54,21 @@ public:
     bool FindUniformBlockByMappedName(const nsACString& mappedName,
                                       nsCString* const out_userName,
                                       bool* const out_isArray) const;
 
     bool IsCompiled() const {
         return mTranslationSuccessful && mCompilationSuccessful;
     }
 
+    void ApplyTransformFeedbackVaryings(GLuint prog,
+                                        const std::vector<nsCString>& varyings,
+                                        GLenum bufferMode,
+                                        std::vector<std::string>* out_mappedVaryings) const;
+
     // Other funcs
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
     void Delete();
 
     WebGLContext* GetParentObject() const { return Context(); }
 
     virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto) override;