• 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,TransformFeedbackID id,const Caps & caps)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 
onDestroy(const Context * context)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 
~TransformFeedback()118 TransformFeedback::~TransformFeedback()
119 {
120     SafeDelete(mImplementation);
121 }
122 
setLabel(const Context * context,const std::string & label)123 void TransformFeedback::setLabel(const Context *context, const std::string &label)
124 {
125     mState.mLabel = label;
126 }
127 
getLabel() const128 const std::string &TransformFeedback::getLabel() const
129 {
130     return mState.mLabel;
131 }
132 
begin(const Context * context,PrimitiveMode primitiveMode,Program * program)133 angle::Result TransformFeedback::begin(const Context *context,
134                                        PrimitiveMode primitiveMode,
135                                        Program *program)
136 {
137     ANGLE_TRY(mImplementation->begin(context, primitiveMode));
138     mState.mActive        = true;
139     mState.mPrimitiveMode = primitiveMode;
140     mState.mPaused        = false;
141     mState.mVerticesDrawn = 0;
142     bindProgram(context, program);
143 
144     if (program)
145     {
146         // Compute the number of vertices we can draw before overflowing the bound buffers.
147         auto strides = program->getTransformFeedbackStrides();
148         ASSERT(strides.size() <= mState.mIndexedBuffers.size() && !strides.empty());
149         GLsizeiptr minCapacity = std::numeric_limits<GLsizeiptr>::max();
150         for (size_t index = 0; index < strides.size(); index++)
151         {
152             GLsizeiptr capacity =
153                 GetBoundBufferAvailableSize(mState.mIndexedBuffers[index]) / strides[index];
154             minCapacity = std::min(minCapacity, capacity);
155         }
156         mState.mVertexCapacity = minCapacity;
157     }
158     else
159     {
160         mState.mVertexCapacity = 0;
161     }
162     return angle::Result::Continue;
163 }
164 
end(const Context * context)165 angle::Result TransformFeedback::end(const Context *context)
166 {
167     ANGLE_TRY(mImplementation->end(context));
168     mState.mActive         = false;
169     mState.mPrimitiveMode  = PrimitiveMode::InvalidEnum;
170     mState.mPaused         = false;
171     mState.mVerticesDrawn  = 0;
172     mState.mVertexCapacity = 0;
173     if (mState.mProgram)
174     {
175         mState.mProgram->release(context);
176         mState.mProgram = nullptr;
177     }
178     return angle::Result::Continue;
179 }
180 
pause(const Context * context)181 angle::Result TransformFeedback::pause(const Context *context)
182 {
183     ANGLE_TRY(mImplementation->pause(context));
184     mState.mPaused = true;
185     return angle::Result::Continue;
186 }
187 
resume(const Context * context)188 angle::Result TransformFeedback::resume(const Context *context)
189 {
190     ANGLE_TRY(mImplementation->resume(context));
191     mState.mPaused = false;
192     return angle::Result::Continue;
193 }
194 
isPaused() const195 bool TransformFeedback::isPaused() const
196 {
197     return mState.mPaused;
198 }
199 
getPrimitiveMode() const200 PrimitiveMode TransformFeedback::getPrimitiveMode() const
201 {
202     return mState.mPrimitiveMode;
203 }
204 
checkBufferSpaceForDraw(GLsizei count,GLsizei primcount) const205 bool TransformFeedback::checkBufferSpaceForDraw(GLsizei count, GLsizei primcount) const
206 {
207     auto vertices =
208         mState.mVerticesDrawn + GetVerticesNeededForDraw(mState.mPrimitiveMode, count, primcount);
209     return vertices.IsValid() && vertices.ValueOrDie() <= mState.mVertexCapacity;
210 }
211 
onVerticesDrawn(const Context * context,GLsizei count,GLsizei primcount)212 void TransformFeedback::onVerticesDrawn(const Context *context, GLsizei count, GLsizei primcount)
213 {
214     ASSERT(mState.mActive && !mState.mPaused);
215     // All draws should be validated with checkBufferSpaceForDraw so ValueOrDie should never fail.
216     mState.mVerticesDrawn =
217         (mState.mVerticesDrawn + GetVerticesNeededForDraw(mState.mPrimitiveMode, count, primcount))
218             .ValueOrDie();
219 
220     for (auto &buffer : mState.mIndexedBuffers)
221     {
222         if (buffer.get() != nullptr)
223         {
224             buffer->onDataChanged();
225         }
226     }
227 }
228 
bindProgram(const Context * context,Program * program)229 void TransformFeedback::bindProgram(const Context *context, Program *program)
230 {
231     if (mState.mProgram != program)
232     {
233         if (mState.mProgram != nullptr)
234         {
235             mState.mProgram->release(context);
236         }
237         mState.mProgram = program;
238         if (mState.mProgram != nullptr)
239         {
240             mState.mProgram->addRef();
241         }
242     }
243 }
244 
hasBoundProgram(ShaderProgramID program) const245 bool TransformFeedback::hasBoundProgram(ShaderProgramID program) const
246 {
247     return mState.mProgram != nullptr && mState.mProgram->id().value == program.value;
248 }
249 
detachBuffer(const Context * context,BufferID bufferID)250 angle::Result TransformFeedback::detachBuffer(const Context *context, BufferID bufferID)
251 {
252     bool isBound = context->isCurrentTransformFeedback(this);
253     for (size_t index = 0; index < mState.mIndexedBuffers.size(); index++)
254     {
255         if (mState.mIndexedBuffers[index].id() == bufferID)
256         {
257             if (isBound)
258             {
259                 mState.mIndexedBuffers[index]->onTFBindingChanged(context, false, true);
260             }
261             mState.mIndexedBuffers[index].set(context, nullptr, 0, 0);
262             ANGLE_TRY(
263                 mImplementation->bindIndexedBuffer(context, index, mState.mIndexedBuffers[index]));
264         }
265     }
266 
267     return angle::Result::Continue;
268 }
269 
bindIndexedBuffer(const Context * context,size_t index,Buffer * buffer,size_t offset,size_t size)270 angle::Result TransformFeedback::bindIndexedBuffer(const Context *context,
271                                                    size_t index,
272                                                    Buffer *buffer,
273                                                    size_t offset,
274                                                    size_t size)
275 {
276     ASSERT(index < mState.mIndexedBuffers.size());
277     bool isBound = context && context->isCurrentTransformFeedback(this);
278     if (isBound && mState.mIndexedBuffers[index].get())
279     {
280         mState.mIndexedBuffers[index]->onTFBindingChanged(context, false, true);
281     }
282     mState.mIndexedBuffers[index].set(context, buffer, offset, size);
283     if (isBound && buffer)
284     {
285         buffer->onTFBindingChanged(context, true, true);
286     }
287 
288     return mImplementation->bindIndexedBuffer(context, index, mState.mIndexedBuffers[index]);
289 }
290 
getIndexedBuffer(size_t index) const291 const OffsetBindingPointer<Buffer> &TransformFeedback::getIndexedBuffer(size_t index) const
292 {
293     ASSERT(index < mState.mIndexedBuffers.size());
294     return mState.mIndexedBuffers[index];
295 }
296 
getIndexedBufferCount() const297 size_t TransformFeedback::getIndexedBufferCount() const
298 {
299     return mState.mIndexedBuffers.size();
300 }
301 
buffersBoundForOtherUse() const302 bool TransformFeedback::buffersBoundForOtherUse() const
303 {
304     for (auto &buffer : mState.mIndexedBuffers)
305     {
306         if (buffer.get() && buffer->isBoundForTransformFeedbackAndOtherUse())
307         {
308             return true;
309         }
310     }
311     return false;
312 }
313 
getImplementation() const314 rx::TransformFeedbackImpl *TransformFeedback::getImplementation() const
315 {
316     return mImplementation;
317 }
318 
onBindingChanged(const Context * context,bool bound)319 void TransformFeedback::onBindingChanged(const Context *context, bool bound)
320 {
321     for (auto &buffer : mState.mIndexedBuffers)
322     {
323         if (buffer.get())
324         {
325             buffer->onTFBindingChanged(context, bound, true);
326         }
327     }
328 }
329 
getIndexedBuffers() const330 const std::vector<OffsetBindingPointer<Buffer>> &TransformFeedback::getIndexedBuffers() const
331 {
332     return mState.mIndexedBuffers;
333 }
334 }  // namespace gl
335