xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/vulkan/TransformFeedbackVk.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2016 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 // TransformFeedbackVk.cpp:
7 //    Implements the class methods for TransformFeedbackVk.
8 //
9 
10 #include "libANGLE/renderer/vulkan/TransformFeedbackVk.h"
11 
12 #include "libANGLE/Context.h"
13 #include "libANGLE/Query.h"
14 #include "libANGLE/renderer/vulkan/BufferVk.h"
15 #include "libANGLE/renderer/vulkan/ContextVk.h"
16 #include "libANGLE/renderer/vulkan/FramebufferVk.h"
17 #include "libANGLE/renderer/vulkan/ProgramVk.h"
18 #include "libANGLE/renderer/vulkan/QueryVk.h"
19 #include "libANGLE/renderer/vulkan/ShaderInterfaceVariableInfoMap.h"
20 
21 #include "common/debug.h"
22 
23 namespace rx
24 {
TransformFeedbackVk(const gl::TransformFeedbackState & state)25 TransformFeedbackVk::TransformFeedbackVk(const gl::TransformFeedbackState &state)
26     : TransformFeedbackImpl(state),
27       mRebindTransformFeedbackBuffer(false),
28       mBufferHelpers{},
29       mBufferHandles{},
30       mBufferOffsets{},
31       mBufferSizes{},
32       mCounterBufferHandles{},
33       mCounterBufferOffsets{}
34 {
35     for (angle::SubjectIndex bufferIndex = 0;
36          bufferIndex < gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS; ++bufferIndex)
37     {
38         mBufferObserverBindings.emplace_back(this, bufferIndex);
39     }
40 }
41 
~TransformFeedbackVk()42 TransformFeedbackVk::~TransformFeedbackVk() {}
43 
onDestroy(const gl::Context * context)44 void TransformFeedbackVk::onDestroy(const gl::Context *context)
45 {
46     ContextVk *contextVk   = vk::GetImpl(context);
47     vk::Renderer *renderer = contextVk->getRenderer();
48 
49     releaseCounterBuffers(renderer);
50 }
51 
releaseCounterBuffers(vk::Renderer * renderer)52 void TransformFeedbackVk::releaseCounterBuffers(vk::Renderer *renderer)
53 {
54     for (vk::BufferHelper &bufferHelper : mCounterBufferHelpers)
55     {
56         bufferHelper.release(renderer);
57     }
58     for (VkBuffer &buffer : mCounterBufferHandles)
59     {
60         buffer = VK_NULL_HANDLE;
61     }
62     for (VkDeviceSize &offset : mCounterBufferOffsets)
63     {
64         offset = 0;
65     }
66 }
67 
initializeXFBVariables(ContextVk * contextVk,uint32_t xfbBufferCount)68 void TransformFeedbackVk::initializeXFBVariables(ContextVk *contextVk, uint32_t xfbBufferCount)
69 {
70     for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
71     {
72         const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(bufferIndex);
73         ASSERT(binding.get());
74 
75         BufferVk *bufferVk = vk::GetImpl(binding.get());
76 
77         if (bufferVk->isBufferValid())
78         {
79             mBufferHelpers[bufferIndex] = &bufferVk->getBuffer();
80             mBufferOffsets[bufferIndex] =
81                 binding.getOffset() + mBufferHelpers[bufferIndex]->getOffset();
82             mBufferSizes[bufferIndex] = gl::GetBoundBufferAvailableSize(binding);
83             mBufferObserverBindings[bufferIndex].bind(bufferVk);
84         }
85         else
86         {
87             // This can happen in error conditions.
88             vk::BufferHelper &nullBuffer = contextVk->getEmptyBuffer();
89             mBufferHelpers[bufferIndex]  = &nullBuffer;
90             mBufferOffsets[bufferIndex]  = 0;
91             mBufferSizes[bufferIndex]    = nullBuffer.getSize();
92             mBufferObserverBindings[bufferIndex].reset();
93         }
94     }
95 }
96 
begin(const gl::Context * context,gl::PrimitiveMode primitiveMode)97 angle::Result TransformFeedbackVk::begin(const gl::Context *context,
98                                          gl::PrimitiveMode primitiveMode)
99 {
100     ContextVk *contextVk = vk::GetImpl(context);
101 
102     const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
103     ASSERT(executable);
104     size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
105 
106     initializeXFBVariables(contextVk, static_cast<uint32_t>(xfbBufferCount));
107 
108     for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
109     {
110         mBufferHandles[bufferIndex] = mBufferHelpers[bufferIndex]->getBuffer().getHandle();
111         if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
112         {
113             if (mCounterBufferHandles[bufferIndex] == VK_NULL_HANDLE)
114             {
115                 vk::BufferHelper &bufferHelper = mCounterBufferHelpers[bufferIndex];
116                 ANGLE_TRY(contextVk->initBufferAllocation(
117                     &bufferHelper, contextVk->getRenderer()->getDeviceLocalMemoryTypeIndex(), 16,
118                     contextVk->getRenderer()->getDefaultBufferAlignment(),
119                     BufferUsageType::Static));
120                 mCounterBufferHandles[bufferIndex] = bufferHelper.getBuffer().getHandle();
121                 mCounterBufferOffsets[bufferIndex] = bufferHelper.getOffset();
122             }
123         }
124     }
125 
126     if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
127     {
128         mRebindTransformFeedbackBuffer = true;
129     }
130 
131     return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
132                                                mCounterBufferHelpers);
133 }
134 
end(const gl::Context * context)135 angle::Result TransformFeedbackVk::end(const gl::Context *context)
136 {
137     ContextVk *contextVk = vk::GetImpl(context);
138 
139     // If there's an active transform feedback query, accumulate the primitives drawn.
140     const gl::State &glState = context->getState();
141     gl::Query *transformFeedbackQuery =
142         glState.getActiveQuery(gl::QueryType::TransformFeedbackPrimitivesWritten);
143 
144     if (transformFeedbackQuery && contextVk->getFeatures().emulateTransformFeedback.enabled)
145     {
146         vk::GetImpl(transformFeedbackQuery)->onTransformFeedbackEnd(mState.getPrimitivesDrawn());
147     }
148 
149     for (angle::ObserverBinding &bufferBinding : mBufferObserverBindings)
150     {
151         bufferBinding.reset();
152     }
153 
154     contextVk->onEndTransformFeedback();
155 
156     releaseCounterBuffers(contextVk->getRenderer());
157 
158     return angle::Result::Continue;
159 }
160 
pause(const gl::Context * context)161 angle::Result TransformFeedbackVk::pause(const gl::Context *context)
162 {
163     ContextVk *contextVk = vk::GetImpl(context);
164     return contextVk->onPauseTransformFeedback();
165 }
166 
resume(const gl::Context * context)167 angle::Result TransformFeedbackVk::resume(const gl::Context *context)
168 {
169     ContextVk *contextVk                    = vk::GetImpl(context);
170     const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
171     ASSERT(executable);
172     size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
173 
174     if (contextVk->getFeatures().emulateTransformFeedback.enabled)
175     {
176         initializeXFBVariables(contextVk, static_cast<uint32_t>(xfbBufferCount));
177     }
178 
179     return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
180                                                mCounterBufferHelpers);
181 }
182 
bindIndexedBuffer(const gl::Context * context,size_t index,const gl::OffsetBindingPointer<gl::Buffer> & binding)183 angle::Result TransformFeedbackVk::bindIndexedBuffer(
184     const gl::Context *context,
185     size_t index,
186     const gl::OffsetBindingPointer<gl::Buffer> &binding)
187 {
188     ContextVk *contextVk = vk::GetImpl(context);
189 
190     // Make sure the transform feedback buffers are bound to the program descriptor sets.
191     contextVk->invalidateCurrentTransformFeedbackBuffers();
192 
193     return angle::Result::Continue;
194 }
195 
getBufferOffsets(ContextVk * contextVk,GLint drawCallFirstVertex,int32_t * offsetsOut,size_t offsetsSize) const196 void TransformFeedbackVk::getBufferOffsets(ContextVk *contextVk,
197                                            GLint drawCallFirstVertex,
198                                            int32_t *offsetsOut,
199                                            size_t offsetsSize) const
200 {
201     if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
202     {
203         return;
204     }
205 
206     GLsizeiptr verticesDrawn                = mState.getVerticesDrawn();
207     const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
208     ASSERT(executable);
209     const std::vector<GLsizei> &bufferStrides = executable->getTransformFeedbackStrides();
210     size_t xfbBufferCount                     = executable->getTransformFeedbackBufferCount();
211     const VkDeviceSize offsetAlignment        = contextVk->getRenderer()
212                                              ->getPhysicalDeviceProperties()
213                                              .limits.minStorageBufferOffsetAlignment;
214 
215     ASSERT(xfbBufferCount > 0);
216 
217     // The caller should make sure the offsets array has enough space.  The maximum possible
218     // number of outputs is gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS.
219     ASSERT(offsetsSize >= xfbBufferCount);
220 
221     for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
222     {
223         int64_t offsetFromDescriptor =
224             static_cast<int64_t>(mBufferOffsets[bufferIndex] % offsetAlignment);
225         int64_t drawCallVertexOffset = static_cast<int64_t>(verticesDrawn) - drawCallFirstVertex;
226 
227         int64_t writeOffset =
228             (offsetFromDescriptor + drawCallVertexOffset * bufferStrides[bufferIndex]) /
229             static_cast<int64_t>(sizeof(uint32_t));
230 
231         offsetsOut[bufferIndex] = static_cast<int32_t>(writeOffset);
232 
233         // Assert on overflow.  For now, support transform feedback up to 2GB.
234         ASSERT(offsetsOut[bufferIndex] == writeOffset);
235     }
236 }
237 
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)238 void TransformFeedbackVk::onSubjectStateChange(angle::SubjectIndex index,
239                                                angle::SubjectMessage message)
240 {
241     if (message == angle::SubjectMessage::InternalMemoryAllocationChanged)
242     {
243         ASSERT(index < mBufferObserverBindings.size());
244         const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(index);
245         ASSERT(binding.get());
246 
247         BufferVk *bufferVk = vk::GetImpl(binding.get());
248 
249         ASSERT(bufferVk->isBufferValid());
250         mBufferHelpers[index] = &bufferVk->getBuffer();
251         mBufferOffsets[index] = binding.getOffset() + mBufferHelpers[index]->getOffset();
252         mBufferSizes[index]   = std::min<VkDeviceSize>(gl::GetBoundBufferAvailableSize(binding),
253                                                        mBufferHelpers[index]->getSize());
254         mBufferObserverBindings[index].bind(bufferVk);
255         mBufferHandles[index] = mBufferHelpers[index]->getBuffer().getHandle();
256     }
257 }
258 
updateTransformFeedbackDescriptorDesc(const vk::Context * context,const gl::ProgramExecutable & executable,const ShaderInterfaceVariableInfoMap & variableInfoMap,const vk::WriteDescriptorDescs & writeDescriptorDescs,const vk::BufferHelper & emptyBuffer,bool activeUnpaused,vk::DescriptorSetDescBuilder * builder) const259 void TransformFeedbackVk::updateTransformFeedbackDescriptorDesc(
260     const vk::Context *context,
261     const gl::ProgramExecutable &executable,
262     const ShaderInterfaceVariableInfoMap &variableInfoMap,
263     const vk::WriteDescriptorDescs &writeDescriptorDescs,
264     const vk::BufferHelper &emptyBuffer,
265     bool activeUnpaused,
266     vk::DescriptorSetDescBuilder *builder) const
267 {
268     size_t xfbBufferCount = executable.getTransformFeedbackBufferCount();
269 
270     for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
271     {
272         if (mBufferHelpers[bufferIndex] && activeUnpaused)
273         {
274             builder->updateTransformFeedbackBuffer(context, variableInfoMap, writeDescriptorDescs,
275                                                    bufferIndex, *mBufferHelpers[bufferIndex],
276                                                    mBufferOffsets[bufferIndex],
277                                                    mBufferSizes[bufferIndex]);
278         }
279         else
280         {
281             builder->updateTransformFeedbackBuffer(context, variableInfoMap, writeDescriptorDescs,
282                                                    bufferIndex, emptyBuffer, 0,
283                                                    emptyBuffer.getSize());
284         }
285     }
286 }
287 
onNewDescriptorSet(const gl::ProgramExecutable & executable,const vk::SharedDescriptorSetCacheKey & sharedCacheKey)288 void TransformFeedbackVk::onNewDescriptorSet(const gl::ProgramExecutable &executable,
289                                              const vk::SharedDescriptorSetCacheKey &sharedCacheKey)
290 {
291     size_t xfbBufferCount = executable.getTransformFeedbackBufferCount();
292     for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
293     {
294         const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(bufferIndex);
295         if (binding.get())
296         {
297             BufferVk *bufferVk = vk::GetImpl(binding.get());
298             if (bufferVk->getBuffer().valid())
299             {
300                 bufferVk->getBuffer().getBufferBlock()->onNewDescriptorSet(sharedCacheKey);
301             }
302         }
303     }
304 }
305 }  // namespace rx
306