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