blob: f3acb796b76e7cc4bb93a6ae837b3280e575aa0b [file] [log] [blame]
/* 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_; }
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:
// 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 ReadWriteLock 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(); }
};