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/vulkan/BufferVk.h"
15 #include "libANGLE/renderer/vulkan/ContextVk.h"
16 #include "libANGLE/renderer/vulkan/FramebufferVk.h"
17 #include "libANGLE/renderer/vulkan/ProgramVk.h"
18 #include "libANGLE/renderer/vulkan/QueryVk.h"
19 #include "libANGLE/renderer/vulkan/ShaderInterfaceVariableInfoMap.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
initializeXFBVariables(ContextVk * contextVk,uint32_t xfbBufferCount)68 void TransformFeedbackVk::initializeXFBVariables(ContextVk *contextVk, uint32_t xfbBufferCount)
69 {
70 for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
71 {
72 const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(bufferIndex);
73 ASSERT(binding.get());
74
75 BufferVk *bufferVk = vk::GetImpl(binding.get());
76
77 if (bufferVk->isBufferValid())
78 {
79 mBufferHelpers[bufferIndex] = &bufferVk->getBuffer();
80 mBufferOffsets[bufferIndex] =
81 binding.getOffset() + mBufferHelpers[bufferIndex]->getOffset();
82 mBufferSizes[bufferIndex] = gl::GetBoundBufferAvailableSize(binding);
83 mBufferObserverBindings[bufferIndex].bind(bufferVk);
84 }
85 else
86 {
87 // This can happen in error conditions.
88 vk::BufferHelper &nullBuffer = contextVk->getEmptyBuffer();
89 mBufferHelpers[bufferIndex] = &nullBuffer;
90 mBufferOffsets[bufferIndex] = 0;
91 mBufferSizes[bufferIndex] = nullBuffer.getSize();
92 mBufferObserverBindings[bufferIndex].reset();
93 }
94 }
95 }
96
begin(const gl::Context * context,gl::PrimitiveMode primitiveMode)97 angle::Result TransformFeedbackVk::begin(const gl::Context *context,
98 gl::PrimitiveMode primitiveMode)
99 {
100 ContextVk *contextVk = vk::GetImpl(context);
101
102 const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
103 ASSERT(executable);
104 size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
105
106 initializeXFBVariables(contextVk, static_cast<uint32_t>(xfbBufferCount));
107
108 for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
109 {
110 mBufferHandles[bufferIndex] = mBufferHelpers[bufferIndex]->getBuffer().getHandle();
111 if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
112 {
113 if (mCounterBufferHandles[bufferIndex] == VK_NULL_HANDLE)
114 {
115 vk::BufferHelper &bufferHelper = mCounterBufferHelpers[bufferIndex];
116 ANGLE_TRY(bufferHelper.initSuballocation(
117 contextVk, contextVk->getRenderer()->getDeviceLocalMemoryTypeIndex(), 16,
118 contextVk->getRenderer()->getDefaultBufferAlignment(),
119 BufferUsageType::Static));
120 mCounterBufferHandles[bufferIndex] = bufferHelper.getBuffer().getHandle();
121 mCounterBufferOffsets[bufferIndex] = bufferHelper.getOffset();
122 }
123 }
124 }
125
126 if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
127 {
128 mRebindTransformFeedbackBuffer = true;
129 }
130
131 return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
132 mCounterBufferHelpers);
133 }
134
end(const gl::Context * context)135 angle::Result TransformFeedbackVk::end(const gl::Context *context)
136 {
137 ContextVk *contextVk = vk::GetImpl(context);
138
139 // If there's an active transform feedback query, accumulate the primitives drawn.
140 const gl::State &glState = context->getState();
141 gl::Query *transformFeedbackQuery =
142 glState.getActiveQuery(gl::QueryType::TransformFeedbackPrimitivesWritten);
143
144 if (transformFeedbackQuery && contextVk->getFeatures().emulateTransformFeedback.enabled)
145 {
146 vk::GetImpl(transformFeedbackQuery)->onTransformFeedbackEnd(mState.getPrimitivesDrawn());
147 }
148
149 for (angle::ObserverBinding &bufferBinding : mBufferObserverBindings)
150 {
151 bufferBinding.reset();
152 }
153
154 contextVk->onEndTransformFeedback();
155
156 releaseCounterBuffers(contextVk->getRenderer());
157
158 return angle::Result::Continue;
159 }
160
pause(const gl::Context * context)161 angle::Result TransformFeedbackVk::pause(const gl::Context *context)
162 {
163 ContextVk *contextVk = vk::GetImpl(context);
164 return contextVk->onPauseTransformFeedback();
165 }
166
resume(const gl::Context * context)167 angle::Result TransformFeedbackVk::resume(const gl::Context *context)
168 {
169 ContextVk *contextVk = vk::GetImpl(context);
170 const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
171 ASSERT(executable);
172 size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
173
174 if (contextVk->getFeatures().emulateTransformFeedback.enabled)
175 {
176 initializeXFBVariables(contextVk, static_cast<uint32_t>(xfbBufferCount));
177 }
178
179 return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
180 mCounterBufferHelpers);
181 }
182
bindIndexedBuffer(const gl::Context * context,size_t index,const gl::OffsetBindingPointer<gl::Buffer> & binding)183 angle::Result TransformFeedbackVk::bindIndexedBuffer(
184 const gl::Context *context,
185 size_t index,
186 const gl::OffsetBindingPointer<gl::Buffer> &binding)
187 {
188 ContextVk *contextVk = vk::GetImpl(context);
189
190 // Make sure the transform feedback buffers are bound to the program descriptor sets.
191 contextVk->invalidateCurrentTransformFeedbackBuffers();
192
193 return angle::Result::Continue;
194 }
195
updateDescriptorSetLayout(ContextVk * contextVk,const ShaderInterfaceVariableInfoMap & variableInfoMap,size_t xfbBufferCount,vk::DescriptorSetLayoutDesc * descSetLayoutOut) const196 void TransformFeedbackVk::updateDescriptorSetLayout(
197 ContextVk *contextVk,
198 const ShaderInterfaceVariableInfoMap &variableInfoMap,
199 size_t xfbBufferCount,
200 vk::DescriptorSetLayoutDesc *descSetLayoutOut) const
201 {
202 if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
203 {
204 return;
205 }
206
207 for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
208 {
209 const uint32_t binding = variableInfoMap.getEmulatedXfbBufferBinding(bufferIndex);
210
211 ASSERT(binding != std::numeric_limits<uint32_t>::max());
212
213 descSetLayoutOut->update(binding, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
214 VK_SHADER_STAGE_VERTEX_BIT, nullptr);
215 }
216 }
217
getBufferOffsets(ContextVk * contextVk,GLint drawCallFirstVertex,int32_t * offsetsOut,size_t offsetsSize) const218 void TransformFeedbackVk::getBufferOffsets(ContextVk *contextVk,
219 GLint drawCallFirstVertex,
220 int32_t *offsetsOut,
221 size_t offsetsSize) const
222 {
223 if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
224 {
225 return;
226 }
227
228 GLsizeiptr verticesDrawn = mState.getVerticesDrawn();
229 const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
230 ASSERT(executable);
231 const std::vector<GLsizei> &bufferStrides = executable->getTransformFeedbackStrides();
232 size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
233 const VkDeviceSize offsetAlignment = contextVk->getRenderer()
234 ->getPhysicalDeviceProperties()
235 .limits.minStorageBufferOffsetAlignment;
236
237 ASSERT(xfbBufferCount > 0);
238
239 // The caller should make sure the offsets array has enough space. The maximum possible
240 // number of outputs is gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS.
241 ASSERT(offsetsSize >= xfbBufferCount);
242
243 for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
244 {
245 int64_t offsetFromDescriptor =
246 static_cast<int64_t>(mBufferOffsets[bufferIndex] % offsetAlignment);
247 int64_t drawCallVertexOffset = static_cast<int64_t>(verticesDrawn) - drawCallFirstVertex;
248
249 int64_t writeOffset =
250 (offsetFromDescriptor + drawCallVertexOffset * bufferStrides[bufferIndex]) /
251 static_cast<int64_t>(sizeof(uint32_t));
252
253 offsetsOut[bufferIndex] = static_cast<int32_t>(writeOffset);
254
255 // Assert on overflow. For now, support transform feedback up to 2GB.
256 ASSERT(offsetsOut[bufferIndex] == writeOffset);
257 }
258 }
259
onSubjectStateChange(angle::SubjectIndex index,angle::SubjectMessage message)260 void TransformFeedbackVk::onSubjectStateChange(angle::SubjectIndex index,
261 angle::SubjectMessage message)
262 {
263 if (message == angle::SubjectMessage::InternalMemoryAllocationChanged)
264 {
265 ASSERT(index < mBufferObserverBindings.size());
266 const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(index);
267 ASSERT(binding.get());
268
269 BufferVk *bufferVk = vk::GetImpl(binding.get());
270
271 ASSERT(bufferVk->isBufferValid());
272 mBufferHelpers[index] = &bufferVk->getBuffer();
273 mBufferOffsets[index] = binding.getOffset() + mBufferHelpers[index]->getOffset();
274 mBufferSizes[index] = std::min<VkDeviceSize>(gl::GetBoundBufferAvailableSize(binding),
275 mBufferHelpers[index]->getSize());
276 mBufferObserverBindings[index].bind(bufferVk);
277 mBufferHandles[index] = mBufferHelpers[index]->getBuffer().getHandle();
278 }
279 }
280
updateTransformFeedbackDescriptorDesc(const vk::Context * context,const gl::ProgramExecutable & executable,const ShaderInterfaceVariableInfoMap & variableInfoMap,const vk::WriteDescriptorDescs & writeDescriptorDescs,const vk::BufferHelper & emptyBuffer,bool activeUnpaused,vk::DescriptorSetDescBuilder * builder) const281 void TransformFeedbackVk::updateTransformFeedbackDescriptorDesc(
282 const vk::Context *context,
283 const gl::ProgramExecutable &executable,
284 const ShaderInterfaceVariableInfoMap &variableInfoMap,
285 const vk::WriteDescriptorDescs &writeDescriptorDescs,
286 const vk::BufferHelper &emptyBuffer,
287 bool activeUnpaused,
288 vk::DescriptorSetDescBuilder *builder) const
289 {
290 size_t xfbBufferCount = executable.getTransformFeedbackBufferCount();
291
292 for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
293 {
294 if (mBufferHelpers[bufferIndex] && activeUnpaused)
295 {
296 builder->updateTransformFeedbackBuffer(context, variableInfoMap, writeDescriptorDescs,
297 bufferIndex, *mBufferHelpers[bufferIndex],
298 mBufferOffsets[bufferIndex],
299 mBufferSizes[bufferIndex]);
300 }
301 else
302 {
303 builder->updateTransformFeedbackBuffer(context, variableInfoMap, writeDescriptorDescs,
304 bufferIndex, emptyBuffer, 0,
305 emptyBuffer.getSize());
306 }
307 }
308 }
309
onNewDescriptorSet(const gl::ProgramExecutable & executable,const vk::SharedDescriptorSetCacheKey & sharedCacheKey)310 void TransformFeedbackVk::onNewDescriptorSet(const gl::ProgramExecutable &executable,
311 const vk::SharedDescriptorSetCacheKey &sharedCacheKey)
312 {
313 size_t xfbBufferCount = executable.getTransformFeedbackBufferCount();
314 for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
315 {
316 const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(bufferIndex);
317 if (binding.get())
318 {
319 BufferVk *bufferVk = vk::GetImpl(binding.get());
320 if (bufferVk->getBuffer().valid())
321 {
322 bufferVk->getBuffer().getBufferBlock()->onNewDescriptorSet(sharedCacheKey);
323 }
324 }
325 }
326 }
327 } // namespace rx
328