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 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 RendererVk *rendererVk = contextVk->getRenderer();
48
49 releaseCounterBuffers(rendererVk);
50 }
51
releaseCounterBuffers(RendererVk * renderer)52 void TransformFeedbackVk::releaseCounterBuffers(RendererVk *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
initializeXFBBuffersDesc(ContextVk * contextVk,size_t xfbBufferCount)68 void TransformFeedbackVk::initializeXFBBuffersDesc(ContextVk *contextVk, size_t xfbBufferCount)
69 {
70 mXFBBuffersDesc.reset();
71 for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
72 {
73 const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(bufferIndex);
74 ASSERT(binding.get());
75
76 BufferVk *bufferVk = vk::GetImpl(binding.get());
77
78 if (bufferVk->isBufferValid())
79 {
80 mBufferHelpers[bufferIndex] = &bufferVk->getBuffer();
81 mBufferOffsets[bufferIndex] =
82 binding.getOffset() + mBufferHelpers[bufferIndex]->getOffset();
83 mBufferSizes[bufferIndex] = gl::GetBoundBufferAvailableSize(binding);
84 mBufferObserverBindings[bufferIndex].bind(bufferVk);
85 }
86 else
87 {
88 // This can happen in error conditions.
89 vk::BufferHelper &nullBuffer = contextVk->getEmptyBuffer();
90 mBufferHelpers[bufferIndex] = &nullBuffer;
91 mBufferOffsets[bufferIndex] = 0;
92 mBufferSizes[bufferIndex] = nullBuffer.getSize();
93 mBufferObserverBindings[bufferIndex].reset();
94 }
95
96 mXFBBuffersDesc.updateTransformFeedbackBuffer(
97 bufferIndex, mBufferHelpers[bufferIndex]->getBufferSerial(),
98 mBufferOffsets[bufferIndex]);
99 }
100 }
101
begin(const gl::Context * context,gl::PrimitiveMode primitiveMode)102 angle::Result TransformFeedbackVk::begin(const gl::Context *context,
103 gl::PrimitiveMode primitiveMode)
104 {
105 ContextVk *contextVk = vk::GetImpl(context);
106
107 const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
108 ASSERT(executable);
109 size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
110
111 initializeXFBBuffersDesc(contextVk, xfbBufferCount);
112
113 for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
114 {
115 mBufferHandles[bufferIndex] = mBufferHelpers[bufferIndex]->getBuffer().getHandle();
116 if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
117 {
118 if (mCounterBufferHandles[bufferIndex] == VK_NULL_HANDLE)
119 {
120 vk::BufferHelper &bufferHelper = mCounterBufferHelpers[bufferIndex];
121 ANGLE_TRY(bufferHelper.initSuballocation(
122 contextVk, contextVk->getRenderer()->getDeviceLocalMemoryTypeIndex(), 16,
123 contextVk->getRenderer()->getDefaultBufferAlignment()));
124 mCounterBufferHandles[bufferIndex] = bufferHelper.getBuffer().getHandle();
125 mCounterBufferOffsets[bufferIndex] = bufferHelper.getOffset();
126 }
127 }
128 }
129
130 if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
131 {
132 mRebindTransformFeedbackBuffer = true;
133 }
134
135 return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
136 mCounterBufferHelpers);
137 }
138
end(const gl::Context * context)139 angle::Result TransformFeedbackVk::end(const gl::Context *context)
140 {
141 ContextVk *contextVk = vk::GetImpl(context);
142
143 // If there's an active transform feedback query, accumulate the primitives drawn.
144 const gl::State &glState = context->getState();
145 gl::Query *transformFeedbackQuery =
146 glState.getActiveQuery(gl::QueryType::TransformFeedbackPrimitivesWritten);
147
148 if (transformFeedbackQuery && contextVk->getFeatures().emulateTransformFeedback.enabled)
149 {
150 vk::GetImpl(transformFeedbackQuery)->onTransformFeedbackEnd(mState.getPrimitivesDrawn());
151 }
152
153 for (angle::ObserverBinding &bufferBinding : mBufferObserverBindings)
154 {
155 bufferBinding.reset();
156 }
157
158 contextVk->onEndTransformFeedback();
159
160 releaseCounterBuffers(contextVk->getRenderer());
161
162 return angle::Result::Continue;
163 }
164
pause(const gl::Context * context)165 angle::Result TransformFeedbackVk::pause(const gl::Context *context)
166 {
167 ContextVk *contextVk = vk::GetImpl(context);
168
169 if (contextVk->getFeatures().emulateTransformFeedback.enabled)
170 {
171 // Bind the empty buffer until we resume.
172 const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
173 ASSERT(executable);
174 size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
175
176 const vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer();
177
178 for (size_t xfbIndex = 0; xfbIndex < xfbBufferCount; ++xfbIndex)
179 {
180 mXFBBuffersDesc.updateTransformFeedbackBuffer(xfbIndex, emptyBuffer.getBufferSerial(),
181 0);
182 }
183 }
184
185 return contextVk->onPauseTransformFeedback();
186 }
187
resume(const gl::Context * context)188 angle::Result TransformFeedbackVk::resume(const gl::Context *context)
189 {
190 ContextVk *contextVk = vk::GetImpl(context);
191 const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
192 ASSERT(executable);
193 size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
194
195 if (contextVk->getFeatures().emulateTransformFeedback.enabled)
196 {
197 initializeXFBBuffersDesc(contextVk, xfbBufferCount);
198 }
199
200 return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
201 mCounterBufferHelpers);
202 }
203
bindIndexedBuffer(const gl::Context * context,size_t index,const gl::OffsetBindingPointer<gl::Buffer> & binding)204 angle::Result TransformFeedbackVk::bindIndexedBuffer(
205 const gl::Context *context,
206 size_t index,
207 const gl::OffsetBindingPointer<gl::Buffer> &binding)
208 {
209 ContextVk *contextVk = vk::GetImpl(context);
210
211 // Make sure the transform feedback buffers are bound to the program descriptor sets.
212 contextVk->invalidateCurrentTransformFeedbackBuffers();
213
214 return angle::Result::Continue;
215 }
216
updateDescriptorSetLayout(ContextVk * contextVk,const ShaderInterfaceVariableInfoMap & variableInfoMap,size_t xfbBufferCount,vk::DescriptorSetLayoutDesc * descSetLayoutOut) const217 void TransformFeedbackVk::updateDescriptorSetLayout(
218 ContextVk *contextVk,
219 const ShaderInterfaceVariableInfoMap &variableInfoMap,
220 size_t xfbBufferCount,
221 vk::DescriptorSetLayoutDesc *descSetLayoutOut) const
222 {
223 if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
224 {
225 return;
226 }
227
228 for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
229 {
230 const ShaderInterfaceVariableInfo &info =
231 variableInfoMap.getTransformFeedbackInfo(gl::ShaderType::Vertex, bufferIndex);
232
233 ASSERT(info.binding != std::numeric_limits<uint32_t>::max());
234
235 descSetLayoutOut->update(info.binding, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
236 VK_SHADER_STAGE_VERTEX_BIT, nullptr);
237 }
238 }
239
initDescriptorSet(vk::Context * context,UpdateDescriptorSetsBuilder * updateBuilder,vk::BufferHelper * emptyBuffer,const ShaderInterfaceVariableInfoMap & variableInfoMap,size_t xfbBufferCount,VkDescriptorSet descSet) const240 void TransformFeedbackVk::initDescriptorSet(vk::Context *context,
241 UpdateDescriptorSetsBuilder *updateBuilder,
242 vk::BufferHelper *emptyBuffer,
243 const ShaderInterfaceVariableInfoMap &variableInfoMap,
244 size_t xfbBufferCount,
245 VkDescriptorSet descSet) const
246 {
247 if (!context->getRenderer()->getFeatures().emulateTransformFeedback.enabled)
248 {
249 return;
250 }
251
252 VkDescriptorBufferInfo *descriptorBufferInfo =
253 updateBuilder->allocDescriptorBufferInfos(xfbBufferCount);
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 = emptyBuffer->getOffset();
260 bufferInfo.range = emptyBuffer->getSize();
261 }
262
263 writeDescriptorSet(context, updateBuilder, variableInfoMap, xfbBufferCount,
264 descriptorBufferInfo, descSet);
265 }
266
updateDescriptorSet(vk::Context * context,UpdateDescriptorSetsBuilder * updateBuilder,const gl::ProgramExecutable & executable,const ShaderInterfaceVariableInfoMap & variableInfoMap,VkDescriptorSet descSet) const267 void TransformFeedbackVk::updateDescriptorSet(vk::Context *context,
268 UpdateDescriptorSetsBuilder *updateBuilder,
269 const gl::ProgramExecutable &executable,
270 const ShaderInterfaceVariableInfoMap &variableInfoMap,
271 VkDescriptorSet descSet) const
272 {
273 RendererVk *renderer = context->getRenderer();
274
275 if (!renderer->getFeatures().emulateTransformFeedback.enabled)
276 {
277 return;
278 }
279
280 size_t xfbBufferCount = executable.getTransformFeedbackBufferCount();
281 const VkDeviceSize offsetAlignment =
282 renderer->getPhysicalDeviceProperties().limits.minStorageBufferOffsetAlignment;
283
284 ASSERT(xfbBufferCount > 0);
285 ASSERT(executable.getTransformFeedbackBufferMode() != GL_INTERLEAVED_ATTRIBS ||
286 xfbBufferCount == 1);
287
288 VkDescriptorBufferInfo *descriptorBufferInfo =
289 updateBuilder->allocDescriptorBufferInfos(xfbBufferCount);
290
291 // Update buffer descriptor binding info for output buffers
292 for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
293 {
294 VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[bufferIndex];
295
296 bufferInfo.buffer = mBufferHandles[bufferIndex];
297 // Set the offset as close as possible to the requested offset while remaining aligned.
298 bufferInfo.offset = (mBufferOffsets[bufferIndex] / offsetAlignment) * offsetAlignment;
299 bufferInfo.range =
300 mBufferSizes[bufferIndex] + (mBufferOffsets[bufferIndex] - bufferInfo.offset);
301 ASSERT(bufferInfo.range != 0);
302 }
303
304 writeDescriptorSet(context, updateBuilder, variableInfoMap, xfbBufferCount,
305 descriptorBufferInfo, descSet);
306 }
307
getBufferOffsets(ContextVk * contextVk,GLint drawCallFirstVertex,int32_t * offsetsOut,size_t offsetsSize) const308 void TransformFeedbackVk::getBufferOffsets(ContextVk *contextVk,
309 GLint drawCallFirstVertex,
310 int32_t *offsetsOut,
311 size_t offsetsSize) const
312 {
313 if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
314 {
315 return;
316 }
317
318 GLsizeiptr verticesDrawn = mState.getVerticesDrawn();
319 const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
320 ASSERT(executable);
321 const std::vector<GLsizei> &bufferStrides = executable->getTransformFeedbackStrides();
322 size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
323 const VkDeviceSize offsetAlignment = contextVk->getRenderer()
324 ->getPhysicalDeviceProperties()
325 .limits.minStorageBufferOffsetAlignment;
326
327 ASSERT(xfbBufferCount > 0);
328
329 // The caller should make sure the offsets array has enough space. The maximum possible
330 // number of outputs is gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS.
331 ASSERT(offsetsSize >= xfbBufferCount);
332
333 for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
334 {
335 int64_t offsetFromDescriptor =
336 static_cast<int64_t>(mBufferOffsets[bufferIndex] % offsetAlignment);
337 int64_t drawCallVertexOffset = static_cast<int64_t>(verticesDrawn) - drawCallFirstVertex;
338
339 int64_t writeOffset =
340 (offsetFromDescriptor + drawCallVertexOffset * bufferStrides[bufferIndex]) /
341 static_cast<int64_t>(sizeof(uint32_t));
342
343 offsetsOut[bufferIndex] = static_cast<int32_t>(writeOffset);
344
345 // Assert on overflow. For now, support transform feedback up to 2GB.
346 ASSERT(offsetsOut[bufferIndex] == writeOffset);
347 }
348 }
349
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)350 void TransformFeedbackVk::onSubjectStateChange(angle::SubjectIndex index,
351 angle::SubjectMessage message)
352 {
353 if (message == angle::SubjectMessage::BufferVkStorageChanged)
354 {
355 ASSERT(index < mBufferObserverBindings.size());
356 const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(index);
357 ASSERT(binding.get());
358
359 BufferVk *bufferVk = vk::GetImpl(binding.get());
360
361 ASSERT(bufferVk->isBufferValid());
362 mBufferHelpers[index] = &bufferVk->getBuffer();
363 mBufferOffsets[index] = binding.getOffset() + mBufferHelpers[index]->getOffset();
364 mBufferSizes[index] = gl::GetBoundBufferAvailableSize(binding);
365 mBufferObserverBindings[index].bind(bufferVk);
366
367 mXFBBuffersDesc.updateTransformFeedbackBuffer(
368 index, mBufferHelpers[index]->getBufferSerial(), mBufferOffsets[index]);
369
370 mBufferHandles[index] = mBufferHelpers[index]->getBuffer().getHandle();
371 }
372 }
373
writeDescriptorSet(vk::Context * context,UpdateDescriptorSetsBuilder * updateBuilder,const ShaderInterfaceVariableInfoMap & variableInfoMap,size_t xfbBufferCount,VkDescriptorBufferInfo * bufferInfo,VkDescriptorSet descSet) const374 void TransformFeedbackVk::writeDescriptorSet(vk::Context *context,
375 UpdateDescriptorSetsBuilder *updateBuilder,
376 const ShaderInterfaceVariableInfoMap &variableInfoMap,
377 size_t xfbBufferCount,
378 VkDescriptorBufferInfo *bufferInfo,
379 VkDescriptorSet descSet) const
380 {
381 ASSERT(context->getRenderer()->getFeatures().emulateTransformFeedback.enabled);
382
383 const ShaderInterfaceVariableInfo &info =
384 variableInfoMap.getTransformFeedbackInfo(gl::ShaderType::Vertex, 0);
385
386 VkWriteDescriptorSet &writeDescriptorInfo = updateBuilder->allocWriteDescriptorSet();
387 writeDescriptorInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
388 writeDescriptorInfo.dstSet = descSet;
389 writeDescriptorInfo.dstBinding = info.binding;
390 writeDescriptorInfo.dstArrayElement = 0;
391 writeDescriptorInfo.descriptorCount = static_cast<uint32_t>(xfbBufferCount);
392 writeDescriptorInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
393 writeDescriptorInfo.pImageInfo = nullptr;
394 writeDescriptorInfo.pBufferInfo = bufferInfo;
395 writeDescriptorInfo.pTexelBufferView = nullptr;
396 }
397
398 } // namespace rx
399