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