• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetVerticesNeededForDraw(PrimitiveMode primitiveMode,GLsizei count,GLsizei primcount)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 
TransformFeedbackState(size_t maxIndexedBuffers)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 
~TransformFeedbackState()60 TransformFeedbackState::~TransformFeedbackState() {}
61 
getIndexedBuffer(size_t idx) const62 const OffsetBindingPointer<Buffer> &TransformFeedbackState::getIndexedBuffer(size_t idx) const
63 {
64     return mIndexedBuffers[idx];
65 }
66 
getIndexedBuffers() const67 const std::vector<OffsetBindingPointer<Buffer>> &TransformFeedbackState::getIndexedBuffers() const
68 {
69     return mIndexedBuffers;
70 }
71 
getPrimitivesDrawn() const72 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 
TransformFeedback(rx::GLImplFactory * implFactory,GLuint id,const Caps & caps)87 TransformFeedback::TransformFeedback(rx::GLImplFactory *implFactory, GLuint id, const Caps &caps)
88     : RefCountObject(id),
89       mState(caps.maxTransformFeedbackSeparateAttributes),
90       mImplementation(implFactory->createTransformFeedback(mState))
91 {
92     ASSERT(mImplementation != nullptr);
93 }
94 
onDestroy(const Context * context)95 void TransformFeedback::onDestroy(const Context *context)
96 {
97     ASSERT(!context || !context->isCurrentTransformFeedback(this));
98     if (mState.mProgram)
99     {
100         mState.mProgram->release(context);
101         mState.mProgram = nullptr;
102     }
103 
104     ASSERT(!mState.mProgram);
105     for (size_t i = 0; i < mState.mIndexedBuffers.size(); i++)
106     {
107         mState.mIndexedBuffers[i].set(context, nullptr, 0, 0);
108     }
109 }
110 
~TransformFeedback()111 TransformFeedback::~TransformFeedback()
112 {
113     SafeDelete(mImplementation);
114 }
115 
setLabel(const Context * context,const std::string & label)116 void TransformFeedback::setLabel(const Context *context, const std::string &label)
117 {
118     mState.mLabel = label;
119 }
120 
getLabel() const121 const std::string &TransformFeedback::getLabel() const
122 {
123     return mState.mLabel;
124 }
125 
begin(const Context * context,PrimitiveMode primitiveMode,Program * program)126 angle::Result TransformFeedback::begin(const Context *context,
127                                        PrimitiveMode primitiveMode,
128                                        Program *program)
129 {
130     ANGLE_TRY(mImplementation->begin(context, primitiveMode));
131     mState.mActive        = true;
132     mState.mPrimitiveMode = primitiveMode;
133     mState.mPaused        = false;
134     mState.mVerticesDrawn = 0;
135     bindProgram(context, program);
136 
137     if (program)
138     {
139         // Compute the number of vertices we can draw before overflowing the bound buffers.
140         auto strides = program->getTransformFeedbackStrides();
141         ASSERT(strides.size() <= mState.mIndexedBuffers.size() && !strides.empty());
142         GLsizeiptr minCapacity = std::numeric_limits<GLsizeiptr>::max();
143         for (size_t index = 0; index < strides.size(); index++)
144         {
145             GLsizeiptr capacity =
146                 GetBoundBufferAvailableSize(mState.mIndexedBuffers[index]) / strides[index];
147             minCapacity = std::min(minCapacity, capacity);
148         }
149         mState.mVertexCapacity = minCapacity;
150     }
151     else
152     {
153         mState.mVertexCapacity = 0;
154     }
155     return angle::Result::Continue;
156 }
157 
end(const Context * context)158 angle::Result TransformFeedback::end(const Context *context)
159 {
160     ANGLE_TRY(mImplementation->end(context));
161     mState.mActive         = false;
162     mState.mPrimitiveMode  = PrimitiveMode::InvalidEnum;
163     mState.mPaused         = false;
164     mState.mVerticesDrawn  = 0;
165     mState.mVertexCapacity = 0;
166     if (mState.mProgram)
167     {
168         mState.mProgram->release(context);
169         mState.mProgram = nullptr;
170     }
171     return angle::Result::Continue;
172 }
173 
pause(const Context * context)174 angle::Result TransformFeedback::pause(const Context *context)
175 {
176     ANGLE_TRY(mImplementation->pause(context));
177     mState.mPaused = true;
178     return angle::Result::Continue;
179 }
180 
resume(const Context * context)181 angle::Result TransformFeedback::resume(const Context *context)
182 {
183     ANGLE_TRY(mImplementation->resume(context));
184     mState.mPaused = false;
185     return angle::Result::Continue;
186 }
187 
isPaused() const188 bool TransformFeedback::isPaused() const
189 {
190     return mState.mPaused;
191 }
192 
getPrimitiveMode() const193 PrimitiveMode TransformFeedback::getPrimitiveMode() const
194 {
195     return mState.mPrimitiveMode;
196 }
197 
checkBufferSpaceForDraw(GLsizei count,GLsizei primcount) const198 bool TransformFeedback::checkBufferSpaceForDraw(GLsizei count, GLsizei primcount) const
199 {
200     auto vertices =
201         mState.mVerticesDrawn + GetVerticesNeededForDraw(mState.mPrimitiveMode, count, primcount);
202     return vertices.IsValid() && vertices.ValueOrDie() <= mState.mVertexCapacity;
203 }
204 
onVerticesDrawn(const Context * context,GLsizei count,GLsizei primcount)205 void TransformFeedback::onVerticesDrawn(const Context *context, GLsizei count, GLsizei primcount)
206 {
207     ASSERT(mState.mActive && !mState.mPaused);
208     // All draws should be validated with checkBufferSpaceForDraw so ValueOrDie should never fail.
209     mState.mVerticesDrawn =
210         (mState.mVerticesDrawn + GetVerticesNeededForDraw(mState.mPrimitiveMode, count, primcount))
211             .ValueOrDie();
212 
213     for (auto &buffer : mState.mIndexedBuffers)
214     {
215         if (buffer.get() != nullptr)
216         {
217             buffer->onDataChanged();
218         }
219     }
220 }
221 
bindProgram(const Context * context,Program * program)222 void TransformFeedback::bindProgram(const Context *context, Program *program)
223 {
224     if (mState.mProgram != program)
225     {
226         if (mState.mProgram != nullptr)
227         {
228             mState.mProgram->release(context);
229         }
230         mState.mProgram = program;
231         if (mState.mProgram != nullptr)
232         {
233             mState.mProgram->addRef();
234         }
235     }
236 }
237 
hasBoundProgram(GLuint program) const238 bool TransformFeedback::hasBoundProgram(GLuint program) const
239 {
240     return mState.mProgram != nullptr && mState.mProgram->id() == program;
241 }
242 
detachBuffer(const Context * context,GLuint bufferName)243 angle::Result TransformFeedback::detachBuffer(const Context *context, GLuint bufferName)
244 {
245     bool isBound = context->isCurrentTransformFeedback(this);
246     for (size_t index = 0; index < mState.mIndexedBuffers.size(); index++)
247     {
248         if (mState.mIndexedBuffers[index].id() == bufferName)
249         {
250             if (isBound)
251             {
252                 mState.mIndexedBuffers[index]->onTFBindingChanged(context, false, true);
253             }
254             mState.mIndexedBuffers[index].set(context, nullptr, 0, 0);
255             ANGLE_TRY(
256                 mImplementation->bindIndexedBuffer(context, index, mState.mIndexedBuffers[index]));
257         }
258     }
259 
260     return angle::Result::Continue;
261 }
262 
bindIndexedBuffer(const Context * context,size_t index,Buffer * buffer,size_t offset,size_t size)263 angle::Result TransformFeedback::bindIndexedBuffer(const Context *context,
264                                                    size_t index,
265                                                    Buffer *buffer,
266                                                    size_t offset,
267                                                    size_t size)
268 {
269     ASSERT(index < mState.mIndexedBuffers.size());
270     bool isBound = context && context->isCurrentTransformFeedback(this);
271     if (isBound && mState.mIndexedBuffers[index].get())
272     {
273         mState.mIndexedBuffers[index]->onTFBindingChanged(context, false, true);
274     }
275     mState.mIndexedBuffers[index].set(context, buffer, offset, size);
276     if (isBound && buffer)
277     {
278         buffer->onTFBindingChanged(context, true, true);
279     }
280 
281     return mImplementation->bindIndexedBuffer(context, index, mState.mIndexedBuffers[index]);
282 }
283 
getIndexedBuffer(size_t index) const284 const OffsetBindingPointer<Buffer> &TransformFeedback::getIndexedBuffer(size_t index) const
285 {
286     ASSERT(index < mState.mIndexedBuffers.size());
287     return mState.mIndexedBuffers[index];
288 }
289 
getIndexedBufferCount() const290 size_t TransformFeedback::getIndexedBufferCount() const
291 {
292     return mState.mIndexedBuffers.size();
293 }
294 
buffersBoundForOtherUse() const295 bool TransformFeedback::buffersBoundForOtherUse() const
296 {
297     for (auto &buffer : mState.mIndexedBuffers)
298     {
299         if (buffer.get() && buffer->isBoundForTransformFeedbackAndOtherUse())
300         {
301             return true;
302         }
303     }
304     return false;
305 }
306 
getImplementation() const307 rx::TransformFeedbackImpl *TransformFeedback::getImplementation() const
308 {
309     return mImplementation;
310 }
311 
onBindingChanged(const Context * context,bool bound)312 void TransformFeedback::onBindingChanged(const Context *context, bool bound)
313 {
314     for (auto &buffer : mState.mIndexedBuffers)
315     {
316         if (buffer.get())
317         {
318             buffer->onTFBindingChanged(context, bound, true);
319         }
320     }
321 }
322 }  // namespace gl
323