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
20 #include "common/debug.h"
21
22 namespace rx
23 {
24
TransformFeedbackVk(const gl::TransformFeedbackState & state)25 TransformFeedbackVk::TransformFeedbackVk(const gl::TransformFeedbackState &state)
26 : TransformFeedbackImpl(state)
27 {}
28
~TransformFeedbackVk()29 TransformFeedbackVk::~TransformFeedbackVk() {}
30
begin(const gl::Context * context,gl::PrimitiveMode primitiveMode)31 angle::Result TransformFeedbackVk::begin(const gl::Context *context,
32 gl::PrimitiveMode primitiveMode)
33 {
34 ContextVk *contextVk = vk::GetImpl(context);
35
36 // Make sure the transform feedback buffers are bound to the program descriptor sets.
37 contextVk->invalidateCurrentTransformFeedbackBuffers();
38
39 vk::GetImpl(context)->onTransformFeedbackPauseResume();
40 onBeginOrEnd(context);
41
42 return angle::Result::Continue;
43 }
44
end(const gl::Context * context)45 angle::Result TransformFeedbackVk::end(const gl::Context *context)
46 {
47 // If there's an active transform feedback query, accumulate the primitives drawn.
48 const gl::State &glState = context->getState();
49 gl::Query *transformFeedbackQuery =
50 glState.getActiveQuery(gl::QueryType::TransformFeedbackPrimitivesWritten);
51 if (transformFeedbackQuery)
52 {
53 vk::GetImpl(transformFeedbackQuery)->onTransformFeedbackEnd(context);
54 }
55
56 vk::GetImpl(context)->onTransformFeedbackPauseResume();
57 onBeginOrEnd(context);
58
59 return angle::Result::Continue;
60 }
61
pause(const gl::Context * context)62 angle::Result TransformFeedbackVk::pause(const gl::Context *context)
63 {
64 vk::GetImpl(context)->onTransformFeedbackPauseResume();
65 return angle::Result::Continue;
66 }
67
resume(const gl::Context * context)68 angle::Result TransformFeedbackVk::resume(const gl::Context *context)
69 {
70 vk::GetImpl(context)->onTransformFeedbackPauseResume();
71 return angle::Result::Continue;
72 }
73
bindIndexedBuffer(const gl::Context * context,size_t index,const gl::OffsetBindingPointer<gl::Buffer> & binding)74 angle::Result TransformFeedbackVk::bindIndexedBuffer(
75 const gl::Context *context,
76 size_t index,
77 const gl::OffsetBindingPointer<gl::Buffer> &binding)
78 {
79 RendererVk *rendererVk = vk::GetImpl(context)->getRenderer();
80 const VkDeviceSize offsetAlignment =
81 rendererVk->getPhysicalDeviceProperties().limits.minStorageBufferOffsetAlignment;
82
83 // Make sure there's no possible under/overflow with binding size.
84 static_assert(sizeof(VkDeviceSize) >= sizeof(binding.getSize()), "VkDeviceSize too small");
85
86 mBoundBufferRanges[index].offset = binding.getOffset();
87 mBoundBufferRanges[index].size = gl::GetBoundBufferAvailableSize(binding);
88
89 // Set the offset as close as possible to the requested offset while remaining aligned.
90 mBoundBufferRanges[index].alignedOffset =
91 (mBoundBufferRanges[index].offset / offsetAlignment) * offsetAlignment;
92
93 return angle::Result::Continue;
94 }
95
updateDescriptorSetLayout(const gl::ProgramState & programState,vk::DescriptorSetLayoutDesc * descSetLayoutOut) const96 void TransformFeedbackVk::updateDescriptorSetLayout(
97 const gl::ProgramState &programState,
98 vk::DescriptorSetLayoutDesc *descSetLayoutOut) const
99 {
100 size_t xfbBufferCount = programState.getTransformFeedbackBufferCount();
101
102 for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
103 {
104 descSetLayoutOut->update(kXfbBindingIndexStart + bufferIndex,
105 VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT);
106 }
107 }
108
addFramebufferDependency(ContextVk * contextVk,const gl::ProgramState & programState,vk::FramebufferHelper * framebuffer) const109 void TransformFeedbackVk::addFramebufferDependency(ContextVk *contextVk,
110 const gl::ProgramState &programState,
111 vk::FramebufferHelper *framebuffer) const
112 {
113 const std::vector<gl::OffsetBindingPointer<gl::Buffer>> &xfbBuffers =
114 mState.getIndexedBuffers();
115 size_t xfbBufferCount = programState.getTransformFeedbackBufferCount();
116
117 ASSERT(xfbBufferCount > 0);
118 ASSERT(programState.getTransformFeedbackBufferMode() != GL_INTERLEAVED_ATTRIBS ||
119 xfbBufferCount == 1);
120
121 // Set framebuffer dependent to the transform feedback buffers. This is especially done
122 // separately from |updateDescriptorSet|, to avoid introducing unnecessary buffer barriers
123 // every time the descriptor set is updated (which, as the set is shared with default uniforms,
124 // could get updated frequently).
125 for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
126 {
127 const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding = xfbBuffers[bufferIndex];
128 gl::Buffer *buffer = bufferBinding.get();
129 ASSERT(buffer != nullptr);
130
131 vk::BufferHelper &bufferHelper = vk::GetImpl(buffer)->getBuffer();
132 bufferHelper.onWrite(contextVk, framebuffer, 0, VK_ACCESS_SHADER_WRITE_BIT);
133 }
134 }
135
updateDescriptorSet(ContextVk * contextVk,const gl::ProgramState & programState,VkDescriptorSet descSet) const136 void TransformFeedbackVk::updateDescriptorSet(ContextVk *contextVk,
137 const gl::ProgramState &programState,
138 VkDescriptorSet descSet) const
139 {
140 const std::vector<gl::OffsetBindingPointer<gl::Buffer>> &xfbBuffers =
141 mState.getIndexedBuffers();
142 size_t xfbBufferCount = programState.getTransformFeedbackBufferCount();
143
144 ASSERT(xfbBufferCount > 0);
145 ASSERT(programState.getTransformFeedbackBufferMode() != GL_INTERLEAVED_ATTRIBS ||
146 xfbBufferCount == 1);
147
148 std::array<VkDescriptorBufferInfo, gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS>
149 descriptorBufferInfo;
150
151 // Write default uniforms for each shader type.
152 for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
153 {
154 VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[bufferIndex];
155 const BoundBufferRange &bufferRange = mBoundBufferRanges[bufferIndex];
156
157 const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding = xfbBuffers[bufferIndex];
158 gl::Buffer *buffer = bufferBinding.get();
159 ASSERT(buffer != nullptr);
160
161 vk::BufferHelper &bufferHelper = vk::GetImpl(buffer)->getBuffer();
162
163 bufferInfo.buffer = bufferHelper.getBuffer().getHandle();
164 bufferInfo.offset = bufferRange.alignedOffset;
165 bufferInfo.range = bufferRange.size + (bufferRange.offset - bufferRange.alignedOffset);
166 }
167
168 VkDevice device = contextVk->getDevice();
169
170 VkWriteDescriptorSet writeDescriptorInfo = {};
171 writeDescriptorInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
172 writeDescriptorInfo.dstSet = descSet;
173 writeDescriptorInfo.dstBinding = kXfbBindingIndexStart;
174 writeDescriptorInfo.dstArrayElement = 0;
175 writeDescriptorInfo.descriptorCount = static_cast<uint32_t>(xfbBufferCount);
176 writeDescriptorInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
177 writeDescriptorInfo.pImageInfo = nullptr;
178 writeDescriptorInfo.pBufferInfo = descriptorBufferInfo.data();
179 writeDescriptorInfo.pTexelBufferView = nullptr;
180
181 vkUpdateDescriptorSets(device, 1, &writeDescriptorInfo, 0, nullptr);
182 }
183
getBufferOffsets(ContextVk * contextVk,const gl::ProgramState & programState,GLint drawCallFirstVertex,int32_t * offsetsOut,size_t offsetsSize) const184 void TransformFeedbackVk::getBufferOffsets(ContextVk *contextVk,
185 const gl::ProgramState &programState,
186 GLint drawCallFirstVertex,
187 int32_t *offsetsOut,
188 size_t offsetsSize) const
189 {
190 GLsizeiptr verticesDrawn = mState.getVerticesDrawn();
191 const std::vector<GLsizei> &bufferStrides =
192 mState.getBoundProgram()->getTransformFeedbackStrides();
193 size_t xfbBufferCount = programState.getTransformFeedbackBufferCount();
194
195 ASSERT(xfbBufferCount > 0);
196
197 // The caller should make sure the offsets array has enough space. The maximum possible number
198 // of outputs is gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS.
199 ASSERT(offsetsSize >= xfbBufferCount);
200
201 for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
202 {
203 const BoundBufferRange &bufferRange = mBoundBufferRanges[bufferIndex];
204
205 int64_t offsetFromDescriptor =
206 static_cast<int64_t>(bufferRange.offset - bufferRange.alignedOffset);
207 int64_t drawCallVertexOffset = static_cast<int64_t>(verticesDrawn) - drawCallFirstVertex;
208
209 int64_t writeOffset =
210 (offsetFromDescriptor + drawCallVertexOffset * bufferStrides[bufferIndex]) /
211 static_cast<int64_t>(sizeof(uint32_t));
212
213 offsetsOut[bufferIndex] = static_cast<int32_t>(writeOffset);
214
215 // Assert on overflow. For now, support transform feedback up to 2GB.
216 ASSERT(offsetsOut[bufferIndex] == writeOffset);
217 }
218 }
219
onBeginOrEnd(const gl::Context * context)220 void TransformFeedbackVk::onBeginOrEnd(const gl::Context *context)
221 {
222 // Currently, we don't handle resources switching from read-only to writable and back correctly.
223 // In the case of transform feedback, the attached buffers can switch between being written by
224 // transform feedback and being read as a vertex array buffer. For now, we'll end the render
225 // pass every time transform feedback is started or ended to work around this issue temporarily.
226 //
227 // TODO(syoussefi): detect changes to buffer usage (e.g. as transform feedback output, vertex
228 // or index data etc) in the front end and notify the backend. A new node should be created
229 // only on such changes. http://anglebug.com/3205
230 ContextVk *contextVk = vk::GetImpl(context);
231 FramebufferVk *framebufferVk = vk::GetImpl(context->getState().getDrawFramebuffer());
232 vk::FramebufferHelper *framebuffer = framebufferVk->getFramebuffer();
233
234 framebuffer->updateQueueSerial(contextVk->getCurrentQueueSerial());
235 if (framebuffer->hasStartedRenderPass())
236 {
237 framebuffer->finishCurrentCommands(contextVk);
238 }
239 }
240
241 } // namespace rx
242