• 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 {
25 
TransformFeedbackVk(const gl::TransformFeedbackState & state)26 TransformFeedbackVk::TransformFeedbackVk(const gl::TransformFeedbackState &state)
27     : TransformFeedbackImpl(state),
28       mRebindTransformFeedbackBuffer(false),
29       mBufferHelpers{},
30       mBufferHandles{},
31       mBufferOffsets{},
32       mBufferSizes{},
33       mAlignedBufferOffsets{},
34       mCounterBufferHandles{}
35 {}
36 
~TransformFeedbackVk()37 TransformFeedbackVk::~TransformFeedbackVk() {}
38 
onDestroy(const gl::Context * context)39 void TransformFeedbackVk::onDestroy(const gl::Context *context)
40 {
41     RendererVk *rendererVk = vk::GetImpl(context)->getRenderer();
42 
43     for (vk::BufferHelper &bufferHelper : mCounterBufferHelpers)
44     {
45         bufferHelper.release(rendererVk);
46     }
47 }
48 
begin(const gl::Context * context,gl::PrimitiveMode primitiveMode)49 angle::Result TransformFeedbackVk::begin(const gl::Context *context,
50                                          gl::PrimitiveMode primitiveMode)
51 {
52     ContextVk *contextVk = vk::GetImpl(context);
53 
54     contextVk->onTransformFeedbackStateChanged();
55 
56     const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
57     ASSERT(executable);
58     size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
59 
60     for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
61     {
62         const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(bufferIndex);
63         ASSERT(binding.get());
64 
65         BufferVk *bufferVk = vk::GetImpl(binding.get());
66 
67         if (bufferVk->isBufferValid())
68         {
69             mBufferHelpers[bufferIndex] = &bufferVk->getBuffer();
70             mBufferOffsets[bufferIndex] = binding.getOffset();
71             mBufferSizes[bufferIndex]   = gl::GetBoundBufferAvailableSize(binding);
72         }
73         else
74         {
75             // This can happen in error conditions.
76             vk::BufferHelper &nullBuffer = contextVk->getRenderer()->getNullBuffer();
77             mBufferHelpers[bufferIndex]  = &nullBuffer;
78             mBufferOffsets[bufferIndex]  = 0;
79             mBufferSizes[bufferIndex]    = nullBuffer.getSize();
80         }
81 
82         mBufferHandles[bufferIndex] = mBufferHelpers[bufferIndex]->getBuffer().getHandle();
83 
84         if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
85         {
86             if (mCounterBufferHandles[bufferIndex] == VK_NULL_HANDLE)
87             {
88                 VkBufferCreateInfo createInfo = {};
89                 createInfo.sType              = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
90                 createInfo.size               = 16;
91                 createInfo.usage       = VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT;
92                 createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
93 
94                 vk::BufferHelper &bufferHelper = mCounterBufferHelpers[bufferIndex];
95                 ANGLE_TRY(
96                     bufferHelper.init(contextVk, createInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
97 
98                 mCounterBufferHandles[bufferIndex] = bufferHelper.getBuffer().getHandle();
99             }
100         }
101         else
102         {
103             ASSERT(contextVk->getFeatures().emulateTransformFeedback.enabled);
104             RendererVk *rendererVk = contextVk->getRenderer();
105             const VkDeviceSize offsetAlignment =
106                 rendererVk->getPhysicalDeviceProperties().limits.minStorageBufferOffsetAlignment;
107 
108             // Make sure there's no possible under/overflow with binding size.
109             static_assert(sizeof(VkDeviceSize) >= sizeof(binding.getSize()),
110                           "VkDeviceSize too small");
111 
112             // Set the offset as close as possible to the requested offset while remaining aligned.
113             mAlignedBufferOffsets[bufferIndex] =
114                 (mBufferOffsets[bufferIndex] / offsetAlignment) * offsetAlignment;
115         }
116     }
117 
118     if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
119     {
120         mRebindTransformFeedbackBuffer = true;
121     }
122 
123     return onTransformFeedbackStateChanged(contextVk);
124 }
125 
end(const gl::Context * context)126 angle::Result TransformFeedbackVk::end(const gl::Context *context)
127 {
128     // If there's an active transform feedback query, accumulate the primitives drawn.
129     const gl::State &glState = context->getState();
130     gl::Query *transformFeedbackQuery =
131         glState.getActiveQuery(gl::QueryType::TransformFeedbackPrimitivesWritten);
132 
133     if (transformFeedbackQuery)
134     {
135         vk::GetImpl(transformFeedbackQuery)->onTransformFeedbackEnd(context);
136     }
137 
138     ContextVk *contextVk = vk::GetImpl(context);
139     contextVk->onTransformFeedbackStateChanged();
140 
141     return onTransformFeedbackStateChanged(contextVk);
142 }
143 
pause(const gl::Context * context)144 angle::Result TransformFeedbackVk::pause(const gl::Context *context)
145 {
146     ContextVk *contextVk = vk::GetImpl(context);
147 
148     contextVk->onTransformFeedbackStateChanged();
149 
150     if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
151     {
152         // We need to end the RenderPass to perform transform feedback pause/resume
153         // becasue vkCmdBegin/EndTransformFeedback can be placed once per RenderPass.
154         ANGLE_TRY(onTransformFeedbackStateChanged(contextVk));
155     }
156 
157     return angle::Result::Continue;
158 }
159 
resume(const gl::Context * context)160 angle::Result TransformFeedbackVk::resume(const gl::Context *context)
161 {
162     ContextVk *contextVk = vk::GetImpl(context);
163 
164     contextVk->onTransformFeedbackStateChanged();
165 
166     if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
167     {
168         // We need to end the RenderPass to perform transform feedback pause/resume
169         // becasue vkCmdBegin/EndTransformFeedback can be placed once per RenderPass.
170         ANGLE_TRY(onTransformFeedbackStateChanged(contextVk));
171     }
172 
173     return angle::Result::Continue;
174 }
175 
bindIndexedBuffer(const gl::Context * context,size_t index,const gl::OffsetBindingPointer<gl::Buffer> & binding)176 angle::Result TransformFeedbackVk::bindIndexedBuffer(
177     const gl::Context *context,
178     size_t index,
179     const gl::OffsetBindingPointer<gl::Buffer> &binding)
180 {
181     ContextVk *contextVk = vk::GetImpl(context);
182 
183     // Make sure the transform feedback buffers are bound to the program descriptor sets.
184     contextVk->invalidateCurrentTransformFeedbackBuffers();
185 
186     return angle::Result::Continue;
187 }
188 
updateDescriptorSetLayout(ContextVk * contextVk,ShaderInterfaceVariableInfoMap & vsVariableInfoMap,size_t xfbBufferCount,vk::DescriptorSetLayoutDesc * descSetLayoutOut) const189 void TransformFeedbackVk::updateDescriptorSetLayout(
190     ContextVk *contextVk,
191     ShaderInterfaceVariableInfoMap &vsVariableInfoMap,
192     size_t xfbBufferCount,
193     vk::DescriptorSetLayoutDesc *descSetLayoutOut) const
194 {
195     if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
196         return;
197 
198     for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
199     {
200         const std::string bufferName            = GetXfbBufferName(bufferIndex);
201         const ShaderInterfaceVariableInfo &info = vsVariableInfoMap[bufferName];
202 
203         descSetLayoutOut->update(info.binding, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
204                                  VK_SHADER_STAGE_VERTEX_BIT);
205     }
206 }
207 
initDescriptorSet(ContextVk * contextVk,size_t xfbBufferCount,vk::BufferHelper * emptyBuffer,VkDescriptorSet descSet) const208 void TransformFeedbackVk::initDescriptorSet(ContextVk *contextVk,
209                                             size_t xfbBufferCount,
210                                             vk::BufferHelper *emptyBuffer,
211                                             VkDescriptorSet descSet) const
212 {
213     if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
214         return;
215 
216     gl::TransformFeedbackBuffersArray<VkDescriptorBufferInfo> descriptorBufferInfo;
217 
218     for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
219     {
220         VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[bufferIndex];
221         bufferInfo.buffer                  = emptyBuffer->getBuffer().getHandle();
222         bufferInfo.offset                  = 0;
223         bufferInfo.range                   = VK_WHOLE_SIZE;
224     }
225 
226     writeDescriptorSet(contextVk, xfbBufferCount, descriptorBufferInfo.data(), descSet);
227 }
228 
updateDescriptorSet(ContextVk * contextVk,const gl::ProgramState & programState,VkDescriptorSet descSet) const229 void TransformFeedbackVk::updateDescriptorSet(ContextVk *contextVk,
230                                               const gl::ProgramState &programState,
231                                               VkDescriptorSet descSet) const
232 {
233     if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
234         return;
235 
236     const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
237     ASSERT(executable);
238     size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
239 
240     ASSERT(xfbBufferCount > 0);
241     ASSERT(programState.getTransformFeedbackBufferMode() != GL_INTERLEAVED_ATTRIBS ||
242            xfbBufferCount == 1);
243 
244     gl::TransformFeedbackBuffersArray<VkDescriptorBufferInfo> descriptorBufferInfo;
245 
246     // Update buffer descriptor binding info for output buffers
247     for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
248     {
249         VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[bufferIndex];
250 
251         bufferInfo.buffer = mBufferHandles[bufferIndex];
252         bufferInfo.offset = mAlignedBufferOffsets[bufferIndex];
253         bufferInfo.range  = mBufferSizes[bufferIndex] +
254                            (mBufferOffsets[bufferIndex] - mAlignedBufferOffsets[bufferIndex]);
255         ASSERT(bufferInfo.range != 0);
256     }
257 
258     writeDescriptorSet(contextVk, xfbBufferCount, descriptorBufferInfo.data(), descSet);
259 }
260 
getBufferOffsets(ContextVk * contextVk,GLint drawCallFirstVertex,int32_t * offsetsOut,size_t offsetsSize) const261 void TransformFeedbackVk::getBufferOffsets(ContextVk *contextVk,
262                                            GLint drawCallFirstVertex,
263                                            int32_t *offsetsOut,
264                                            size_t offsetsSize) const
265 {
266     if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
267         return;
268 
269     GLsizeiptr verticesDrawn = mState.getVerticesDrawn();
270     const std::vector<GLsizei> &bufferStrides =
271         mState.getBoundProgram()->getTransformFeedbackStrides();
272     const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
273     ASSERT(executable);
274     size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
275 
276     ASSERT(xfbBufferCount > 0);
277 
278     // The caller should make sure the offsets array has enough space.  The maximum possible
279     // number of outputs is gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS.
280     ASSERT(offsetsSize >= xfbBufferCount);
281 
282     for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
283     {
284         int64_t offsetFromDescriptor =
285             static_cast<int64_t>(mBufferOffsets[bufferIndex] - mAlignedBufferOffsets[bufferIndex]);
286         int64_t drawCallVertexOffset = static_cast<int64_t>(verticesDrawn) - drawCallFirstVertex;
287 
288         int64_t writeOffset =
289             (offsetFromDescriptor + drawCallVertexOffset * bufferStrides[bufferIndex]) /
290             static_cast<int64_t>(sizeof(uint32_t));
291 
292         offsetsOut[bufferIndex] = static_cast<int32_t>(writeOffset);
293 
294         // Assert on overflow.  For now, support transform feedback up to 2GB.
295         ASSERT(offsetsOut[bufferIndex] == writeOffset);
296     }
297 }
298 
onTransformFeedbackStateChanged(ContextVk * contextVk)299 angle::Result TransformFeedbackVk::onTransformFeedbackStateChanged(ContextVk *contextVk)
300 {
301     // Currently, we don't handle resources switching from read-only to writable and back correctly.
302     // In the case of transform feedback, the attached buffers can switch between being written by
303     // transform feedback and being read as a vertex array buffer.  For now, we'll end the render
304     // pass every time transform feedback is started or ended to work around this issue temporarily.
305     //
306     // TODO(syoussefi): detect changes to buffer usage (e.g. as transform feedback output, vertex
307     // or index data etc) in the front end and notify the backend.  A new node should be created
308     // only on such changes.  http://anglebug.com/3205
309     ANGLE_TRY(contextVk->endRenderPass());
310     return angle::Result::Continue;
311 }
312 
writeDescriptorSet(ContextVk * contextVk,size_t xfbBufferCount,VkDescriptorBufferInfo * pBufferInfo,VkDescriptorSet descSet) const313 void TransformFeedbackVk::writeDescriptorSet(ContextVk *contextVk,
314                                              size_t xfbBufferCount,
315                                              VkDescriptorBufferInfo *pBufferInfo,
316                                              VkDescriptorSet descSet) const
317 {
318     VkDevice device                   = contextVk->getDevice();
319     ProgramExecutableVk *executableVk = contextVk->getExecutable();
320     ShaderMapInterfaceVariableInfoMap variableInfoMap =
321         executableVk->getShaderInterfaceVariableInfoMap();
322     const std::string bufferName      = GetXfbBufferName(0);
323     ShaderInterfaceVariableInfo &info = variableInfoMap[gl::ShaderType::Vertex][bufferName];
324 
325     VkWriteDescriptorSet writeDescriptorInfo = {};
326     writeDescriptorInfo.sType                = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
327     writeDescriptorInfo.dstSet               = descSet;
328     writeDescriptorInfo.dstBinding           = info.binding;
329     writeDescriptorInfo.dstArrayElement      = 0;
330     writeDescriptorInfo.descriptorCount      = static_cast<uint32_t>(xfbBufferCount);
331     writeDescriptorInfo.descriptorType       = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
332     writeDescriptorInfo.pImageInfo           = nullptr;
333     writeDescriptorInfo.pBufferInfo          = pBufferInfo;
334     writeDescriptorInfo.pTexelBufferView     = nullptr;
335 
336     vkUpdateDescriptorSets(device, 1, &writeDescriptorInfo, 0, nullptr);
337 }
338 
339 }  // namespace rx
340