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