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