• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/glslang_wrapper_utils.h"
15 #include "libANGLE/renderer/vulkan/BufferVk.h"
16 #include "libANGLE/renderer/vulkan/ContextVk.h"
17 #include "libANGLE/renderer/vulkan/FramebufferVk.h"
18 #include "libANGLE/renderer/vulkan/ProgramVk.h"
19 #include "libANGLE/renderer/vulkan/QueryVk.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       mAlignedBufferOffsets{},
33       mCounterBufferHandles{}
34 {}
35 
~TransformFeedbackVk()36 TransformFeedbackVk::~TransformFeedbackVk() {}
37 
onDestroy(const gl::Context * context)38 void TransformFeedbackVk::onDestroy(const gl::Context *context)
39 {
40     ASSERT(std::all_of(mCounterBufferHelpers.begin(), mCounterBufferHelpers.end(),
41                        [](vk::BufferHelper &counterBuffer) { return !counterBuffer.valid(); }));
42 }
43 
releaseCounterBuffers(RendererVk * renderer)44 void TransformFeedbackVk::releaseCounterBuffers(RendererVk *renderer)
45 {
46     for (vk::BufferHelper &bufferHelper : mCounterBufferHelpers)
47     {
48         bufferHelper.release(renderer);
49     }
50     for (VkBuffer &buffer : mCounterBufferHandles)
51     {
52         buffer = VK_NULL_HANDLE;
53     }
54 }
55 
initializeXFBBuffersDesc(ContextVk * contextVk,size_t xfbBufferCount)56 void TransformFeedbackVk::initializeXFBBuffersDesc(ContextVk *contextVk, size_t xfbBufferCount)
57 {
58     mXFBBuffersDesc.reset();
59     for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
60     {
61         const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(bufferIndex);
62         ASSERT(binding.get());
63 
64         BufferVk *bufferVk = vk::GetImpl(binding.get());
65 
66         if (bufferVk->isBufferValid())
67         {
68             VkDeviceSize bufferOffset   = 0;
69             mBufferHelpers[bufferIndex] = &bufferVk->getBufferAndOffset(&bufferOffset);
70             mBufferOffsets[bufferIndex] = binding.getOffset() + bufferOffset;
71             mBufferSizes[bufferIndex]   = gl::GetBoundBufferAvailableSize(binding);
72         }
73         else
74         {
75             // This can happen in error conditions.
76             vk::BufferHelper &nullBuffer = contextVk->getEmptyBuffer();
77             mBufferHelpers[bufferIndex]  = &nullBuffer;
78             mBufferOffsets[bufferIndex]  = 0;
79             mBufferSizes[bufferIndex]    = nullBuffer.getSize();
80         }
81 
82         mXFBBuffersDesc.updateTransformFeedbackBuffer(
83             bufferIndex, mBufferHelpers[bufferIndex]->getBufferSerial(),
84             mBufferOffsets[bufferIndex]);
85     }
86 }
87 
begin(const gl::Context * context,gl::PrimitiveMode primitiveMode)88 angle::Result TransformFeedbackVk::begin(const gl::Context *context,
89                                          gl::PrimitiveMode primitiveMode)
90 {
91     ContextVk *contextVk = vk::GetImpl(context);
92 
93     const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
94     ASSERT(executable);
95     size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
96 
97     initializeXFBBuffersDesc(contextVk, xfbBufferCount);
98 
99     for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
100     {
101         const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(bufferIndex);
102         mBufferHandles[bufferIndex] = mBufferHelpers[bufferIndex]->getBuffer().getHandle();
103         if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
104         {
105             if (mCounterBufferHandles[bufferIndex] == VK_NULL_HANDLE)
106             {
107                 VkBufferCreateInfo createInfo = {};
108                 createInfo.sType              = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
109                 createInfo.size               = 16;
110                 createInfo.usage       = VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT;
111                 createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
112 
113                 vk::BufferHelper &bufferHelper = mCounterBufferHelpers[bufferIndex];
114                 ANGLE_TRY(
115                     bufferHelper.init(contextVk, createInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
116 
117                 mCounterBufferHandles[bufferIndex] = bufferHelper.getBuffer().getHandle();
118             }
119         }
120         else
121         {
122             ASSERT(contextVk->getFeatures().emulateTransformFeedback.enabled);
123             RendererVk *rendererVk = contextVk->getRenderer();
124             const VkDeviceSize offsetAlignment =
125                 rendererVk->getPhysicalDeviceProperties().limits.minStorageBufferOffsetAlignment;
126 
127             // Make sure there's no possible under/overflow with binding size.
128             static_assert(sizeof(VkDeviceSize) >= sizeof(binding.getSize()),
129                           "VkDeviceSize too small");
130 
131             // Set the offset as close as possible to the requested offset while remaining aligned.
132             mAlignedBufferOffsets[bufferIndex] =
133                 (mBufferOffsets[bufferIndex] / offsetAlignment) * offsetAlignment;
134         }
135     }
136 
137     if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
138     {
139         mRebindTransformFeedbackBuffer = true;
140     }
141 
142     return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
143                                                mCounterBufferHelpers);
144 }
145 
end(const gl::Context * context)146 angle::Result TransformFeedbackVk::end(const gl::Context *context)
147 {
148     ContextVk *contextVk = vk::GetImpl(context);
149 
150     // If there's an active transform feedback query, accumulate the primitives drawn.
151     const gl::State &glState = context->getState();
152     gl::Query *transformFeedbackQuery =
153         glState.getActiveQuery(gl::QueryType::TransformFeedbackPrimitivesWritten);
154 
155     if (transformFeedbackQuery && contextVk->getFeatures().emulateTransformFeedback.enabled)
156     {
157         vk::GetImpl(transformFeedbackQuery)->onTransformFeedbackEnd(mState.getPrimitivesDrawn());
158     }
159 
160     contextVk->onEndTransformFeedback();
161 
162     releaseCounterBuffers(contextVk->getRenderer());
163 
164     return angle::Result::Continue;
165 }
166 
pause(const gl::Context * context)167 angle::Result TransformFeedbackVk::pause(const gl::Context *context)
168 {
169     ContextVk *contextVk = vk::GetImpl(context);
170 
171     if (contextVk->getFeatures().emulateTransformFeedback.enabled)
172     {
173         // Bind the empty buffer until we resume.
174         const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
175         ASSERT(executable);
176         size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
177 
178         const vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer();
179 
180         for (size_t xfbIndex = 0; xfbIndex < xfbBufferCount; ++xfbIndex)
181         {
182             mXFBBuffersDesc.updateTransformFeedbackBuffer(xfbIndex, emptyBuffer.getBufferSerial(),
183                                                           0);
184         }
185     }
186 
187     return contextVk->onPauseTransformFeedback();
188 }
189 
resume(const gl::Context * context)190 angle::Result TransformFeedbackVk::resume(const gl::Context *context)
191 {
192     ContextVk *contextVk                    = vk::GetImpl(context);
193     const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
194     ASSERT(executable);
195     size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
196 
197     if (contextVk->getFeatures().emulateTransformFeedback.enabled)
198     {
199         initializeXFBBuffersDesc(contextVk, xfbBufferCount);
200     }
201 
202     return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
203                                                mCounterBufferHelpers);
204 }
205 
bindIndexedBuffer(const gl::Context * context,size_t index,const gl::OffsetBindingPointer<gl::Buffer> & binding)206 angle::Result TransformFeedbackVk::bindIndexedBuffer(
207     const gl::Context *context,
208     size_t index,
209     const gl::OffsetBindingPointer<gl::Buffer> &binding)
210 {
211     ContextVk *contextVk = vk::GetImpl(context);
212 
213     // Make sure the transform feedback buffers are bound to the program descriptor sets.
214     contextVk->invalidateCurrentTransformFeedbackBuffers();
215 
216     return angle::Result::Continue;
217 }
218 
updateDescriptorSetLayout(ContextVk * contextVk,const ShaderInterfaceVariableInfoMap & variableInfoMap,size_t xfbBufferCount,vk::DescriptorSetLayoutDesc * descSetLayoutOut) const219 void TransformFeedbackVk::updateDescriptorSetLayout(
220     ContextVk *contextVk,
221     const ShaderInterfaceVariableInfoMap &variableInfoMap,
222     size_t xfbBufferCount,
223     vk::DescriptorSetLayoutDesc *descSetLayoutOut) const
224 {
225     if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
226     {
227         return;
228     }
229 
230     for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
231     {
232         const std::string bufferName = GetXfbBufferName(bufferIndex);
233         const ShaderInterfaceVariableInfo &info =
234             variableInfoMap.get(gl::ShaderType::Vertex, bufferName);
235 
236         descSetLayoutOut->update(info.binding, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
237                                  VK_SHADER_STAGE_VERTEX_BIT, nullptr);
238     }
239 }
240 
initDescriptorSet(ContextVk * contextVk,const ShaderInterfaceVariableInfoMap & variableInfoMap,size_t xfbBufferCount,VkDescriptorSet descSet) const241 void TransformFeedbackVk::initDescriptorSet(ContextVk *contextVk,
242                                             const ShaderInterfaceVariableInfoMap &variableInfoMap,
243                                             size_t xfbBufferCount,
244                                             VkDescriptorSet descSet) const
245 {
246     if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
247     {
248         return;
249     }
250 
251     VkDescriptorBufferInfo *descriptorBufferInfo =
252         contextVk->allocDescriptorBufferInfos(xfbBufferCount);
253     vk::BufferHelper *emptyBuffer = &contextVk->getEmptyBuffer();
254 
255     for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
256     {
257         VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[bufferIndex];
258         bufferInfo.buffer                  = emptyBuffer->getBuffer().getHandle();
259         bufferInfo.offset                  = 0;
260         bufferInfo.range                   = VK_WHOLE_SIZE;
261     }
262 
263     writeDescriptorSet(contextVk, variableInfoMap, xfbBufferCount, descriptorBufferInfo, descSet);
264 }
265 
updateDescriptorSet(ContextVk * contextVk,const gl::ProgramState & programState,const ShaderInterfaceVariableInfoMap & variableInfoMap,VkDescriptorSet descSet) const266 void TransformFeedbackVk::updateDescriptorSet(ContextVk *contextVk,
267                                               const gl::ProgramState &programState,
268                                               const ShaderInterfaceVariableInfoMap &variableInfoMap,
269                                               VkDescriptorSet descSet) const
270 {
271     if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
272     {
273         return;
274     }
275 
276     const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
277     ASSERT(executable);
278     size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
279 
280     ASSERT(xfbBufferCount > 0);
281     ASSERT(programState.getTransformFeedbackBufferMode() != GL_INTERLEAVED_ATTRIBS ||
282            xfbBufferCount == 1);
283 
284     VkDescriptorBufferInfo *descriptorBufferInfo =
285         contextVk->allocDescriptorBufferInfos(xfbBufferCount);
286 
287     // Update buffer descriptor binding info for output buffers
288     for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
289     {
290         VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[bufferIndex];
291 
292         bufferInfo.buffer = mBufferHandles[bufferIndex];
293         bufferInfo.offset = mAlignedBufferOffsets[bufferIndex];
294         bufferInfo.range  = mBufferSizes[bufferIndex] +
295                            (mBufferOffsets[bufferIndex] - mAlignedBufferOffsets[bufferIndex]);
296         ASSERT(bufferInfo.range != 0);
297     }
298 
299     writeDescriptorSet(contextVk, variableInfoMap, xfbBufferCount, descriptorBufferInfo, descSet);
300 }
301 
getBufferOffsets(ContextVk * contextVk,GLint drawCallFirstVertex,int32_t * offsetsOut,size_t offsetsSize) const302 void TransformFeedbackVk::getBufferOffsets(ContextVk *contextVk,
303                                            GLint drawCallFirstVertex,
304                                            int32_t *offsetsOut,
305                                            size_t offsetsSize) const
306 {
307     if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
308     {
309         return;
310     }
311 
312     GLsizeiptr verticesDrawn                = mState.getVerticesDrawn();
313     const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
314     ASSERT(executable);
315     const std::vector<GLsizei> &bufferStrides = executable->getTransformFeedbackStrides();
316     size_t xfbBufferCount                     = executable->getTransformFeedbackBufferCount();
317 
318     ASSERT(xfbBufferCount > 0);
319 
320     // The caller should make sure the offsets array has enough space.  The maximum possible
321     // number of outputs is gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS.
322     ASSERT(offsetsSize >= xfbBufferCount);
323 
324     for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
325     {
326         int64_t offsetFromDescriptor =
327             static_cast<int64_t>(mBufferOffsets[bufferIndex] - mAlignedBufferOffsets[bufferIndex]);
328         int64_t drawCallVertexOffset = static_cast<int64_t>(verticesDrawn) - drawCallFirstVertex;
329 
330         int64_t writeOffset =
331             (offsetFromDescriptor + drawCallVertexOffset * bufferStrides[bufferIndex]) /
332             static_cast<int64_t>(sizeof(uint32_t));
333 
334         offsetsOut[bufferIndex] = static_cast<int32_t>(writeOffset);
335 
336         // Assert on overflow.  For now, support transform feedback up to 2GB.
337         ASSERT(offsetsOut[bufferIndex] == writeOffset);
338     }
339 }
340 
writeDescriptorSet(ContextVk * contextVk,const ShaderInterfaceVariableInfoMap & variableInfoMap,size_t xfbBufferCount,VkDescriptorBufferInfo * bufferInfo,VkDescriptorSet descSet) const341 void TransformFeedbackVk::writeDescriptorSet(ContextVk *contextVk,
342                                              const ShaderInterfaceVariableInfoMap &variableInfoMap,
343                                              size_t xfbBufferCount,
344                                              VkDescriptorBufferInfo *bufferInfo,
345                                              VkDescriptorSet descSet) const
346 {
347     ASSERT(contextVk->getFeatures().emulateTransformFeedback.enabled);
348 
349     const std::string bufferName = GetXfbBufferName(0);
350     const ShaderInterfaceVariableInfo &info =
351         variableInfoMap.get(gl::ShaderType::Vertex, bufferName);
352 
353     VkWriteDescriptorSet &writeDescriptorInfo = contextVk->allocWriteDescriptorSet();
354     writeDescriptorInfo.sType                 = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
355     writeDescriptorInfo.dstSet                = descSet;
356     writeDescriptorInfo.dstBinding            = info.binding;
357     writeDescriptorInfo.dstArrayElement       = 0;
358     writeDescriptorInfo.descriptorCount       = static_cast<uint32_t>(xfbBufferCount);
359     writeDescriptorInfo.descriptorType        = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
360     writeDescriptorInfo.pImageInfo            = nullptr;
361     writeDescriptorInfo.pBufferInfo           = bufferInfo;
362     writeDescriptorInfo.pTexelBufferView      = nullptr;
363 }
364 
365 }  // namespace rx
366