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