WebGLTransformFeedback.cpp (4612B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "WebGLTransformFeedback.h" 7 8 #include "GLContext.h" 9 #include "WebGL2Context.h" 10 #include "WebGLBuffer.h" 11 #include "WebGLProgram.h" 12 #include "mozilla/IntegerRange.h" 13 #include "mozilla/dom/WebGL2RenderingContextBinding.h" 14 15 namespace mozilla { 16 17 WebGLTransformFeedback::WebGLTransformFeedback(WebGLContext* webgl, GLuint tf) 18 : WebGLContextBoundObject(webgl), 19 mGLName(tf), 20 mIndexedBindings(webgl::kMaxTransformFeedbackSeparateAttribs), 21 mIsPaused(false), 22 mIsActive(false) {} 23 24 WebGLTransformFeedback::~WebGLTransformFeedback() { 25 if (!mContext) return; 26 if (mGLName) { 27 mContext->gl->fDeleteTransformFeedbacks(1, &mGLName); 28 } 29 } 30 31 //////////////////////////////////////// 32 33 void WebGLTransformFeedback::BeginTransformFeedback(GLenum primMode) { 34 if (mIsActive) return mContext->ErrorInvalidOperation("Already active."); 35 36 switch (primMode) { 37 case LOCAL_GL_POINTS: 38 case LOCAL_GL_LINES: 39 case LOCAL_GL_TRIANGLES: 40 break; 41 default: 42 mContext->ErrorInvalidEnum( 43 "`primitiveMode` must be one of POINTS, LINES, or" 44 " TRIANGLES."); 45 return; 46 } 47 48 const auto& prog = mContext->mCurrentProgram; 49 if (!prog || !prog->IsLinked() || 50 prog->LinkInfo()->componentsPerTFVert.empty()) { 51 mContext->ErrorInvalidOperation( 52 "Current program not valid for transform" 53 " feedback."); 54 return; 55 } 56 57 const auto& linkInfo = prog->LinkInfo(); 58 const auto& componentsPerTFVert = linkInfo->componentsPerTFVert; 59 60 size_t minVertCapacity = SIZE_MAX; 61 for (size_t i = 0; i < componentsPerTFVert.size(); i++) { 62 const auto& indexedBinding = mIndexedBindings[i]; 63 const auto& componentsPerVert = componentsPerTFVert[i]; 64 65 const auto& buffer = indexedBinding.mBufferBinding; 66 if (!buffer) { 67 mContext->ErrorInvalidOperation( 68 "No buffer attached to required transform" 69 " feedback index %u.", 70 (uint32_t)i); 71 return; 72 } 73 74 for (const auto iBound : IntegerRange(mIndexedBindings.size())) { 75 const auto& bound = mIndexedBindings[iBound].mBufferBinding.get(); 76 if (iBound != i && buffer == bound) { 77 mContext->GenErrorIllegalUse( 78 LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, static_cast<uint32_t>(i), 79 LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER, static_cast<uint32_t>(iBound)); 80 return; 81 } 82 } 83 84 const size_t vertCapacity = buffer->ByteLength() / 4 / componentsPerVert; 85 minVertCapacity = std::min(minVertCapacity, vertCapacity); 86 } 87 88 //// 89 90 const auto& gl = mContext->gl; 91 gl->fBeginTransformFeedback(primMode); 92 93 //// 94 95 mIsActive = true; 96 MOZ_ASSERT(!mIsPaused); 97 98 mActive_Program = prog; 99 mActive_PrimMode = primMode; 100 mActive_VertPosition = 0; 101 mActive_VertCapacity = minVertCapacity; 102 103 //// 104 105 mActive_Program->mNumActiveTFOs++; 106 } 107 108 void WebGLTransformFeedback::EndTransformFeedback() { 109 if (!mIsActive) return mContext->ErrorInvalidOperation("Not active."); 110 111 //// 112 113 const auto& gl = mContext->gl; 114 gl->fEndTransformFeedback(); 115 116 if (gl->WorkAroundDriverBugs()) { 117 #ifdef XP_MACOSX 118 // Multi-threaded GL on mac will generate INVALID_OP in some cases for at 119 // least BindBufferBase after an EndTransformFeedback if there is not a 120 // flush between the two. Single-threaded GL does not have this issue. This 121 // is likely due to not synchronizing client/server state, and erroring in 122 // BindBufferBase because the client thinks we're still in transform 123 // feedback. 124 gl->fFlush(); 125 #endif 126 } 127 128 //// 129 130 mIsActive = false; 131 mIsPaused = false; 132 133 //// 134 135 mActive_Program->mNumActiveTFOs--; 136 } 137 138 void WebGLTransformFeedback::PauseTransformFeedback() { 139 if (!mIsActive || mIsPaused) { 140 mContext->ErrorInvalidOperation("Not active or is paused."); 141 return; 142 } 143 144 //// 145 146 const auto& gl = mContext->gl; 147 gl->fPauseTransformFeedback(); 148 149 //// 150 151 mIsPaused = true; 152 } 153 154 void WebGLTransformFeedback::ResumeTransformFeedback() { 155 if (!mIsPaused) return mContext->ErrorInvalidOperation("Not paused."); 156 157 if (mContext->mCurrentProgram != mActive_Program) { 158 mContext->ErrorInvalidOperation("Active program differs from original."); 159 return; 160 } 161 162 //// 163 164 const auto& gl = mContext->gl; 165 gl->fResumeTransformFeedback(); 166 167 //// 168 169 MOZ_ASSERT(mIsActive); 170 mIsPaused = false; 171 } 172 173 } // namespace mozilla