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