TransformFeedback.cpp (10571B)
1 // 2 // Copyright 2014 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 7 #include "libANGLE/TransformFeedback.h" 8 9 #include "common/mathutil.h" 10 #include "libANGLE/Buffer.h" 11 #include "libANGLE/Caps.h" 12 #include "libANGLE/Context.h" 13 #include "libANGLE/Program.h" 14 #include "libANGLE/State.h" 15 #include "libANGLE/renderer/GLImplFactory.h" 16 #include "libANGLE/renderer/TransformFeedbackImpl.h" 17 18 #include <limits> 19 20 namespace gl 21 { 22 23 angle::CheckedNumeric<GLsizeiptr> GetVerticesNeededForDraw(PrimitiveMode primitiveMode, 24 GLsizei count, 25 GLsizei primcount) 26 { 27 if (count < 0 || primcount < 0) 28 { 29 return 0; 30 } 31 // Transform feedback only outputs complete primitives, so we need to round down to the nearest 32 // complete primitive before multiplying by the number of instances. 33 angle::CheckedNumeric<GLsizeiptr> checkedCount = count; 34 angle::CheckedNumeric<GLsizeiptr> checkedPrimcount = primcount; 35 switch (primitiveMode) 36 { 37 case PrimitiveMode::Triangles: 38 return checkedPrimcount * (checkedCount - checkedCount % 3); 39 case PrimitiveMode::Lines: 40 return checkedPrimcount * (checkedCount - checkedCount % 2); 41 case PrimitiveMode::Points: 42 return checkedPrimcount * checkedCount; 43 default: 44 UNREACHABLE(); 45 return checkedPrimcount * checkedCount; 46 } 47 } 48 49 TransformFeedbackState::TransformFeedbackState(size_t maxIndexedBuffers) 50 : mLabel(), 51 mActive(false), 52 mPrimitiveMode(PrimitiveMode::InvalidEnum), 53 mPaused(false), 54 mVerticesDrawn(0), 55 mVertexCapacity(0), 56 mProgram(nullptr), 57 mIndexedBuffers(maxIndexedBuffers) 58 {} 59 60 TransformFeedbackState::~TransformFeedbackState() {} 61 62 const OffsetBindingPointer<Buffer> &TransformFeedbackState::getIndexedBuffer(size_t idx) const 63 { 64 return mIndexedBuffers[idx]; 65 } 66 67 const std::vector<OffsetBindingPointer<Buffer>> &TransformFeedbackState::getIndexedBuffers() const 68 { 69 return mIndexedBuffers; 70 } 71 72 GLsizeiptr TransformFeedbackState::getPrimitivesDrawn() const 73 { 74 switch (mPrimitiveMode) 75 { 76 case gl::PrimitiveMode::Points: 77 return mVerticesDrawn; 78 case gl::PrimitiveMode::Lines: 79 return mVerticesDrawn / 2; 80 case gl::PrimitiveMode::Triangles: 81 return mVerticesDrawn / 3; 82 default: 83 return 0; 84 } 85 } 86 87 TransformFeedback::TransformFeedback(rx::GLImplFactory *implFactory, 88 TransformFeedbackID id, 89 const Caps &caps) 90 : RefCountObject(implFactory->generateSerial(), id), 91 mState(caps.maxTransformFeedbackSeparateAttributes), 92 mImplementation(implFactory->createTransformFeedback(mState)) 93 { 94 ASSERT(mImplementation != nullptr); 95 } 96 97 void TransformFeedback::onDestroy(const Context *context) 98 { 99 ASSERT(!context || !context->isCurrentTransformFeedback(this)); 100 if (mState.mProgram) 101 { 102 mState.mProgram->release(context); 103 mState.mProgram = nullptr; 104 } 105 106 ASSERT(!mState.mProgram); 107 for (size_t i = 0; i < mState.mIndexedBuffers.size(); i++) 108 { 109 mState.mIndexedBuffers[i].set(context, nullptr, 0, 0); 110 } 111 112 if (mImplementation) 113 { 114 mImplementation->onDestroy(context); 115 } 116 } 117 118 TransformFeedback::~TransformFeedback() 119 { 120 SafeDelete(mImplementation); 121 } 122 123 angle::Result TransformFeedback::setLabel(const Context *context, const std::string &label) 124 { 125 mState.mLabel = label; 126 127 if (mImplementation) 128 { 129 return mImplementation->onLabelUpdate(context); 130 } 131 return angle::Result::Continue; 132 } 133 134 const std::string &TransformFeedback::getLabel() const 135 { 136 return mState.mLabel; 137 } 138 139 angle::Result TransformFeedback::begin(const Context *context, 140 PrimitiveMode primitiveMode, 141 Program *program) 142 { 143 // TODO: http://anglebug.com/5486: This method should take in as parameter a 144 // ProgramExecutable instead of a Program. 145 146 ANGLE_TRY(mImplementation->begin(context, primitiveMode)); 147 mState.mActive = true; 148 mState.mPrimitiveMode = primitiveMode; 149 mState.mPaused = false; 150 mState.mVerticesDrawn = 0; 151 bindProgram(context, program); 152 153 // In one of the angle_unittests - "TransformFeedbackTest.SideEffectsOfStartAndStop" 154 // there is a code path where <context> is a nullptr, account for that possiblity. 155 const ProgramExecutable *programExecutable = 156 context ? context->getState().getLinkedProgramExecutable(context) : nullptr; 157 if (programExecutable) 158 { 159 // Compute the number of vertices we can draw before overflowing the bound buffers. 160 auto strides = programExecutable->getTransformFeedbackStrides(); 161 ASSERT(strides.size() <= mState.mIndexedBuffers.size() && !strides.empty()); 162 GLsizeiptr minCapacity = std::numeric_limits<GLsizeiptr>::max(); 163 for (size_t index = 0; index < strides.size(); index++) 164 { 165 GLsizeiptr capacity = 166 GetBoundBufferAvailableSize(mState.mIndexedBuffers[index]) / strides[index]; 167 minCapacity = std::min(minCapacity, capacity); 168 } 169 mState.mVertexCapacity = minCapacity; 170 } 171 else 172 { 173 mState.mVertexCapacity = 0; 174 } 175 return angle::Result::Continue; 176 } 177 178 angle::Result TransformFeedback::end(const Context *context) 179 { 180 ANGLE_TRY(mImplementation->end(context)); 181 mState.mActive = false; 182 mState.mPrimitiveMode = PrimitiveMode::InvalidEnum; 183 mState.mPaused = false; 184 mState.mVerticesDrawn = 0; 185 mState.mVertexCapacity = 0; 186 if (mState.mProgram) 187 { 188 mState.mProgram->release(context); 189 mState.mProgram = nullptr; 190 } 191 return angle::Result::Continue; 192 } 193 194 angle::Result TransformFeedback::pause(const Context *context) 195 { 196 ANGLE_TRY(mImplementation->pause(context)); 197 mState.mPaused = true; 198 return angle::Result::Continue; 199 } 200 201 angle::Result TransformFeedback::resume(const Context *context) 202 { 203 ANGLE_TRY(mImplementation->resume(context)); 204 mState.mPaused = false; 205 return angle::Result::Continue; 206 } 207 208 bool TransformFeedback::isPaused() const 209 { 210 return mState.mPaused; 211 } 212 213 PrimitiveMode TransformFeedback::getPrimitiveMode() const 214 { 215 return mState.mPrimitiveMode; 216 } 217 218 bool TransformFeedback::checkBufferSpaceForDraw(GLsizei count, GLsizei primcount) const 219 { 220 auto vertices = 221 mState.mVerticesDrawn + GetVerticesNeededForDraw(mState.mPrimitiveMode, count, primcount); 222 return vertices.IsValid() && vertices.ValueOrDie() <= mState.mVertexCapacity; 223 } 224 225 void TransformFeedback::onVerticesDrawn(const Context *context, GLsizei count, GLsizei primcount) 226 { 227 ASSERT(mState.mActive && !mState.mPaused); 228 // All draws should be validated with checkBufferSpaceForDraw so ValueOrDie should never fail. 229 mState.mVerticesDrawn = 230 (mState.mVerticesDrawn + GetVerticesNeededForDraw(mState.mPrimitiveMode, count, primcount)) 231 .ValueOrDie(); 232 233 for (auto &buffer : mState.mIndexedBuffers) 234 { 235 if (buffer.get() != nullptr) 236 { 237 buffer->onDataChanged(); 238 } 239 } 240 } 241 242 void TransformFeedback::bindProgram(const Context *context, Program *program) 243 { 244 if (mState.mProgram != program) 245 { 246 if (mState.mProgram != nullptr) 247 { 248 mState.mProgram->release(context); 249 } 250 mState.mProgram = program; 251 if (mState.mProgram != nullptr) 252 { 253 mState.mProgram->addRef(); 254 } 255 } 256 } 257 258 bool TransformFeedback::hasBoundProgram(ShaderProgramID program) const 259 { 260 return mState.mProgram != nullptr && mState.mProgram->id().value == program.value; 261 } 262 263 angle::Result TransformFeedback::detachBuffer(const Context *context, BufferID bufferID) 264 { 265 bool isBound = context->isCurrentTransformFeedback(this); 266 for (size_t index = 0; index < mState.mIndexedBuffers.size(); index++) 267 { 268 if (mState.mIndexedBuffers[index].id() == bufferID) 269 { 270 if (isBound) 271 { 272 mState.mIndexedBuffers[index]->onTFBindingChanged(context, false, true); 273 } 274 mState.mIndexedBuffers[index].set(context, nullptr, 0, 0); 275 ANGLE_TRY( 276 mImplementation->bindIndexedBuffer(context, index, mState.mIndexedBuffers[index])); 277 } 278 } 279 280 return angle::Result::Continue; 281 } 282 283 angle::Result TransformFeedback::bindIndexedBuffer(const Context *context, 284 size_t index, 285 Buffer *buffer, 286 size_t offset, 287 size_t size) 288 { 289 ASSERT(index < mState.mIndexedBuffers.size()); 290 bool isBound = context && context->isCurrentTransformFeedback(this); 291 if (isBound && mState.mIndexedBuffers[index].get()) 292 { 293 mState.mIndexedBuffers[index]->onTFBindingChanged(context, false, true); 294 } 295 mState.mIndexedBuffers[index].set(context, buffer, offset, size); 296 if (isBound && buffer) 297 { 298 buffer->onTFBindingChanged(context, true, true); 299 } 300 301 return mImplementation->bindIndexedBuffer(context, index, mState.mIndexedBuffers[index]); 302 } 303 304 const OffsetBindingPointer<Buffer> &TransformFeedback::getIndexedBuffer(size_t index) const 305 { 306 ASSERT(index < mState.mIndexedBuffers.size()); 307 return mState.mIndexedBuffers[index]; 308 } 309 310 size_t TransformFeedback::getIndexedBufferCount() const 311 { 312 return mState.mIndexedBuffers.size(); 313 } 314 315 bool TransformFeedback::buffersBoundForOtherUseInWebGL() const 316 { 317 for (auto &buffer : mState.mIndexedBuffers) 318 { 319 if (buffer.get() && buffer->hasWebGLXFBBindingConflict(true)) 320 { 321 return true; 322 } 323 } 324 return false; 325 } 326 327 rx::TransformFeedbackImpl *TransformFeedback::getImplementation() const 328 { 329 return mImplementation; 330 } 331 332 void TransformFeedback::onBindingChanged(const Context *context, bool bound) 333 { 334 for (auto &buffer : mState.mIndexedBuffers) 335 { 336 if (buffer.get()) 337 { 338 buffer->onTFBindingChanged(context, bound, true); 339 } 340 } 341 } 342 343 const std::vector<OffsetBindingPointer<Buffer>> &TransformFeedback::getIndexedBuffers() const 344 { 345 return mState.mIndexedBuffers; 346 } 347 } // namespace gl