blob: 86b7eca0f403ef9c1f87cbaa488c9fea4efbfb73 [file] [log] [blame]
Jamie Madill1f46bc12018-02-20 16:09:43 -05001//
2// Copyright 2017 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// CommandGraph:
7// Deferred work constructed by GL calls, that will later be flushed to Vulkan.
8//
9
10#include "libANGLE/renderer/vulkan/CommandGraph.h"
11
12#include "libANGLE/renderer/vulkan/RenderTargetVk.h"
13#include "libANGLE/renderer/vulkan/RendererVk.h"
14#include "libANGLE/renderer/vulkan/vk_format_utils.h"
15
16namespace rx
17{
18
19namespace vk
20{
21
22namespace
23{
24
25Error InitAndBeginCommandBuffer(VkDevice device,
26 const CommandPool &commandPool,
27 const VkCommandBufferInheritanceInfo &inheritanceInfo,
28 VkCommandBufferUsageFlags flags,
29 CommandBuffer *commandBuffer)
30{
31 ASSERT(!commandBuffer->valid());
32
33 VkCommandBufferAllocateInfo createInfo;
34 createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
35 createInfo.pNext = nullptr;
36 createInfo.commandPool = commandPool.getHandle();
37 createInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
38 createInfo.commandBufferCount = 1;
39
40 ANGLE_TRY(commandBuffer->init(device, createInfo));
41
42 VkCommandBufferBeginInfo beginInfo;
43 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
44 beginInfo.pNext = nullptr;
45 beginInfo.flags = flags | VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
46 beginInfo.pInheritanceInfo = &inheritanceInfo;
47
48 ANGLE_TRY(commandBuffer->begin(beginInfo));
49 return NoError();
50}
51
52} // anonymous namespace
53
54// CommandGraphNode implementation.
55
56CommandGraphNode::CommandGraphNode() : mHasChildren(false), mVisitedState(VisitedState::Unvisited)
57{
58}
59
60CommandGraphNode::~CommandGraphNode()
61{
62 mRenderPassFramebuffer.setHandle(VK_NULL_HANDLE);
63
64 // Command buffers are managed by the command pool, so don't need to be freed.
65 mOutsideRenderPassCommands.releaseHandle();
66 mInsideRenderPassCommands.releaseHandle();
67}
68
69CommandBuffer *CommandGraphNode::getOutsideRenderPassCommands()
70{
71 ASSERT(!mHasChildren);
72 return &mOutsideRenderPassCommands;
73}
74
75CommandBuffer *CommandGraphNode::getInsideRenderPassCommands()
76{
77 ASSERT(!mHasChildren);
78 return &mInsideRenderPassCommands;
79}
80
81Error CommandGraphNode::beginOutsideRenderPassRecording(VkDevice device,
82 const CommandPool &commandPool,
83 CommandBuffer **commandsOut)
84{
85 ASSERT(!mHasChildren);
86
87 VkCommandBufferInheritanceInfo inheritanceInfo;
88 inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
89 inheritanceInfo.pNext = nullptr;
90 inheritanceInfo.renderPass = VK_NULL_HANDLE;
91 inheritanceInfo.subpass = 0;
92 inheritanceInfo.framebuffer = VK_NULL_HANDLE;
93 inheritanceInfo.occlusionQueryEnable = VK_FALSE;
94 inheritanceInfo.queryFlags = 0;
95 inheritanceInfo.pipelineStatistics = 0;
96
97 ANGLE_TRY(InitAndBeginCommandBuffer(device, commandPool, inheritanceInfo, 0,
98 &mOutsideRenderPassCommands));
99
100 *commandsOut = &mOutsideRenderPassCommands;
101 return NoError();
102}
103
104Error CommandGraphNode::beginInsideRenderPassRecording(RendererVk *renderer,
105 CommandBuffer **commandsOut)
106{
107 ASSERT(!mHasChildren);
108
109 // Get a compatible RenderPass from the cache so we can initialize the inheritance info.
110 // TODO(jmadill): Support query for compatible/conformant render pass. htto://anglebug.com/2361
111 RenderPass *compatibleRenderPass;
112 ANGLE_TRY(renderer->getCompatibleRenderPass(mRenderPassDesc, &compatibleRenderPass));
113
114 VkCommandBufferInheritanceInfo inheritanceInfo;
115 inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
116 inheritanceInfo.pNext = nullptr;
117 inheritanceInfo.renderPass = compatibleRenderPass->getHandle();
118 inheritanceInfo.subpass = 0;
119 inheritanceInfo.framebuffer = mRenderPassFramebuffer.getHandle();
120 inheritanceInfo.occlusionQueryEnable = VK_FALSE;
121 inheritanceInfo.queryFlags = 0;
122 inheritanceInfo.pipelineStatistics = 0;
123
124 ANGLE_TRY(InitAndBeginCommandBuffer(
125 renderer->getDevice(), renderer->getCommandPool(), inheritanceInfo,
126 VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, &mInsideRenderPassCommands));
127
128 *commandsOut = &mInsideRenderPassCommands;
129 return NoError();
130}
131
132void CommandGraphNode::storeRenderPassInfo(const Framebuffer &framebuffer,
133 const gl::Rectangle renderArea,
134 const std::vector<VkClearValue> &clearValues)
135{
136 mRenderPassFramebuffer.setHandle(framebuffer.getHandle());
137 mRenderPassRenderArea = renderArea;
138 std::copy(clearValues.begin(), clearValues.end(), mRenderPassClearValues.begin());
139}
140
141void CommandGraphNode::appendColorRenderTarget(Serial serial, RenderTargetVk *colorRenderTarget)
142{
Jamie Madillbc543422018-03-30 10:43:19 -0400143 mRenderPassDesc.packColorAttachment(*colorRenderTarget->image);
Jamie Madill1f46bc12018-02-20 16:09:43 -0500144 colorRenderTarget->resource->onWriteResource(this, serial);
145}
146
147void CommandGraphNode::appendDepthStencilRenderTarget(Serial serial,
148 RenderTargetVk *depthStencilRenderTarget)
149{
Jamie Madillbc543422018-03-30 10:43:19 -0400150 mRenderPassDesc.packDepthStencilAttachment(*depthStencilRenderTarget->image);
Jamie Madill1f46bc12018-02-20 16:09:43 -0500151 depthStencilRenderTarget->resource->onWriteResource(this, serial);
152}
153
154// static
155void CommandGraphNode::SetHappensBeforeDependency(CommandGraphNode *beforeNode,
156 CommandGraphNode *afterNode)
157{
158 afterNode->mParents.emplace_back(beforeNode);
159 beforeNode->setHasChildren();
160 ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
161}
162
163// static
164void CommandGraphNode::SetHappensBeforeDependencies(
165 const std::vector<CommandGraphNode *> &beforeNodes,
166 CommandGraphNode *afterNode)
167{
168 afterNode->mParents.insert(afterNode->mParents.end(), beforeNodes.begin(), beforeNodes.end());
169
170 // TODO(jmadill): is there a faster way to do this?
171 for (CommandGraphNode *beforeNode : beforeNodes)
172 {
173 beforeNode->setHasChildren();
174
175 ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
176 }
177}
178
179bool CommandGraphNode::hasParents() const
180{
181 return !mParents.empty();
182}
183
184void CommandGraphNode::setHasChildren()
185{
186 mHasChildren = true;
187}
188
189bool CommandGraphNode::hasChildren() const
190{
191 return mHasChildren;
192}
193
194// Do not call this in anything but testing code, since it's slow.
195bool CommandGraphNode::isChildOf(CommandGraphNode *parent)
196{
197 std::set<CommandGraphNode *> visitedList;
198 std::vector<CommandGraphNode *> openList;
199 openList.insert(openList.begin(), mParents.begin(), mParents.end());
200 while (!openList.empty())
201 {
202 CommandGraphNode *current = openList.back();
203 openList.pop_back();
204 if (visitedList.count(current) == 0)
205 {
206 if (current == parent)
207 {
208 return true;
209 }
210 visitedList.insert(current);
211 openList.insert(openList.end(), current->mParents.begin(), current->mParents.end());
212 }
213 }
214
215 return false;
216}
217
218VisitedState CommandGraphNode::visitedState() const
219{
220 return mVisitedState;
221}
222
223void CommandGraphNode::visitParents(std::vector<CommandGraphNode *> *stack)
224{
225 ASSERT(mVisitedState == VisitedState::Unvisited);
226 stack->insert(stack->end(), mParents.begin(), mParents.end());
227 mVisitedState = VisitedState::Ready;
228}
229
230Error CommandGraphNode::visitAndExecute(VkDevice device,
231 Serial serial,
232 RenderPassCache *renderPassCache,
233 CommandBuffer *primaryCommandBuffer)
234{
235 if (mOutsideRenderPassCommands.valid())
236 {
237 mOutsideRenderPassCommands.end();
238 primaryCommandBuffer->executeCommands(1, &mOutsideRenderPassCommands);
239 }
240
241 if (mInsideRenderPassCommands.valid())
242 {
243 // Pull a compatible RenderPass from the cache.
244 // TODO(jmadill): Insert real ops and layout transitions.
245 RenderPass *renderPass = nullptr;
246 ANGLE_TRY(
247 renderPassCache->getCompatibleRenderPass(device, serial, mRenderPassDesc, &renderPass));
248
249 mInsideRenderPassCommands.end();
250
251 VkRenderPassBeginInfo beginInfo;
252 beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
253 beginInfo.pNext = nullptr;
254 beginInfo.renderPass = renderPass->getHandle();
255 beginInfo.framebuffer = mRenderPassFramebuffer.getHandle();
256 beginInfo.renderArea.offset.x = static_cast<uint32_t>(mRenderPassRenderArea.x);
257 beginInfo.renderArea.offset.y = static_cast<uint32_t>(mRenderPassRenderArea.y);
258 beginInfo.renderArea.extent.width = static_cast<uint32_t>(mRenderPassRenderArea.width);
259 beginInfo.renderArea.extent.height = static_cast<uint32_t>(mRenderPassRenderArea.height);
260 beginInfo.clearValueCount = mRenderPassDesc.attachmentCount();
261 beginInfo.pClearValues = mRenderPassClearValues.data();
262
263 primaryCommandBuffer->beginRenderPass(beginInfo,
264 VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
265 primaryCommandBuffer->executeCommands(1, &mInsideRenderPassCommands);
266 primaryCommandBuffer->endRenderPass();
267 }
268
269 mVisitedState = VisitedState::Visited;
270 return NoError();
271}
272
273// CommandGraph implementation.
274CommandGraph::CommandGraph()
275{
276}
277
278CommandGraph::~CommandGraph()
279{
280 ASSERT(empty());
281}
282
283CommandGraphNode *CommandGraph::allocateNode()
284{
285 // TODO(jmadill): Use a pool allocator for the CPU node allocations.
286 CommandGraphNode *newCommands = new CommandGraphNode();
287 mNodes.emplace_back(newCommands);
288 return newCommands;
289}
290
291Error CommandGraph::submitCommands(VkDevice device,
292 Serial serial,
293 RenderPassCache *renderPassCache,
294 CommandPool *commandPool,
295 CommandBuffer *primaryCommandBufferOut)
296{
297 VkCommandBufferAllocateInfo primaryInfo;
298 primaryInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
299 primaryInfo.pNext = nullptr;
300 primaryInfo.commandPool = commandPool->getHandle();
301 primaryInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
302 primaryInfo.commandBufferCount = 1;
303
304 ANGLE_TRY(primaryCommandBufferOut->init(device, primaryInfo));
305
306 if (mNodes.empty())
307 {
308 return NoError();
309 }
310
311 std::vector<CommandGraphNode *> nodeStack;
312
313 VkCommandBufferBeginInfo beginInfo;
314 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
315 beginInfo.pNext = nullptr;
316 beginInfo.flags = 0;
317 beginInfo.pInheritanceInfo = nullptr;
318
319 ANGLE_TRY(primaryCommandBufferOut->begin(beginInfo));
320
321 for (CommandGraphNode *topLevelNode : mNodes)
322 {
323 // Only process commands that don't have child commands. The others will be pulled in
324 // automatically. Also skip commands that have already been visited.
325 if (topLevelNode->hasChildren() || topLevelNode->visitedState() != VisitedState::Unvisited)
326 continue;
327
328 nodeStack.push_back(topLevelNode);
329
330 while (!nodeStack.empty())
331 {
332 CommandGraphNode *node = nodeStack.back();
333
334 switch (node->visitedState())
335 {
336 case VisitedState::Unvisited:
337 node->visitParents(&nodeStack);
338 break;
339 case VisitedState::Ready:
340 ANGLE_TRY(node->visitAndExecute(device, serial, renderPassCache,
341 primaryCommandBufferOut));
342 nodeStack.pop_back();
343 break;
344 case VisitedState::Visited:
345 nodeStack.pop_back();
346 break;
347 default:
348 UNREACHABLE();
349 break;
350 }
351 }
352 }
353
354 ANGLE_TRY(primaryCommandBufferOut->end());
355
356 // TODO(jmadill): Use pool allocation so we don't need to deallocate command graph.
357 for (vk::CommandGraphNode *node : mNodes)
358 {
359 delete node;
360 }
361 mNodes.clear();
362
363 return NoError();
364}
365
366bool CommandGraph::empty() const
367{
368 return mNodes.empty();
369}
370
371} // namespace vk
372} // namespace rx