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