blob: 9643c1f9c1a1ba8d86d720f4fd85ba757b97aa37 [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{
143 // TODO(jmadill): Layout transition?
144 mRenderPassDesc.packColorAttachment(*colorRenderTarget->format, colorRenderTarget->samples);
145 colorRenderTarget->resource->onWriteResource(this, serial);
146}
147
148void CommandGraphNode::appendDepthStencilRenderTarget(Serial serial,
149 RenderTargetVk *depthStencilRenderTarget)
150{
151 // TODO(jmadill): Layout transition?
152 mRenderPassDesc.packDepthStencilAttachment(*depthStencilRenderTarget->format,
153 depthStencilRenderTarget->samples);
154 depthStencilRenderTarget->resource->onWriteResource(this, serial);
155}
156
157// static
158void CommandGraphNode::SetHappensBeforeDependency(CommandGraphNode *beforeNode,
159 CommandGraphNode *afterNode)
160{
161 afterNode->mParents.emplace_back(beforeNode);
162 beforeNode->setHasChildren();
163 ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
164}
165
166// static
167void CommandGraphNode::SetHappensBeforeDependencies(
168 const std::vector<CommandGraphNode *> &beforeNodes,
169 CommandGraphNode *afterNode)
170{
171 afterNode->mParents.insert(afterNode->mParents.end(), beforeNodes.begin(), beforeNodes.end());
172
173 // TODO(jmadill): is there a faster way to do this?
174 for (CommandGraphNode *beforeNode : beforeNodes)
175 {
176 beforeNode->setHasChildren();
177
178 ASSERT(beforeNode != afterNode && !beforeNode->isChildOf(afterNode));
179 }
180}
181
182bool CommandGraphNode::hasParents() const
183{
184 return !mParents.empty();
185}
186
187void CommandGraphNode::setHasChildren()
188{
189 mHasChildren = true;
190}
191
192bool CommandGraphNode::hasChildren() const
193{
194 return mHasChildren;
195}
196
197// Do not call this in anything but testing code, since it's slow.
198bool CommandGraphNode::isChildOf(CommandGraphNode *parent)
199{
200 std::set<CommandGraphNode *> visitedList;
201 std::vector<CommandGraphNode *> openList;
202 openList.insert(openList.begin(), mParents.begin(), mParents.end());
203 while (!openList.empty())
204 {
205 CommandGraphNode *current = openList.back();
206 openList.pop_back();
207 if (visitedList.count(current) == 0)
208 {
209 if (current == parent)
210 {
211 return true;
212 }
213 visitedList.insert(current);
214 openList.insert(openList.end(), current->mParents.begin(), current->mParents.end());
215 }
216 }
217
218 return false;
219}
220
221VisitedState CommandGraphNode::visitedState() const
222{
223 return mVisitedState;
224}
225
226void CommandGraphNode::visitParents(std::vector<CommandGraphNode *> *stack)
227{
228 ASSERT(mVisitedState == VisitedState::Unvisited);
229 stack->insert(stack->end(), mParents.begin(), mParents.end());
230 mVisitedState = VisitedState::Ready;
231}
232
233Error CommandGraphNode::visitAndExecute(VkDevice device,
234 Serial serial,
235 RenderPassCache *renderPassCache,
236 CommandBuffer *primaryCommandBuffer)
237{
238 if (mOutsideRenderPassCommands.valid())
239 {
240 mOutsideRenderPassCommands.end();
241 primaryCommandBuffer->executeCommands(1, &mOutsideRenderPassCommands);
242 }
243
244 if (mInsideRenderPassCommands.valid())
245 {
246 // Pull a compatible RenderPass from the cache.
247 // TODO(jmadill): Insert real ops and layout transitions.
248 RenderPass *renderPass = nullptr;
249 ANGLE_TRY(
250 renderPassCache->getCompatibleRenderPass(device, serial, mRenderPassDesc, &renderPass));
251
252 mInsideRenderPassCommands.end();
253
254 VkRenderPassBeginInfo beginInfo;
255 beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
256 beginInfo.pNext = nullptr;
257 beginInfo.renderPass = renderPass->getHandle();
258 beginInfo.framebuffer = mRenderPassFramebuffer.getHandle();
259 beginInfo.renderArea.offset.x = static_cast<uint32_t>(mRenderPassRenderArea.x);
260 beginInfo.renderArea.offset.y = static_cast<uint32_t>(mRenderPassRenderArea.y);
261 beginInfo.renderArea.extent.width = static_cast<uint32_t>(mRenderPassRenderArea.width);
262 beginInfo.renderArea.extent.height = static_cast<uint32_t>(mRenderPassRenderArea.height);
263 beginInfo.clearValueCount = mRenderPassDesc.attachmentCount();
264 beginInfo.pClearValues = mRenderPassClearValues.data();
265
266 primaryCommandBuffer->beginRenderPass(beginInfo,
267 VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
268 primaryCommandBuffer->executeCommands(1, &mInsideRenderPassCommands);
269 primaryCommandBuffer->endRenderPass();
270 }
271
272 mVisitedState = VisitedState::Visited;
273 return NoError();
274}
275
276// CommandGraph implementation.
277CommandGraph::CommandGraph()
278{
279}
280
281CommandGraph::~CommandGraph()
282{
283 ASSERT(empty());
284}
285
286CommandGraphNode *CommandGraph::allocateNode()
287{
288 // TODO(jmadill): Use a pool allocator for the CPU node allocations.
289 CommandGraphNode *newCommands = new CommandGraphNode();
290 mNodes.emplace_back(newCommands);
291 return newCommands;
292}
293
294Error CommandGraph::submitCommands(VkDevice device,
295 Serial serial,
296 RenderPassCache *renderPassCache,
297 CommandPool *commandPool,
298 CommandBuffer *primaryCommandBufferOut)
299{
300 VkCommandBufferAllocateInfo primaryInfo;
301 primaryInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
302 primaryInfo.pNext = nullptr;
303 primaryInfo.commandPool = commandPool->getHandle();
304 primaryInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
305 primaryInfo.commandBufferCount = 1;
306
307 ANGLE_TRY(primaryCommandBufferOut->init(device, primaryInfo));
308
309 if (mNodes.empty())
310 {
311 return NoError();
312 }
313
314 std::vector<CommandGraphNode *> nodeStack;
315
316 VkCommandBufferBeginInfo beginInfo;
317 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
318 beginInfo.pNext = nullptr;
319 beginInfo.flags = 0;
320 beginInfo.pInheritanceInfo = nullptr;
321
322 ANGLE_TRY(primaryCommandBufferOut->begin(beginInfo));
323
324 for (CommandGraphNode *topLevelNode : mNodes)
325 {
326 // Only process commands that don't have child commands. The others will be pulled in
327 // automatically. Also skip commands that have already been visited.
328 if (topLevelNode->hasChildren() || topLevelNode->visitedState() != VisitedState::Unvisited)
329 continue;
330
331 nodeStack.push_back(topLevelNode);
332
333 while (!nodeStack.empty())
334 {
335 CommandGraphNode *node = nodeStack.back();
336
337 switch (node->visitedState())
338 {
339 case VisitedState::Unvisited:
340 node->visitParents(&nodeStack);
341 break;
342 case VisitedState::Ready:
343 ANGLE_TRY(node->visitAndExecute(device, serial, renderPassCache,
344 primaryCommandBufferOut));
345 nodeStack.pop_back();
346 break;
347 case VisitedState::Visited:
348 nodeStack.pop_back();
349 break;
350 default:
351 UNREACHABLE();
352 break;
353 }
354 }
355 }
356
357 ANGLE_TRY(primaryCommandBufferOut->end());
358
359 // TODO(jmadill): Use pool allocation so we don't need to deallocate command graph.
360 for (vk::CommandGraphNode *node : mNodes)
361 {
362 delete node;
363 }
364 mNodes.clear();
365
366 return NoError();
367}
368
369bool CommandGraph::empty() const
370{
371 return mNodes.empty();
372}
373
374} // namespace vk
375} // namespace rx