xref: /aosp_15_r20/external/angle/src/libANGLE/TransformFeedback.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 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 
getLabel() const134 const std::string &TransformFeedback::getLabel() const
135 {
136     return mState.mLabel;
137 }
138 
begin(const Context * context,PrimitiveMode primitiveMode,Program * program)139 angle::Result TransformFeedback::begin(const Context *context,
140                                        PrimitiveMode primitiveMode,
141                                        Program *program)
142 {
143     // TODO: http://anglebug.com/42264023: 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 
end(const Context * context)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 
pause(const Context * context)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 
resume(const Context * context)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 
isPaused() const208 bool TransformFeedback::isPaused() const
209 {
210     return mState.mPaused;
211 }
212 
getPrimitiveMode() const213 PrimitiveMode TransformFeedback::getPrimitiveMode() const
214 {
215     return mState.mPrimitiveMode;
216 }
217 
checkBufferSpaceForDraw(GLsizei count,GLsizei primcount) const218 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 
onVerticesDrawn(const Context * context,GLsizei count,GLsizei primcount)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 
bindProgram(const Context * context,Program * program)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 
hasBoundProgram(ShaderProgramID program) const258 bool TransformFeedback::hasBoundProgram(ShaderProgramID program) const
259 {
260     return mState.mProgram != nullptr && mState.mProgram->id().value == program.value;
261 }
262 
detachBuffer(const Context * context,BufferID bufferID)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 
bindIndexedBuffer(const Context * context,size_t index,Buffer * buffer,size_t offset,size_t size)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 
getIndexedBuffer(size_t index) const304 const OffsetBindingPointer<Buffer> &TransformFeedback::getIndexedBuffer(size_t index) const
305 {
306     ASSERT(index < mState.mIndexedBuffers.size());
307     return mState.mIndexedBuffers[index];
308 }
309 
getIndexedBufferCount() const310 size_t TransformFeedback::getIndexedBufferCount() const
311 {
312     return mState.mIndexedBuffers.size();
313 }
314 
buffersBoundForOtherUseInWebGL() const315 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 
getImplementation() const327 rx::TransformFeedbackImpl *TransformFeedback::getImplementation() const
328 {
329     return mImplementation;
330 }
331 
onBindingChanged(const Context * context,bool bound)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 
getIndexedBuffers() const343 const std::vector<OffsetBindingPointer<Buffer>> &TransformFeedback::getIndexedBuffers() const
344 {
345     return mState.mIndexedBuffers;
346 }
347 }  // namespace gl
348