blob: ee4403a392028f930d116ea78d974ebe2b34d855 [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
60CommandGraphResource::~CommandGraphResource()
61{
62}
63
64void CommandGraphResource::updateQueueSerial(Serial queueSerial)
65{
66 ASSERT(queueSerial >= mStoredQueueSerial);
67
68 if (queueSerial > mStoredQueueSerial)
69 {
70 mCurrentWritingNode = nullptr;
71 mCurrentReadingNodes.clear();
72 mStoredQueueSerial = queueSerial;
73 }
74}
75
76Serial CommandGraphResource::getQueueSerial() const
77{
78 return mStoredQueueSerial;
79}
80
Jamie Madill9cceac42018-03-31 14:19:16 -040081bool CommandGraphResource::hasChildlessWritingNode() const
Jamie Madill6c7ab7f2018-03-31 14:19:15 -040082{
Jamie Madill9cceac42018-03-31 14:19:16 -040083 return (mCurrentWritingNode != nullptr && !mCurrentWritingNode->hasChildren());
Jamie Madill6c7ab7f2018-03-31 14:19:15 -040084}
85
Jamie Madill9cceac42018-03-31 14:19:16 -040086CommandGraphNode *CommandGraphResource::getCurrentWritingNode()
Jamie Madill6c7ab7f2018-03-31 14:19:15 -040087{
Jamie Madill6c7ab7f2018-03-31 14:19:15 -040088 return mCurrentWritingNode;
89}
90
91CommandGraphNode *CommandGraphResource::getNewWritingNode(RendererVk *renderer)
92{
93 CommandGraphNode *newCommands = renderer->allocateCommandNode();
94 onWriteResource(newCommands, renderer->getCurrentQueueSerial());
95 return newCommands;
96}
97
98Error CommandGraphResource::beginWriteResource(RendererVk *renderer,
99 CommandBuffer **commandBufferOut)
100{
101 CommandGraphNode *commands = getNewWritingNode(renderer);
102
103 VkDevice device = renderer->getDevice();
104 ANGLE_TRY(commands->beginOutsideRenderPassRecording(device, renderer->getCommandPool(),
105 commandBufferOut));
106 return NoError();
107}
108
109void CommandGraphResource::onWriteResource(CommandGraphNode *writingNode, Serial serial)
110{
111 updateQueueSerial(serial);
112
Jamie Madill9cceac42018-03-31 14:19:16 -0400113 // Make sure any open reads and writes finish before we execute 'writingNode'.
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400114 if (!mCurrentReadingNodes.empty())
115 {
116 CommandGraphNode::SetHappensBeforeDependencies(mCurrentReadingNodes, writingNode);
117 mCurrentReadingNodes.clear();
118 }
119
120 if (mCurrentWritingNode && mCurrentWritingNode != writingNode)
121 {
122 CommandGraphNode::SetHappensBeforeDependency(mCurrentWritingNode, writingNode);
123 }
124
125 mCurrentWritingNode = writingNode;
126}
127
128void CommandGraphResource::onReadResource(CommandGraphNode *readingNode, Serial serial)
129{
Jamie Madill9cceac42018-03-31 14:19:16 -0400130 updateQueueSerial(serial);
131
132 if (hasChildlessWritingNode())
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400133 {
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400134 ASSERT(mStoredQueueSerial == serial);
Jamie Madill9cceac42018-03-31 14:19:16 -0400135
136 // Ensure 'readingNode' happens after the current writing node.
137 CommandGraphNode::SetHappensBeforeDependency(mCurrentWritingNode, readingNode);
138 }
139
140 // Add the read node to the list of nodes currently reading this resource.
141 mCurrentReadingNodes.push_back(readingNode);
142}
143
144bool CommandGraphResource::checkResourceInUseAndRefreshDeps(RendererVk *renderer)
145{
Jamie Madill26084d02018-04-09 13:44:04 -0400146 if (!renderer->isResourceInUse(*this) ||
147 (renderer->getCurrentQueueSerial() > mStoredQueueSerial))
Jamie Madill9cceac42018-03-31 14:19:16 -0400148 {
149 mCurrentReadingNodes.clear();
150 mCurrentWritingNode = nullptr;
151 return false;
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400152 }
153 else
154 {
Jamie Madill9cceac42018-03-31 14:19:16 -0400155 return true;
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400156 }
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400157}
158
Jamie Madill1f46bc12018-02-20 16:09:43 -0500159// CommandGraphNode implementation.
160
161CommandGraphNode::CommandGraphNode() : mHasChildren(false), mVisitedState(VisitedState::Unvisited)
162{
163}
164
165CommandGraphNode::~CommandGraphNode()
166{
167 mRenderPassFramebuffer.setHandle(VK_NULL_HANDLE);
168
169 // Command buffers are managed by the command pool, so don't need to be freed.
170 mOutsideRenderPassCommands.releaseHandle();
171 mInsideRenderPassCommands.releaseHandle();
172}
173
174CommandBuffer *CommandGraphNode::getOutsideRenderPassCommands()
175{
176 ASSERT(!mHasChildren);
177 return &mOutsideRenderPassCommands;
178}
179
180CommandBuffer *CommandGraphNode::getInsideRenderPassCommands()
181{
182 ASSERT(!mHasChildren);
183 return &mInsideRenderPassCommands;
184}
185
186Error CommandGraphNode::beginOutsideRenderPassRecording(VkDevice device,
187 const CommandPool &commandPool,
188 CommandBuffer **commandsOut)
189{
190 ASSERT(!mHasChildren);
191
192 VkCommandBufferInheritanceInfo inheritanceInfo;
193 inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
194 inheritanceInfo.pNext = nullptr;
195 inheritanceInfo.renderPass = VK_NULL_HANDLE;
196 inheritanceInfo.subpass = 0;
197 inheritanceInfo.framebuffer = VK_NULL_HANDLE;
198 inheritanceInfo.occlusionQueryEnable = VK_FALSE;
199 inheritanceInfo.queryFlags = 0;
200 inheritanceInfo.pipelineStatistics = 0;
201
202 ANGLE_TRY(InitAndBeginCommandBuffer(device, commandPool, inheritanceInfo, 0,
203 &mOutsideRenderPassCommands));
204
205 *commandsOut = &mOutsideRenderPassCommands;
206 return NoError();
207}
208
209Error CommandGraphNode::beginInsideRenderPassRecording(RendererVk *renderer,
210 CommandBuffer **commandsOut)
211{
212 ASSERT(!mHasChildren);
213
214 // Get a compatible RenderPass from the cache so we can initialize the inheritance info.
215 // TODO(jmadill): Support query for compatible/conformant render pass. htto://anglebug.com/2361
216 RenderPass *compatibleRenderPass;
217 ANGLE_TRY(renderer->getCompatibleRenderPass(mRenderPassDesc, &compatibleRenderPass));
218
219 VkCommandBufferInheritanceInfo inheritanceInfo;
220 inheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
221 inheritanceInfo.pNext = nullptr;
222 inheritanceInfo.renderPass = compatibleRenderPass->getHandle();
223 inheritanceInfo.subpass = 0;
224 inheritanceInfo.framebuffer = mRenderPassFramebuffer.getHandle();
225 inheritanceInfo.occlusionQueryEnable = VK_FALSE;
226 inheritanceInfo.queryFlags = 0;
227 inheritanceInfo.pipelineStatistics = 0;
228
229 ANGLE_TRY(InitAndBeginCommandBuffer(
230 renderer->getDevice(), renderer->getCommandPool(), inheritanceInfo,
231 VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, &mInsideRenderPassCommands));
232
233 *commandsOut = &mInsideRenderPassCommands;
234 return NoError();
235}
236
237void CommandGraphNode::storeRenderPassInfo(const Framebuffer &framebuffer,
238 const gl::Rectangle renderArea,
239 const std::vector<VkClearValue> &clearValues)
240{
241 mRenderPassFramebuffer.setHandle(framebuffer.getHandle());
242 mRenderPassRenderArea = renderArea;
243 std::copy(clearValues.begin(), clearValues.end(), mRenderPassClearValues.begin());
244}
245
246void CommandGraphNode::appendColorRenderTarget(Serial serial, RenderTargetVk *colorRenderTarget)
247{
Jamie Madill26084d02018-04-09 13:44:04 -0400248 ASSERT(mOutsideRenderPassCommands.valid());
249
250 // TODO(jmadill): Use automatic layout transition. http://anglebug.com/2361
251 colorRenderTarget->image->changeLayoutWithStages(
252 VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
253 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
254 &mOutsideRenderPassCommands);
255
Jamie Madillbc543422018-03-30 10:43:19 -0400256 mRenderPassDesc.packColorAttachment(*colorRenderTarget->image);
Jamie Madill1f46bc12018-02-20 16:09:43 -0500257 colorRenderTarget->resource->onWriteResource(this, serial);
258}
259
260void CommandGraphNode::appendDepthStencilRenderTarget(Serial serial,
261 RenderTargetVk *depthStencilRenderTarget)
262{
Jamie Madill26084d02018-04-09 13:44:04 -0400263 ASSERT(mOutsideRenderPassCommands.valid());
264 ASSERT(depthStencilRenderTarget->image->getFormat().textureFormat().hasDepthOrStencilBits());
265
266 // TODO(jmadill): Use automatic layout transition. http://anglebug.com/2361
267 const angle::Format &format = depthStencilRenderTarget->image->getFormat().textureFormat();
268 VkImageAspectFlags aspectFlags = (format.depthBits > 0 ? VK_IMAGE_ASPECT_DEPTH_BIT : 0) |
269 (format.stencilBits > 0 ? VK_IMAGE_ASPECT_STENCIL_BIT : 0);
270
271 depthStencilRenderTarget->image->changeLayoutWithStages(
272 aspectFlags, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
273 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
274 &mOutsideRenderPassCommands);
275
Jamie Madillbc543422018-03-30 10:43:19 -0400276 mRenderPassDesc.packDepthStencilAttachment(*depthStencilRenderTarget->image);
Jamie Madill1f46bc12018-02-20 16:09:43 -0500277 depthStencilRenderTarget->resource->onWriteResource(this, serial);
278}
279
280// static
281void CommandGraphNode::SetHappensBeforeDependency(CommandGraphNode *beforeNode,
282 CommandGraphNode *afterNode)
283{
Jamie Madill9cceac42018-03-31 14:19:16 -0400284 ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
Jamie Madill1f46bc12018-02-20 16:09:43 -0500285 afterNode->mParents.emplace_back(beforeNode);
286 beforeNode->setHasChildren();
Jamie Madill1f46bc12018-02-20 16:09:43 -0500287}
288
289// static
290void CommandGraphNode::SetHappensBeforeDependencies(
291 const std::vector<CommandGraphNode *> &beforeNodes,
292 CommandGraphNode *afterNode)
293{
294 afterNode->mParents.insert(afterNode->mParents.end(), beforeNodes.begin(), beforeNodes.end());
295
296 // TODO(jmadill): is there a faster way to do this?
297 for (CommandGraphNode *beforeNode : beforeNodes)
298 {
299 beforeNode->setHasChildren();
300
301 ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
302 }
303}
304
305bool CommandGraphNode::hasParents() const
306{
307 return !mParents.empty();
308}
309
310void CommandGraphNode::setHasChildren()
311{
312 mHasChildren = true;
313}
314
315bool CommandGraphNode::hasChildren() const
316{
317 return mHasChildren;
318}
319
320// Do not call this in anything but testing code, since it's slow.
321bool CommandGraphNode::isChildOf(CommandGraphNode *parent)
322{
323 std::set<CommandGraphNode *> visitedList;
324 std::vector<CommandGraphNode *> openList;
325 openList.insert(openList.begin(), mParents.begin(), mParents.end());
326 while (!openList.empty())
327 {
328 CommandGraphNode *current = openList.back();
329 openList.pop_back();
330 if (visitedList.count(current) == 0)
331 {
332 if (current == parent)
333 {
334 return true;
335 }
336 visitedList.insert(current);
337 openList.insert(openList.end(), current->mParents.begin(), current->mParents.end());
338 }
339 }
340
341 return false;
342}
343
344VisitedState CommandGraphNode::visitedState() const
345{
346 return mVisitedState;
347}
348
349void CommandGraphNode::visitParents(std::vector<CommandGraphNode *> *stack)
350{
351 ASSERT(mVisitedState == VisitedState::Unvisited);
352 stack->insert(stack->end(), mParents.begin(), mParents.end());
353 mVisitedState = VisitedState::Ready;
354}
355
356Error CommandGraphNode::visitAndExecute(VkDevice device,
357 Serial serial,
358 RenderPassCache *renderPassCache,
359 CommandBuffer *primaryCommandBuffer)
360{
361 if (mOutsideRenderPassCommands.valid())
362 {
363 mOutsideRenderPassCommands.end();
364 primaryCommandBuffer->executeCommands(1, &mOutsideRenderPassCommands);
365 }
366
367 if (mInsideRenderPassCommands.valid())
368 {
369 // Pull a compatible RenderPass from the cache.
370 // TODO(jmadill): Insert real ops and layout transitions.
371 RenderPass *renderPass = nullptr;
372 ANGLE_TRY(
373 renderPassCache->getCompatibleRenderPass(device, serial, mRenderPassDesc, &renderPass));
374
375 mInsideRenderPassCommands.end();
376
377 VkRenderPassBeginInfo beginInfo;
378 beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
379 beginInfo.pNext = nullptr;
380 beginInfo.renderPass = renderPass->getHandle();
381 beginInfo.framebuffer = mRenderPassFramebuffer.getHandle();
382 beginInfo.renderArea.offset.x = static_cast<uint32_t>(mRenderPassRenderArea.x);
383 beginInfo.renderArea.offset.y = static_cast<uint32_t>(mRenderPassRenderArea.y);
384 beginInfo.renderArea.extent.width = static_cast<uint32_t>(mRenderPassRenderArea.width);
385 beginInfo.renderArea.extent.height = static_cast<uint32_t>(mRenderPassRenderArea.height);
386 beginInfo.clearValueCount = mRenderPassDesc.attachmentCount();
387 beginInfo.pClearValues = mRenderPassClearValues.data();
388
389 primaryCommandBuffer->beginRenderPass(beginInfo,
390 VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
391 primaryCommandBuffer->executeCommands(1, &mInsideRenderPassCommands);
392 primaryCommandBuffer->endRenderPass();
393 }
394
395 mVisitedState = VisitedState::Visited;
396 return NoError();
397}
398
399// CommandGraph implementation.
400CommandGraph::CommandGraph()
401{
402}
403
404CommandGraph::~CommandGraph()
405{
406 ASSERT(empty());
407}
408
409CommandGraphNode *CommandGraph::allocateNode()
410{
411 // TODO(jmadill): Use a pool allocator for the CPU node allocations.
412 CommandGraphNode *newCommands = new CommandGraphNode();
413 mNodes.emplace_back(newCommands);
414 return newCommands;
415}
416
417Error CommandGraph::submitCommands(VkDevice device,
418 Serial serial,
419 RenderPassCache *renderPassCache,
420 CommandPool *commandPool,
421 CommandBuffer *primaryCommandBufferOut)
422{
423 VkCommandBufferAllocateInfo primaryInfo;
424 primaryInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
425 primaryInfo.pNext = nullptr;
426 primaryInfo.commandPool = commandPool->getHandle();
427 primaryInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
428 primaryInfo.commandBufferCount = 1;
429
430 ANGLE_TRY(primaryCommandBufferOut->init(device, primaryInfo));
431
432 if (mNodes.empty())
433 {
434 return NoError();
435 }
436
437 std::vector<CommandGraphNode *> nodeStack;
438
439 VkCommandBufferBeginInfo beginInfo;
440 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
441 beginInfo.pNext = nullptr;
442 beginInfo.flags = 0;
443 beginInfo.pInheritanceInfo = nullptr;
444
445 ANGLE_TRY(primaryCommandBufferOut->begin(beginInfo));
446
447 for (CommandGraphNode *topLevelNode : mNodes)
448 {
449 // Only process commands that don't have child commands. The others will be pulled in
450 // automatically. Also skip commands that have already been visited.
451 if (topLevelNode->hasChildren() || topLevelNode->visitedState() != VisitedState::Unvisited)
452 continue;
453
454 nodeStack.push_back(topLevelNode);
455
456 while (!nodeStack.empty())
457 {
458 CommandGraphNode *node = nodeStack.back();
459
460 switch (node->visitedState())
461 {
462 case VisitedState::Unvisited:
463 node->visitParents(&nodeStack);
464 break;
465 case VisitedState::Ready:
466 ANGLE_TRY(node->visitAndExecute(device, serial, renderPassCache,
467 primaryCommandBufferOut));
468 nodeStack.pop_back();
469 break;
470 case VisitedState::Visited:
471 nodeStack.pop_back();
472 break;
473 default:
474 UNREACHABLE();
475 break;
476 }
477 }
478 }
479
480 ANGLE_TRY(primaryCommandBufferOut->end());
481
482 // TODO(jmadill): Use pool allocation so we don't need to deallocate command graph.
Jamie Madill6c7ab7f2018-03-31 14:19:15 -0400483 for (CommandGraphNode *node : mNodes)
Jamie Madill1f46bc12018-02-20 16:09:43 -0500484 {
485 delete node;
486 }
487 mNodes.clear();
488
489 return NoError();
490}
491
492bool CommandGraph::empty() const
493{
494 return mNodes.empty();
495}
496
497} // namespace vk
498} // namespace rx