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