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