1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
/* Copyright (c) 2015-2022 The Khronos Group Inc.
* Copyright (c) 2015-2022 Valve Corporation
* Copyright (c) 2015-2022 LunarG, Inc.
* Copyright (C) 2015-2022 Google Inc.
* Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Courtney Goeltzenleuchter <courtneygo@google.com>
* Author: Tobin Ehlis <tobine@google.com>
* Author: Chris Forbes <chrisf@ijw.co.nz>
* Author: Mark Lobodzinski <mark@lunarg.com>
* Author: Dave Houlton <daveh@lunarg.com>
* Author: John Zulauf <jzulauf@lunarg.com>
* Author: Tobias Hector <tobias.hector@amd.com>
* Author: Jeremy Gebben <jeremyg@lunarg.com>
*/
#pragma once
#include "vulkan/vulkan.h"
#include "vk_object_types.h"
#include "vk_layer_data.h"
#include "vk_layer_logging.h"
#include "vk_layer_utils.h"
#include <atomic>
// Intentionally ignore VulkanTypedHandle::node, it is optional
inline bool operator==(const VulkanTypedHandle &a, const VulkanTypedHandle &b) noexcept {
return a.handle == b.handle && a.type == b.type;
}
namespace std {
template <>
struct hash<VulkanTypedHandle> {
size_t operator()(VulkanTypedHandle obj) const noexcept { return hash<uint64_t>()(obj.handle) ^ hash<uint32_t>()(obj.type); }
};
} // namespace std
// inheriting from enable_shared_from_this<> adds a method, shared_from_this(), which
// returns a shared_ptr version of the current object. It requires the object to
// be created with std::make_shared<> and it MUST NOT be used from the constructor
class BASE_NODE : public std::enable_shared_from_this<BASE_NODE> {
public:
// Parent nodes are stored as weak_ptrs to avoid cyclic memory dependencies.
// Because weak_ptrs cannot safely be used as hash keys, the parents are stored
// in a map keyed by VulkanTypedHandle. This also allows looking for specific
// parent types without locking every weak_ptr.
using NodeMap = layer_data::unordered_map<VulkanTypedHandle, std::weak_ptr<BASE_NODE>>;
using NodeList = small_vector<std::shared_ptr<BASE_NODE>, 4, uint32_t>;
template <typename Handle>
BASE_NODE(Handle h, VulkanObjectType t) : handle_(h, t), destroyed_(false) {}
// because shared_from_this() does not work from the constructor, this 2nd phase
// constructor is where a state object should call AddParent() on its child nodes.
// It is called as part of ValidationStateTracker::Add()
virtual void LinkChildNodes() {}
virtual ~BASE_NODE();
// Because state objects are reference counted, they may outlive the vulkan objects
// they represent. Destroy() is called when the vulkan object is destroyed, so that
// it can be cleaned up before all references are gone. It also triggers notifications
// to parent objects.
virtual void Destroy();
bool Destroyed() const { return destroyed_; }
// returns true if this vulkan object or any it uses have been destroyed
virtual bool Invalid() const { return Destroyed(); }
// Save the tedium of two part testing...
static bool Invalid(const BASE_NODE *node) { return !node || node->Destroyed(); }
static bool Invalid(const std::shared_ptr<const BASE_NODE> &node) { return !node || node->Destroyed(); }
const VulkanTypedHandle &Handle() const { return handle_; }
VulkanObjectType Type() const { return handle_.type; }
virtual bool InUse() const;
virtual bool AddParent(BASE_NODE *parent_node);
virtual void RemoveParent(BASE_NODE *parent_node);
// Invalidate is called on a state object to inform its parents that it
// is being destroyed (unlink == true) or otherwise becoming invalid (unlink == false)
void Invalidate(bool unlink = true);
// Helper to let objects examine their immediate parents without holding the tree lock.
NodeMap ObjectBindings() const;
protected:
template <typename Derived, typename Shared = std::shared_ptr<Derived>>
static Shared SharedFromThisImpl(Derived *derived) {
using Base = typename std::conditional<std::is_const<Derived>::value, const BASE_NODE, BASE_NODE>::type;
auto base = static_cast<Base *>(derived);
return std::static_pointer_cast<Derived>(base->shared_from_this());
}
// Called recursively for every parent object of something that has become invalid
virtual void NotifyInvalidate(const NodeList &invalid_nodes, bool unlink);
// returns a copy of the current set of parents so that they can be walked
// without the tree lock held. If unlink == true, parent_nodes_ is also cleared.
NodeMap GetParentsForInvalidate(bool unlink);
VulkanTypedHandle handle_;
// Set to true when the API-level object is destroyed, but this object may
// hang around until its shared_ptr refcount goes to zero.
std::atomic<bool> destroyed_;
private:
ReadLockGuard ReadLockTree() const { return ReadLockGuard(tree_lock_); }
WriteLockGuard WriteLockTree() { return WriteLockGuard(tree_lock_); }
// Set of immediate parent nodes for this object. For an in-use object, the
// parent nodes should form a tree with the root being a command buffer.
NodeMap parent_nodes_;
// Lock guarding parent_nodes_, this lock MUST NOT be used for other purposes.
mutable std::shared_mutex tree_lock_;
};
class REFCOUNTED_NODE : public BASE_NODE {
private:
// Track if command buffer is in-flight
std::atomic_int in_use_;
public:
template <typename Handle>
REFCOUNTED_NODE(Handle h, VulkanObjectType t) : BASE_NODE(h, t), in_use_(0) { }
void BeginUse() { in_use_.fetch_add(1); }
void EndUse() { in_use_.fetch_sub(1); }
void ResetUse() { in_use_.store(0); }
bool InUse() const override { return (in_use_.load() > 0) || BASE_NODE::InUse(); }
};
|