• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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