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