blob: 788376d4a9c2c763c71760c64b9194af9a6018e4 [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"
Jamie Madill26084d02018-04-09 13:44:04 -040015#include "libANGLE/renderer/vulkan/vk_helpers.h"
Jamie Madill1f46bc12018-02-20 16:09:43 -050016
17namespace rx
18{
19
20namespace vk
21{
22
23namespace
24{
25
26Error InitAndBeginCommandBuffer(VkDevice device,
27 const CommandPool &commandPool,
28 const VkCommandBufferInheritanceInfo &inheritanceInfo,
29 VkCommandBufferUsageFlags flags,
30 CommandBuffer *commandBuffer)
31{
32 ASSERT(!commandBuffer->valid());
33
34 VkCommandBufferAllocateInfo createInfo;
35 createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
36 createInfo.pNext = nullptr;
37 createInfo.commandPool = commandPool.getHandle();
38 createInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
39 createInfo.commandBufferCount = 1;
40
41 ANGLE_TRY(commandBuffer->init(device, createInfo));
42
43 VkCommandBufferBeginInfo beginInfo;
44 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
45 beginInfo.pNext = nullptr;
46 beginInfo.flags = flags | VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
47 beginInfo.pInheritanceInfo = &inheritanceInfo;
48
49 ANGLE_TRY(commandBuffer->begin(beginInfo));
50 return NoError();
51}
52
53} // anonymous namespace
54
Jamie Madill6c7ab7f2018-03-31 14:19:15 -040055// CommandGraphResource implementation.
56CommandGraphResource::CommandGraphResource() : mCurrentWritingNode(nullptr)
57{
58}
59
Jamie Madill316c6062018-05-29 10:49:45 -040060CommandGraphResource::~CommandGraphResource() = default;
Jamie Madill6c7ab7f2018-03-31 14:19:15 -040061
62void CommandGraphResource::updateQueueSerial(Serial queueSerial)
63{
64 ASSERT(queueSerial >= mStoredQueueSerial);
65
66 if (queueSerial > mStoredQueueSerial)
67 {
68 mCurrentWritingNode = nullptr;
69 mCurrentReadingNodes.clear();
70 mStoredQueueSerial = queueSerial;
71 }
72}
73
74Serial CommandGraphResource::getQueueSerial() const
75{
76 return mStoredQueueSerial;
77}
78
Jamie Madill9cceac42018-03-31 14:19:16 -040079bool CommandGraphResource::hasChildlessWritingNode() const
Jamie Madill6c7ab7f2018-03-31 14:19:15 -040080{
Jamie Madill9cceac42018-03-31 14:19:16 -040081 return (mCurrentWritingNode != nullptr && !mCurrentWritingNode->hasChildren());
Jamie Madill6c7ab7f2018-03-31 14:19:15 -040082}
83
Jamie Madill6c7ab7f2018-03-31 14:19:15 -040084CommandGraphNode *CommandGraphResource::getNewWritingNode(RendererVk *renderer)
85{
86 CommandGraphNode *newCommands = renderer->allocateCommandNode();
Jamie Madill316c6062018-05-29 10:49:45 -040087 onWriteImpl(newCommands, renderer->getCurrentQueueSerial());
Jamie Madill6c7ab7f2018-03-31 14:19:15 -040088 return newCommands;
89}
90
Jamie Madill316c6062018-05-29 10:49:45 -040091bool CommandGraphResource::hasStartedWriteResource() const
92{
93 return hasChildlessWritingNode() &&
94 mCurrentWritingNode->getOutsideRenderPassCommands()->valid();
95}
96
Jamie Madill6c7ab7f2018-03-31 14:19:15 -040097Error CommandGraphResource::beginWriteResource(RendererVk *renderer,
98 CommandBuffer **commandBufferOut)
99{
100 CommandGraphNode *commands = getNewWritingNode(renderer);
101
102 VkDevice device = renderer->getDevice();
103 ANGLE_TRY(commands->beginOutsideRenderPassRecording(device, renderer->getCommandPool(),
104 commandBufferOut));
105 return NoError();
106}
107
Jamie Madill316c6062018-05-29 10:49:45 -0400108Error CommandGraphResource::appendWriteResource(RendererVk *renderer,
109 CommandBuffer **commandBufferOut)
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400110{
Jamie Madill316c6062018-05-29 10:49:45 -0400111 if (!hasChildlessWritingNode())
112 {
113 return beginWriteResource(renderer, commandBufferOut);
114 }
115
116 CommandBuffer *outsideRenderPassCommands = mCurrentWritingNode->getOutsideRenderPassCommands();
117 if (!outsideRenderPassCommands->valid())
118 {
119 ANGLE_TRY(mCurrentWritingNode->beginOutsideRenderPassRecording(
120 renderer->getDevice(), renderer->getCommandPool(), commandBufferOut));
121 }
122 else
123 {
124 *commandBufferOut = outsideRenderPassCommands;
125 }
126
127 return NoError();
128}
129
130void CommandGraphResource::appendToRenderPass(class CommandBuffer **commandBufferOut) const
131{
132 ASSERT(hasStartedRenderPass());
133 *commandBufferOut = mCurrentWritingNode->getInsideRenderPassCommands();
134}
135
136bool CommandGraphResource::hasStartedRenderPass() const
137{
138 return hasChildlessWritingNode() && mCurrentWritingNode->getInsideRenderPassCommands()->valid();
139}
140
141const gl::Rectangle &CommandGraphResource::getRenderPassRenderArea() const
142{
143 ASSERT(hasStartedRenderPass());
144 return mCurrentWritingNode->getRenderPassRenderArea();
145}
146
147Error CommandGraphResource::beginRenderPass(RendererVk *renderer,
148 const Framebuffer &framebuffer,
149 const gl::Rectangle &renderArea,
150 const RenderPassDesc &renderPassDesc,
151 const std::vector<VkClearValue> &clearValues,
152 CommandBuffer **commandBufferOut) const
153{
154 // Hard-code RenderPass to clear the first render target to the current clear value.
155 // TODO(jmadill): Proper clear value implementation. http://anglebug.com/2361
156 mCurrentWritingNode->storeRenderPassInfo(framebuffer, renderArea, renderPassDesc, clearValues);
157
158 return mCurrentWritingNode->beginInsideRenderPassRecording(renderer, commandBufferOut);
159}
160
161void CommandGraphResource::onResourceChanged(RendererVk *renderer)
162{
163 getNewWritingNode(renderer);
164}
165
166void CommandGraphResource::addWriteDependency(CommandGraphResource *writingResource)
167{
168 CommandGraphNode *writingNode = writingResource->mCurrentWritingNode;
169 ASSERT(writingNode);
170
171 onWriteImpl(writingNode, writingResource->getQueueSerial());
172}
173
174void CommandGraphResource::onWriteImpl(CommandGraphNode *writingNode, Serial currentSerial)
175{
176 updateQueueSerial(currentSerial);
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400177
Jamie Madill9cceac42018-03-31 14:19:16 -0400178 // Make sure any open reads and writes finish before we execute 'writingNode'.
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400179 if (!mCurrentReadingNodes.empty())
180 {
181 CommandGraphNode::SetHappensBeforeDependencies(mCurrentReadingNodes, writingNode);
182 mCurrentReadingNodes.clear();
183 }
184
185 if (mCurrentWritingNode && mCurrentWritingNode != writingNode)
186 {
187 CommandGraphNode::SetHappensBeforeDependency(mCurrentWritingNode, writingNode);
188 }
189
190 mCurrentWritingNode = writingNode;
191}
192
Jamie Madill316c6062018-05-29 10:49:45 -0400193void CommandGraphResource::addReadDependency(CommandGraphResource *readingResource)
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400194{
Jamie Madill316c6062018-05-29 10:49:45 -0400195 updateQueueSerial(readingResource->getQueueSerial());
196
197 CommandGraphNode *readingNode = readingResource->mCurrentWritingNode;
198 ASSERT(readingNode);
Jamie Madill9cceac42018-03-31 14:19:16 -0400199
200 if (hasChildlessWritingNode())
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400201 {
Jamie Madill9cceac42018-03-31 14:19:16 -0400202 // Ensure 'readingNode' happens after the current writing node.
203 CommandGraphNode::SetHappensBeforeDependency(mCurrentWritingNode, readingNode);
204 }
205
206 // Add the read node to the list of nodes currently reading this resource.
207 mCurrentReadingNodes.push_back(readingNode);
208}
209
210bool CommandGraphResource::checkResourceInUseAndRefreshDeps(RendererVk *renderer)
211{
Jamie Madill26084d02018-04-09 13:44:04 -0400212 if (!renderer->isResourceInUse(*this) ||
213 (renderer->getCurrentQueueSerial() > mStoredQueueSerial))
Jamie Madill9cceac42018-03-31 14:19:16 -0400214 {
215 mCurrentReadingNodes.clear();
216 mCurrentWritingNode = nullptr;
217 return false;
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400218 }
219 else
220 {
Jamie Madill9cceac42018-03-31 14:19:16 -0400221 return true;
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400222 }
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400223}
224
Jamie Madill1f46bc12018-02-20 16:09:43 -0500225// CommandGraphNode implementation.
226
Jamie Madill316c6062018-05-29 10:49:45 -0400227CommandGraphNode::CommandGraphNode()
228 : mRenderPassClearValues{}, mHasChildren(false), mVisitedState(VisitedState::Unvisited)
Jamie Madill1f46bc12018-02-20 16:09:43 -0500229{
230}
231
232CommandGraphNode::~CommandGraphNode()
233{
234 mRenderPassFramebuffer.setHandle(VK_NULL_HANDLE);
235
236 // Command buffers are managed by the command pool, so don't need to be freed.
237 mOutsideRenderPassCommands.releaseHandle();
238 mInsideRenderPassCommands.releaseHandle();
239}
240
241CommandBuffer *CommandGraphNode::getOutsideRenderPassCommands()
242{
243 ASSERT(!mHasChildren);
244 return &mOutsideRenderPassCommands;
245}
246
247CommandBuffer *CommandGraphNode::getInsideRenderPassCommands()
248{
249 ASSERT(!mHasChildren);
250 return &mInsideRenderPassCommands;
251}
252
253Error CommandGraphNode::beginOutsideRenderPassRecording(VkDevice device,
254 const CommandPool &commandPool,
255 CommandBuffer **commandsOut)
256{
257 ASSERT(!mHasChildren);
258
259 VkCommandBufferInheritanceInfo inheritanceInfo;
260 inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
261 inheritanceInfo.pNext = nullptr;
262 inheritanceInfo.renderPass = VK_NULL_HANDLE;
263 inheritanceInfo.subpass = 0;
264 inheritanceInfo.framebuffer = VK_NULL_HANDLE;
265 inheritanceInfo.occlusionQueryEnable = VK_FALSE;
266 inheritanceInfo.queryFlags = 0;
267 inheritanceInfo.pipelineStatistics = 0;
268
269 ANGLE_TRY(InitAndBeginCommandBuffer(device, commandPool, inheritanceInfo, 0,
270 &mOutsideRenderPassCommands));
271
272 *commandsOut = &mOutsideRenderPassCommands;
273 return NoError();
274}
275
276Error CommandGraphNode::beginInsideRenderPassRecording(RendererVk *renderer,
277 CommandBuffer **commandsOut)
278{
279 ASSERT(!mHasChildren);
280
281 // Get a compatible RenderPass from the cache so we can initialize the inheritance info.
282 // TODO(jmadill): Support query for compatible/conformant render pass. htto://anglebug.com/2361
283 RenderPass *compatibleRenderPass;
284 ANGLE_TRY(renderer->getCompatibleRenderPass(mRenderPassDesc, &compatibleRenderPass));
285
286 VkCommandBufferInheritanceInfo inheritanceInfo;
287 inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
288 inheritanceInfo.pNext = nullptr;
289 inheritanceInfo.renderPass = compatibleRenderPass->getHandle();
290 inheritanceInfo.subpass = 0;
291 inheritanceInfo.framebuffer = mRenderPassFramebuffer.getHandle();
292 inheritanceInfo.occlusionQueryEnable = VK_FALSE;
293 inheritanceInfo.queryFlags = 0;
294 inheritanceInfo.pipelineStatistics = 0;
295
296 ANGLE_TRY(InitAndBeginCommandBuffer(
297 renderer->getDevice(), renderer->getCommandPool(), inheritanceInfo,
298 VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, &mInsideRenderPassCommands));
299
300 *commandsOut = &mInsideRenderPassCommands;
301 return NoError();
302}
303
304void CommandGraphNode::storeRenderPassInfo(const Framebuffer &framebuffer,
305 const gl::Rectangle renderArea,
Jamie Madillbcf467f2018-05-23 09:46:00 -0400306 const vk::RenderPassDesc &renderPassDesc,
Jamie Madill1f46bc12018-02-20 16:09:43 -0500307 const std::vector<VkClearValue> &clearValues)
308{
Jamie Madillbcf467f2018-05-23 09:46:00 -0400309 mRenderPassDesc = renderPassDesc;
Jamie Madill1f46bc12018-02-20 16:09:43 -0500310 mRenderPassFramebuffer.setHandle(framebuffer.getHandle());
311 mRenderPassRenderArea = renderArea;
312 std::copy(clearValues.begin(), clearValues.end(), mRenderPassClearValues.begin());
313}
314
Jamie Madill1f46bc12018-02-20 16:09:43 -0500315// static
316void CommandGraphNode::SetHappensBeforeDependency(CommandGraphNode *beforeNode,
317 CommandGraphNode *afterNode)
318{
Jamie Madill9cceac42018-03-31 14:19:16 -0400319 ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
Jamie Madill1f46bc12018-02-20 16:09:43 -0500320 afterNode->mParents.emplace_back(beforeNode);
321 beforeNode->setHasChildren();
Jamie Madill1f46bc12018-02-20 16:09:43 -0500322}
323
324// static
325void CommandGraphNode::SetHappensBeforeDependencies(
326 const std::vector<CommandGraphNode *> &beforeNodes,
327 CommandGraphNode *afterNode)
328{
329 afterNode->mParents.insert(afterNode->mParents.end(), beforeNodes.begin(), beforeNodes.end());
330
331 // TODO(jmadill): is there a faster way to do this?
332 for (CommandGraphNode *beforeNode : beforeNodes)
333 {
334 beforeNode->setHasChildren();
335
336 ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
337 }
338}
339
340bool CommandGraphNode::hasParents() const
341{
342 return !mParents.empty();
343}
344
345void CommandGraphNode::setHasChildren()
346{
347 mHasChildren = true;
348}
349
350bool CommandGraphNode::hasChildren() const
351{
352 return mHasChildren;
353}
354
355// Do not call this in anything but testing code, since it's slow.
356bool CommandGraphNode::isChildOf(CommandGraphNode *parent)
357{
358 std::set<CommandGraphNode *> visitedList;
359 std::vector<CommandGraphNode *> openList;
360 openList.insert(openList.begin(), mParents.begin(), mParents.end());
361 while (!openList.empty())
362 {
363 CommandGraphNode *current = openList.back();
364 openList.pop_back();
365 if (visitedList.count(current) == 0)
366 {
367 if (current == parent)
368 {
369 return true;
370 }
371 visitedList.insert(current);
372 openList.insert(openList.end(), current->mParents.begin(), current->mParents.end());
373 }
374 }
375
376 return false;
377}
378
379VisitedState CommandGraphNode::visitedState() const
380{
381 return mVisitedState;
382}
383
384void CommandGraphNode::visitParents(std::vector<CommandGraphNode *> *stack)
385{
386 ASSERT(mVisitedState == VisitedState::Unvisited);
387 stack->insert(stack->end(), mParents.begin(), mParents.end());
388 mVisitedState = VisitedState::Ready;
389}
390
391Error CommandGraphNode::visitAndExecute(VkDevice device,
392 Serial serial,
393 RenderPassCache *renderPassCache,
394 CommandBuffer *primaryCommandBuffer)
395{
396 if (mOutsideRenderPassCommands.valid())
397 {
398 mOutsideRenderPassCommands.end();
399 primaryCommandBuffer->executeCommands(1, &mOutsideRenderPassCommands);
400 }
401
402 if (mInsideRenderPassCommands.valid())
403 {
404 // Pull a compatible RenderPass from the cache.
405 // TODO(jmadill): Insert real ops and layout transitions.
406 RenderPass *renderPass = nullptr;
407 ANGLE_TRY(
408 renderPassCache->getCompatibleRenderPass(device, serial, mRenderPassDesc, &renderPass));
409
410 mInsideRenderPassCommands.end();
411
412 VkRenderPassBeginInfo beginInfo;
413 beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
414 beginInfo.pNext = nullptr;
415 beginInfo.renderPass = renderPass->getHandle();
416 beginInfo.framebuffer = mRenderPassFramebuffer.getHandle();
417 beginInfo.renderArea.offset.x = static_cast<uint32_t>(mRenderPassRenderArea.x);
418 beginInfo.renderArea.offset.y = static_cast<uint32_t>(mRenderPassRenderArea.y);
419 beginInfo.renderArea.extent.width = static_cast<uint32_t>(mRenderPassRenderArea.width);
420 beginInfo.renderArea.extent.height = static_cast<uint32_t>(mRenderPassRenderArea.height);
421 beginInfo.clearValueCount = mRenderPassDesc.attachmentCount();
422 beginInfo.pClearValues = mRenderPassClearValues.data();
423
424 primaryCommandBuffer->beginRenderPass(beginInfo,
425 VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
426 primaryCommandBuffer->executeCommands(1, &mInsideRenderPassCommands);
427 primaryCommandBuffer->endRenderPass();
428 }
429
430 mVisitedState = VisitedState::Visited;
431 return NoError();
432}
433
Luc Ferron14f48172018-04-11 08:43:28 -0400434const gl::Rectangle &CommandGraphNode::getRenderPassRenderArea() const
435{
436 return mRenderPassRenderArea;
437}
438
Jamie Madill1f46bc12018-02-20 16:09:43 -0500439// CommandGraph implementation.
Jamie Madill316c6062018-05-29 10:49:45 -0400440CommandGraph::CommandGraph() = default;
Jamie Madill1f46bc12018-02-20 16:09:43 -0500441
442CommandGraph::~CommandGraph()
443{
444 ASSERT(empty());
445}
446
447CommandGraphNode *CommandGraph::allocateNode()
448{
449 // TODO(jmadill): Use a pool allocator for the CPU node allocations.
450 CommandGraphNode *newCommands = new CommandGraphNode();
451 mNodes.emplace_back(newCommands);
452 return newCommands;
453}
454
455Error CommandGraph::submitCommands(VkDevice device,
456 Serial serial,
457 RenderPassCache *renderPassCache,
458 CommandPool *commandPool,
459 CommandBuffer *primaryCommandBufferOut)
460{
461 VkCommandBufferAllocateInfo primaryInfo;
462 primaryInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
463 primaryInfo.pNext = nullptr;
464 primaryInfo.commandPool = commandPool->getHandle();
465 primaryInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
466 primaryInfo.commandBufferCount = 1;
467
468 ANGLE_TRY(primaryCommandBufferOut->init(device, primaryInfo));
469
470 if (mNodes.empty())
471 {
472 return NoError();
473 }
474
475 std::vector<CommandGraphNode *> nodeStack;
476
477 VkCommandBufferBeginInfo beginInfo;
478 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
479 beginInfo.pNext = nullptr;
480 beginInfo.flags = 0;
481 beginInfo.pInheritanceInfo = nullptr;
482
483 ANGLE_TRY(primaryCommandBufferOut->begin(beginInfo));
484
485 for (CommandGraphNode *topLevelNode : mNodes)
486 {
487 // Only process commands that don't have child commands. The others will be pulled in
488 // automatically. Also skip commands that have already been visited.
489 if (topLevelNode->hasChildren() || topLevelNode->visitedState() != VisitedState::Unvisited)
490 continue;
491
492 nodeStack.push_back(topLevelNode);
493
494 while (!nodeStack.empty())
495 {
496 CommandGraphNode *node = nodeStack.back();
497
498 switch (node->visitedState())
499 {
500 case VisitedState::Unvisited:
501 node->visitParents(&nodeStack);
502 break;
503 case VisitedState::Ready:
504 ANGLE_TRY(node->visitAndExecute(device, serial, renderPassCache,
505 primaryCommandBufferOut));
506 nodeStack.pop_back();
507 break;
508 case VisitedState::Visited:
509 nodeStack.pop_back();
510 break;
511 default:
512 UNREACHABLE();
513 break;
514 }
515 }
516 }
517
518 ANGLE_TRY(primaryCommandBufferOut->end());
519
520 // TODO(jmadill): Use pool allocation so we don't need to deallocate command graph.
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400521 for (CommandGraphNode *node : mNodes)
Jamie Madill1f46bc12018-02-20 16:09:43 -0500522 {
523 delete node;
524 }
525 mNodes.clear();
526
527 return NoError();
528}
529
530bool CommandGraph::empty() const
531{
532 return mNodes.empty();
533}
534
535} // namespace vk
536} // namespace rx