blob: 07a64e5df36d13b6bb031c285f0f54cdaad55c4b [file] [log] [blame]
Jeremy Gebben610d3a62022-01-01 12:53:17 -07001/* Copyright (c) 2015-2022 The Khronos Group Inc.
2 * Copyright (c) 2015-2022 Valve Corporation
3 * Copyright (c) 2015-2022 LunarG, Inc.
4 * Copyright (C) 2015-2022 Google Inc.
Jeremy Gebben88f58142021-06-01 10:07:52 -06005 * Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * Author: Courtney Goeltzenleuchter <courtneygo@google.com>
20 * Author: Tobin Ehlis <tobine@google.com>
21 * Author: Chris Forbes <chrisf@ijw.co.nz>
22 * Author: Mark Lobodzinski <mark@lunarg.com>
23 * Author: Dave Houlton <daveh@lunarg.com>
24 * Author: John Zulauf <jzulauf@lunarg.com>
25 * Author: Tobias Hector <tobias.hector@amd.com>
26 * Author: Jeremy Gebben <jeremyg@lunarg.com>
27 */
ziga-lunarg9ad42712022-08-21 03:01:29 +020028
29#include <bitset>
30
Jeremy Gebben88f58142021-06-01 10:07:52 -060031#include "render_pass_state.h"
32#include "convert_to_renderpass2.h"
33#include "image_state.h"
34
35static const VkImageLayout kInvalidLayout = VK_IMAGE_LAYOUT_MAX_ENUM;
36
John Zulaufbdcc9c62021-10-01 14:57:26 -060037static VkSubpassDependency2 ImplicitDependencyFromExternal(uint32_t subpass) {
38 VkSubpassDependency2 from_external = {VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
39 nullptr,
40 VK_SUBPASS_EXTERNAL,
41 subpass,
42 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
43 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
44 0,
45 VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
46 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
47 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
48 0,
49 0};
50 return from_external;
51}
52
53static VkSubpassDependency2 ImplicitDependencyToExternal(uint32_t subpass) {
54 VkSubpassDependency2 to_external = {VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
55 nullptr,
56 subpass,
57 VK_SUBPASS_EXTERNAL,
58 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
59 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
60 VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
61 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
62 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
63 0,
64 0,
65 0};
66 return to_external;
67}
Jeremy Gebbene83bd442021-09-02 11:18:03 -060068// NOTE: The functions below are only called from the RENDER_PASS_STATE constructor, and use const_cast<> to set up
69// members that never change after construction is finished.
Jeremy Gebben88f58142021-06-01 10:07:52 -060070static void RecordRenderPassDAG(const VkRenderPassCreateInfo2 *pCreateInfo, RENDER_PASS_STATE *render_pass) {
Jeremy Gebbene83bd442021-09-02 11:18:03 -060071 auto &subpass_to_node = const_cast<RENDER_PASS_STATE::DAGNodeVec &>(render_pass->subpass_to_node);
Jeremy Gebben88f58142021-06-01 10:07:52 -060072 subpass_to_node.resize(pCreateInfo->subpassCount);
Jeremy Gebbene83bd442021-09-02 11:18:03 -060073 auto &self_dependencies = const_cast<RENDER_PASS_STATE::SelfDepVec &>(render_pass->self_dependencies);
Jeremy Gebben88f58142021-06-01 10:07:52 -060074 self_dependencies.resize(pCreateInfo->subpassCount);
Jeremy Gebbene83bd442021-09-02 11:18:03 -060075 auto &subpass_dependencies = const_cast<RENDER_PASS_STATE::SubpassGraphVec &>(render_pass->subpass_dependencies);
Jeremy Gebben88f58142021-06-01 10:07:52 -060076 subpass_dependencies.resize(pCreateInfo->subpassCount);
77
78 for (uint32_t i = 0; i < pCreateInfo->subpassCount; ++i) {
79 subpass_to_node[i].pass = i;
80 self_dependencies[i].clear();
81 subpass_dependencies[i].pass = i;
82 }
83 for (uint32_t i = 0; i < pCreateInfo->dependencyCount; ++i) {
84 const auto &dependency = pCreateInfo->pDependencies[i];
85 const auto src_subpass = dependency.srcSubpass;
86 const auto dst_subpass = dependency.dstSubpass;
87 if ((dependency.srcSubpass != VK_SUBPASS_EXTERNAL) && (dependency.dstSubpass != VK_SUBPASS_EXTERNAL)) {
88 if (dependency.srcSubpass == dependency.dstSubpass) {
89 self_dependencies[dependency.srcSubpass].push_back(i);
90 } else {
91 subpass_to_node[dependency.dstSubpass].prev.push_back(dependency.srcSubpass);
92 subpass_to_node[dependency.srcSubpass].next.push_back(dependency.dstSubpass);
93 }
94 }
95 if (src_subpass == VK_SUBPASS_EXTERNAL) {
96 assert(dst_subpass != VK_SUBPASS_EXTERNAL); // this is invalid per VUID-VkSubpassDependency-srcSubpass-00865
97 subpass_dependencies[dst_subpass].barrier_from_external.emplace_back(&dependency);
98 } else if (dst_subpass == VK_SUBPASS_EXTERNAL) {
99 subpass_dependencies[src_subpass].barrier_to_external.emplace_back(&dependency);
100 } else if (dependency.srcSubpass != dependency.dstSubpass) {
101 // ignore self dependencies in prev and next
102 subpass_dependencies[src_subpass].next[&subpass_dependencies[dst_subpass]].emplace_back(&dependency);
103 subpass_dependencies[dst_subpass].prev[&subpass_dependencies[src_subpass]].emplace_back(&dependency);
104 }
105 }
106
John Zulaufbdcc9c62021-10-01 14:57:26 -0600107 // If no barriers to external are provided for a given subpass, add them.
108 for (auto &subpass_dep : subpass_dependencies) {
109 const uint32_t pass = subpass_dep.pass;
110 if (subpass_dep.barrier_from_external.size() == 0) {
111 // Add implicit from barrier if they're aren't any
112 subpass_dep.implicit_barrier_from_external =
113 layer_data::make_unique<VkSubpassDependency2>(ImplicitDependencyFromExternal(pass));
114 subpass_dep.barrier_from_external.emplace_back(subpass_dep.implicit_barrier_from_external.get());
115 }
116 if (subpass_dep.barrier_to_external.size() == 0) {
117 // Add implicit to barrier if they're aren't any
118 subpass_dep.implicit_barrier_to_external =
119 layer_data::make_unique<VkSubpassDependency2>(ImplicitDependencyToExternal(pass));
120 subpass_dep.barrier_to_external.emplace_back(subpass_dep.implicit_barrier_to_external.get());
121 }
122 }
123
Jeremy Gebben88f58142021-06-01 10:07:52 -0600124 //
125 // Determine "asynchrononous" subpassess
126 // syncronization is only interested in asyncronous stages *earlier* that the current one... so we'll only look towards those.
127 // NOTE: This is O(N^3), which we could shrink to O(N^2logN) using sets instead of arrays, but given that N is likely to be
128 // small and the K for |= from the prev is must less than for set, we'll accept the brute force.
129 std::vector<std::vector<bool>> pass_depends(pCreateInfo->subpassCount);
130 for (uint32_t i = 1; i < pCreateInfo->subpassCount; ++i) {
131 auto &depends = pass_depends[i];
132 depends.resize(i);
133 auto &subpass_dep = subpass_dependencies[i];
134 for (const auto &prev : subpass_dep.prev) {
135 const auto prev_pass = prev.first->pass;
136 const auto &prev_depends = pass_depends[prev_pass];
137 for (uint32_t j = 0; j < prev_pass; j++) {
Nico Weber51279392021-10-08 11:19:55 -0400138 depends[j] = depends[j] || prev_depends[j];
Jeremy Gebben88f58142021-06-01 10:07:52 -0600139 }
140 depends[prev_pass] = true;
141 }
142 for (uint32_t pass = 0; pass < subpass_dep.pass; pass++) {
143 if (!depends[pass]) {
144 subpass_dep.async.push_back(pass);
145 }
146 }
147 }
148}
149
Jeremy Gebben88f58142021-06-01 10:07:52 -0600150struct AttachmentTracker { // This is really only of local interest, but a bit big for a lambda
151 RENDER_PASS_STATE *const rp;
Jeremy Gebbene83bd442021-09-02 11:18:03 -0600152 RENDER_PASS_STATE::SubpassVec &first;
153 RENDER_PASS_STATE::FirstIsTransitionVec &first_is_transition;
154 RENDER_PASS_STATE::SubpassVec &last;
155 RENDER_PASS_STATE::TransitionVec &subpass_transitions;
156 RENDER_PASS_STATE::FirstReadMap &first_read;
Jeremy Gebben88f58142021-06-01 10:07:52 -0600157 const uint32_t attachment_count;
158 std::vector<VkImageLayout> attachment_layout;
159 std::vector<std::vector<VkImageLayout>> subpass_attachment_layout;
160 explicit AttachmentTracker(RENDER_PASS_STATE *render_pass)
161 : rp(render_pass),
Jeremy Gebbene83bd442021-09-02 11:18:03 -0600162 first(const_cast<RENDER_PASS_STATE::SubpassVec &>(rp->attachment_first_subpass)),
163 first_is_transition(const_cast<RENDER_PASS_STATE::FirstIsTransitionVec &>(rp->attachment_first_is_transition)),
164 last(const_cast<RENDER_PASS_STATE::SubpassVec &>(rp->attachment_last_subpass)),
165 subpass_transitions(const_cast<RENDER_PASS_STATE::TransitionVec &>(rp->subpass_transitions)),
166 first_read(const_cast<RENDER_PASS_STATE::FirstReadMap &>(rp->attachment_first_read)),
Jeremy Gebben88f58142021-06-01 10:07:52 -0600167 attachment_count(rp->createInfo.attachmentCount),
168 attachment_layout(),
169 subpass_attachment_layout() {
170 first.resize(attachment_count, VK_SUBPASS_EXTERNAL);
171 first_is_transition.resize(attachment_count, false);
172 last.resize(attachment_count, VK_SUBPASS_EXTERNAL);
173 subpass_transitions.resize(rp->createInfo.subpassCount + 1); // Add an extra for EndRenderPass
174 attachment_layout.reserve(attachment_count);
175 subpass_attachment_layout.resize(rp->createInfo.subpassCount);
176 for (auto &subpass_layouts : subpass_attachment_layout) {
177 subpass_layouts.resize(attachment_count, kInvalidLayout);
178 }
179
180 for (uint32_t j = 0; j < attachment_count; j++) {
181 attachment_layout.push_back(rp->createInfo.pAttachments[j].initialLayout);
182 }
183 }
184
185 void Update(uint32_t subpass, const VkAttachmentReference2 *attach_ref, uint32_t count, bool is_read) {
186 if (nullptr == attach_ref) return;
187 for (uint32_t j = 0; j < count; ++j) {
188 const auto attachment = attach_ref[j].attachment;
189 if (attachment != VK_ATTACHMENT_UNUSED) {
190 const auto layout = attach_ref[j].layout;
191 // Take advantage of the fact that insert won't overwrite, so we'll only write the first time.
192 first_read.emplace(attachment, is_read);
193 if (first[attachment] == VK_SUBPASS_EXTERNAL) {
194 first[attachment] = subpass;
195 const auto initial_layout = rp->createInfo.pAttachments[attachment].initialLayout;
196 if (initial_layout != layout) {
197 subpass_transitions[subpass].emplace_back(VK_SUBPASS_EXTERNAL, attachment, initial_layout, layout);
198 first_is_transition[attachment] = true;
199 }
200 }
201 last[attachment] = subpass;
202
203 for (const auto &prev : rp->subpass_dependencies[subpass].prev) {
204 const auto prev_pass = prev.first->pass;
205 const auto prev_layout = subpass_attachment_layout[prev_pass][attachment];
206 if ((prev_layout != kInvalidLayout) && (prev_layout != layout)) {
207 subpass_transitions[subpass].emplace_back(prev_pass, attachment, prev_layout, layout);
208 }
209 }
210 attachment_layout[attachment] = layout;
211 }
212 }
213 }
214 void FinalTransitions() {
215 auto &final_transitions = subpass_transitions[rp->createInfo.subpassCount];
216
217 for (uint32_t attachment = 0; attachment < attachment_count; ++attachment) {
218 const auto final_layout = rp->createInfo.pAttachments[attachment].finalLayout;
219 // Add final transitions for attachments that were used and change layout.
220 if ((last[attachment] != VK_SUBPASS_EXTERNAL) && final_layout != attachment_layout[attachment]) {
221 final_transitions.emplace_back(last[attachment], attachment, attachment_layout[attachment], final_layout);
222 }
223 }
224 }
225};
226
227static void InitRenderPassState(RENDER_PASS_STATE *render_pass) {
228 auto create_info = render_pass->createInfo.ptr();
229
230 RecordRenderPassDAG(create_info, render_pass);
231
232 AttachmentTracker attachment_tracker(render_pass);
233
234 for (uint32_t subpass_index = 0; subpass_index < create_info->subpassCount; ++subpass_index) {
235 const VkSubpassDescription2 &subpass = create_info->pSubpasses[subpass_index];
236 attachment_tracker.Update(subpass_index, subpass.pColorAttachments, subpass.colorAttachmentCount, false);
237 attachment_tracker.Update(subpass_index, subpass.pResolveAttachments, subpass.colorAttachmentCount, false);
238 attachment_tracker.Update(subpass_index, subpass.pDepthStencilAttachment, 1, false);
239 attachment_tracker.Update(subpass_index, subpass.pInputAttachments, subpass.inputAttachmentCount, true);
aitor-lunarga131fca2022-02-17 22:55:55 +0100240
241 // From the spec
242 // If the VkSubpassDescription2::viewMask member of any element of pSubpasses is not zero, multiview functionality is
243 // considered to be enabled for this render pass.
244 (*const_cast<bool *>(&render_pass->has_multiview_enabled)) |= (subpass.viewMask != 0);
Jeremy Gebben88f58142021-06-01 10:07:52 -0600245 }
246 attachment_tracker.FinalTransitions();
Jeremy Gebben88f58142021-06-01 10:07:52 -0600247}
248
249RENDER_PASS_STATE::RENDER_PASS_STATE(VkRenderPass rp, VkRenderPassCreateInfo2 const *pCreateInfo)
aitor-lunarga131fca2022-02-17 22:55:55 +0100250 : BASE_NODE(rp, kVulkanObjectTypeRenderPass),
251 use_dynamic_rendering(false),
252 use_dynamic_rendering_inherited(false),
253 has_multiview_enabled(false),
254 createInfo(pCreateInfo) {
Jeremy Gebben88f58142021-06-01 10:07:52 -0600255 InitRenderPassState(this);
256}
257
Jeremy Gebbene83bd442021-09-02 11:18:03 -0600258static safe_VkRenderPassCreateInfo2 ConvertCreateInfo(const VkRenderPassCreateInfo &create_info) {
259 safe_VkRenderPassCreateInfo2 create_info_2;
260 ConvertVkRenderPassCreateInfoToV2KHR(create_info, &create_info_2);
261 return create_info_2;
262}
263
Jeremy Gebben88f58142021-06-01 10:07:52 -0600264RENDER_PASS_STATE::RENDER_PASS_STATE(VkRenderPass rp, VkRenderPassCreateInfo const *pCreateInfo)
aitor-lunarga131fca2022-02-17 22:55:55 +0100265 : BASE_NODE(rp, kVulkanObjectTypeRenderPass),
266 use_dynamic_rendering(false),
267 use_dynamic_rendering_inherited(false),
268 has_multiview_enabled(false),
269 createInfo(ConvertCreateInfo(*pCreateInfo)) {
Jeremy Gebben88f58142021-06-01 10:07:52 -0600270 InitRenderPassState(this);
271}
272
Tony-LunarG40b33882021-12-02 12:40:11 -0700273const VkPipelineRenderingCreateInfo VkPipelineRenderingCreateInfo_default = {
274 VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
amhagana448ea52021-11-02 14:09:14 -0400275 nullptr,
276 0,
277 0,
278 nullptr,
279 VK_FORMAT_UNDEFINED,
280 VK_FORMAT_UNDEFINED
281};
282
Tony-LunarG40b33882021-12-02 12:40:11 -0700283RENDER_PASS_STATE::RENDER_PASS_STATE(VkPipelineRenderingCreateInfo const *pPipelineRenderingCreateInfo)
amhagana448ea52021-11-02 14:09:14 -0400284 : BASE_NODE(static_cast<VkRenderPass>(VK_NULL_HANDLE), kVulkanObjectTypeRenderPass),
285 use_dynamic_rendering(true),
286 use_dynamic_rendering_inherited(false),
aitor-lunarga131fca2022-02-17 22:55:55 +0100287 has_multiview_enabled(false),
Aaron Hagan92a44f82021-11-19 09:34:56 -0500288 dynamic_rendering_pipeline_create_info(pPipelineRenderingCreateInfo ? pPipelineRenderingCreateInfo
Tony-LunarG40b33882021-12-02 12:40:11 -0700289 : &VkPipelineRenderingCreateInfo_default) {}
amhagana448ea52021-11-02 14:09:14 -0400290
Jeremy Gebben11af9792021-08-20 10:20:09 -0600291bool RENDER_PASS_STATE::UsesColorAttachment(uint32_t subpass_num) const {
292 bool result = false;
293
294 if (subpass_num < createInfo.subpassCount) {
295 const auto &subpass = createInfo.pSubpasses[subpass_num];
296
297 for (uint32_t i = 0; i < subpass.colorAttachmentCount; ++i) {
298 if (subpass.pColorAttachments[i].attachment != VK_ATTACHMENT_UNUSED) {
299 result = true;
300 break;
301 }
302 }
303 }
304 return result;
305}
306
307bool RENDER_PASS_STATE::UsesDepthStencilAttachment(uint32_t subpass_num) const {
308 bool result = false;
309 if (subpass_num < createInfo.subpassCount) {
310 const auto &subpass = createInfo.pSubpasses[subpass_num];
311 if (subpass.pDepthStencilAttachment && subpass.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED) {
312 result = true;
313 }
314 }
315 return result;
316}
317
ziga-lunargb05ba0f2022-08-05 14:46:11 +0200318uint32_t RENDER_PASS_STATE::GetDynamicRenderingColorAttachmentCount() const {
319 if (use_dynamic_rendering_inherited) {
320 return inheritance_rendering_info.colorAttachmentCount;
321 } else if (use_dynamic_rendering) {
322 return dynamic_rendering_begin_rendering_info.colorAttachmentCount;
323 }
324 return 0;
325}
326
327uint32_t RENDER_PASS_STATE::GetDynamicRenderingViewMask() const {
328 if (use_dynamic_rendering_inherited) {
329 return inheritance_rendering_info.viewMask;
330 } else if (use_dynamic_rendering) {
331 return dynamic_rendering_begin_rendering_info.viewMask;
332 }
333 return 0;
334}
335
ziga-lunarg9ad42712022-08-21 03:01:29 +0200336uint32_t RENDER_PASS_STATE::GetViewMaskBits(uint32_t subpass) const {
337 if (use_dynamic_rendering_inherited) {
338 constexpr int num_bits = std::numeric_limits<decltype(inheritance_rendering_info.viewMask)>::digits;
339 std::bitset<num_bits> view_bits(inheritance_rendering_info.viewMask);
340 return static_cast<uint32_t>(view_bits.count());
341 } else if (use_dynamic_rendering) {
342 constexpr int num_bits = std::numeric_limits<decltype(dynamic_rendering_begin_rendering_info.viewMask)>::digits;
343 std::bitset<num_bits> view_bits(dynamic_rendering_begin_rendering_info.viewMask);
344 return static_cast<uint32_t>(view_bits.count());
345 } else {
346 const auto *subpass_desc = &createInfo.pSubpasses[subpass];
347 if (subpass_desc) {
348 constexpr int num_bits = std::numeric_limits<decltype(subpass_desc->viewMask)>::digits;
349 std::bitset<num_bits> view_bits(subpass_desc->viewMask);
350 return static_cast<uint32_t>(view_bits.count());
351 }
352 }
353 return 0;
354}
355
Tony-LunarG40b33882021-12-02 12:40:11 -0700356RENDER_PASS_STATE::RENDER_PASS_STATE(VkRenderingInfo const *pRenderingInfo)
amhagana448ea52021-11-02 14:09:14 -0400357 : BASE_NODE(static_cast<VkRenderPass>(VK_NULL_HANDLE), kVulkanObjectTypeRenderPass),
358 use_dynamic_rendering(true),
359 use_dynamic_rendering_inherited(false),
aitor-lunarga131fca2022-02-17 22:55:55 +0100360 has_multiview_enabled(false),
Aaron Hagan92a44f82021-11-19 09:34:56 -0500361 dynamic_rendering_begin_rendering_info(pRenderingInfo) {}
amhagana448ea52021-11-02 14:09:14 -0400362
aitor-lunarga131fca2022-02-17 22:55:55 +0100363RENDER_PASS_STATE::RENDER_PASS_STATE(VkCommandBufferInheritanceRenderingInfo const *pInheritanceRenderingInfo)
amhagana448ea52021-11-02 14:09:14 -0400364 : BASE_NODE(static_cast<VkRenderPass>(VK_NULL_HANDLE), kVulkanObjectTypeRenderPass),
aitor-lunarga131fca2022-02-17 22:55:55 +0100365 use_dynamic_rendering(false),
366 use_dynamic_rendering_inherited(true),
367 has_multiview_enabled(false),
368 inheritance_rendering_info(pInheritanceRenderingInfo) {}
amhagana448ea52021-11-02 14:09:14 -0400369
Jeremy Gebben88f58142021-06-01 10:07:52 -0600370FRAMEBUFFER_STATE::FRAMEBUFFER_STATE(VkFramebuffer fb, const VkFramebufferCreateInfo *pCreateInfo,
371 std::shared_ptr<RENDER_PASS_STATE> &&rpstate,
372 std::vector<std::shared_ptr<IMAGE_VIEW_STATE>> &&attachments)
Jeremy Gebbene83bd442021-09-02 11:18:03 -0600373 : BASE_NODE(fb, kVulkanObjectTypeFramebuffer),
374 createInfo(pCreateInfo),
375 rp_state(rpstate),
Jeremy Gebben610d3a62022-01-01 12:53:17 -0700376 attachments_view_state(std::move(attachments)) {}
377
378void FRAMEBUFFER_STATE::LinkChildNodes() {
379 // Connect child node(s), which cannot safely be done in the constructor.
Jeremy Gebbene83bd442021-09-02 11:18:03 -0600380 for (auto &a : attachments_view_state) {
Jeremy Gebben88f58142021-06-01 10:07:52 -0600381 a->AddParent(this);
382 }
Jeremy Gebben88f58142021-06-01 10:07:52 -0600383}
384
385void FRAMEBUFFER_STATE::Destroy() {
amhagana448ea52021-11-02 14:09:14 -0400386 for (auto &view : attachments_view_state) {
Jeremy Gebben88f58142021-06-01 10:07:52 -0600387 view->RemoveParent(this);
388 }
389 attachments_view_state.clear();
390 BASE_NODE::Destroy();
391}