Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ CheckOptions:
value: true
- key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams
value: true
- key: readability-convert-member-functions-to-static
value: false

- key: modernize-use-auto.MinTypeNameLength
value: 5
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ compile_commands.json
.cache
logs/
CMakeFiles/

latex/
# macOS
.DS_Store

Expand All @@ -13,3 +13,5 @@ vcpkg/buildtrees/
vcpkg/downloads/
vcpkg/packages/
vcpkg/installed/
.idea
\html/
8 changes: 5 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,14 @@ deps:
else \
echo "ninja already installed"; \
fi; \
if [ ! -f "vcpkg/bootstrap-vcpkg.sh" ] && [ ! -f "vcpkg/bootstrap-vcpkg.bat" ]; then \
if git submodule status -- vcpkg >/dev/null 2>&1; then \
git submodule update --init --recursive vcpkg; \
elif [ ! -f "vcpkg/bootstrap-vcpkg.sh" ] && [ ! -f "vcpkg/bootstrap-vcpkg.bat" ]; then \
echo "vcpkg checkout not found; cloning..."; \
rm -rf vcpkg; \
git clone https://github.com/microsoft/vcpkg.git vcpkg; \
else \
git submodule update --init --recursive; \
echo "vcpkg checkout already present"; \
fi; \
if [ -f "vcpkg/bootstrap-vcpkg.sh" ]; then \
if [ ! -f "vcpkg/vcpkg" ]; then \
Expand All @@ -143,7 +145,7 @@ deps:
vcpkg-install: deps
./vcpkg/vcpkg install --triplet $(VCPKG_TRIPLET)

configure:
configure: deps
cmake --preset $(CMAKE_CONFIGURE_PRESET)
./scripts/sync_compile_commands.sh $(BUILD_DIR)

Expand Down
18 changes: 18 additions & 0 deletions include/quark/utils/allocator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <memory_resource>

namespace util {

using BumpAllocator = std::pmr::monotonic_buffer_resource;
using MemoryResource = std::pmr::memory_resource;

[[nodiscard]] inline MemoryResource *default_memory_resource() noexcept {
return std::pmr::get_default_resource();
}

[[nodiscard]] inline MemoryResource *system_memory_resource() noexcept {
return std::pmr::new_delete_resource();
}

} // namespace util
2 changes: 1 addition & 1 deletion include/quark/utils/diagnostic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ using SinkList = std::vector<DiagnosticSink>;
void set_diagnostic_sinks(std::span<const DiagnosticSink> sinks) noexcept;
std::shared_ptr<const SinkList> diagnostic_sinks_snapshot() noexcept;

void report(const DiagnosticEvent &) noexcept;
void report(const DiagnosticEvent & /*e*/) noexcept;

template <class T>
inline void report_if_error(const util::Result<T> &r) noexcept {
Expand Down
122 changes: 122 additions & 0 deletions include/quark/utils/generational_registry.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#pragma once

#include <cstdint>
#include <memory_resource>
#include <quark/utils/allocator.hpp>
#include <quark/utils/generic_handle.hpp>
#include <quark/utils/raii.hpp>
#include <quark/utils/result.hpp>
#include <vector>

namespace util {

template <class T, OpaqueHandle Handle> class GenerationalRegistry final {
public:
using CreateInfo = typename T::CreateInfo;

explicit GenerationalRegistry(
std::pmr::memory_resource *memory_resource = default_memory_resource())
: slots_(memory_resource), free_(memory_resource) {}

~GenerationalRegistry() = default;

QUARK_MOVE_ONLY(GenerationalRegistry);

void clear() noexcept {
for (auto &slot : slots_) {
if (slot.live) {
slot.object.destroy();
slot.live = false;
++slot.generation;
}
}

free_.clear();
free_.reserve(slots_.size());
for (uint32_t index = 0; index < slots_.size(); ++index) {
free_.push_back(index);
}
}

[[nodiscard]] util::Result<Handle> create(const CreateInfo &create_info) {
uint32_t index = 0;

if (!free_.empty()) {
index = free_.back();
free_.pop_back();
} else {
index = static_cast<uint32_t>(slots_.size());
slots_.emplace_back();
}

Slot &slot = slots_[index];

if (slot.live) {
slot.object.destroy();
slot.live = false;
++slot.generation;
}

QUARK_TRY_STATUS(slot.object.create(create_info));
slot.live = true;

return Handle{.index = index, .generation = slot.generation};
}

void destroy(Handle handle) noexcept {
if (!handle.valid() || handle.index >= slots_.size()) {
return;
}

Slot &slot = slots_[handle.index];
if (!matches_(handle, slot)) {
return;
}

slot.object.destroy();
slot.live = false;
++slot.generation;

free_.push_back(handle.index);
}

[[nodiscard]] bool alive(Handle handle) const noexcept {
if (!handle.valid() || handle.index >= slots_.size()) {
return false;
}

return matches_(handle, slots_[handle.index]);
}

T *get(Handle handle) noexcept {
if (!alive(handle)) {
return nullptr;
}

return &slots_[handle.index].object;
}

[[nodiscard]] const T *get(Handle handle) const noexcept {
if (!alive(handle)) {
return nullptr;
}

return &slots_[handle.index].object;
}

private:
struct Slot {
T object;
uint32_t generation = 1;
bool live = false;
};

[[nodiscard]] static bool matches_(Handle handle, const Slot &slot) noexcept {
return handle.valid() && slot.live && slot.generation == handle.generation;
}

std::pmr::vector<Slot> slots_;
std::pmr::vector<uint32_t> free_;
};

} // namespace util
32 changes: 32 additions & 0 deletions include/quark/utils/generic_handle.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include <concepts>
#include <cstdint>
#include <limits>

namespace util {

template <class H>
concept OpaqueHandle = std::equality_comparable<H> && requires(H handle) {
{ handle.index } -> std::convertible_to<uint32_t>;
{ handle.generation } -> std::convertible_to<uint32_t>;
};

template <class Tag> struct GenericHandle final {
static constexpr uint32_t invalid_index =
std::numeric_limits<uint32_t>::max();

uint32_t index = invalid_index;
uint32_t generation = 0;

[[nodiscard]] constexpr bool valid() const noexcept {
return index != invalid_index;
}

[[nodiscard]] friend constexpr bool
operator==(GenericHandle, GenericHandle) noexcept = default;
};

static_assert(OpaqueHandle<GenericHandle<void>>);

} // namespace util
16 changes: 3 additions & 13 deletions include/quark/vk/device/details/device_handle.hpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
#pragma once

#include <cstdint>
#include <quark/utils/generic_handle.hpp>

namespace quark::vk {

struct DeviceHandle {
uint32_t index = 0xFFFF'FFFFU;
uint32_t generation = 0;
struct DeviceHandleTag;

[[nodiscard]] constexpr bool valid() const noexcept {
return index != 0xFFFF'FFFFU;
}

[[nodiscard]] friend constexpr bool operator==(DeviceHandle a,
DeviceHandle b) noexcept {
return a.index == b.index && a.generation == b.generation;
}
};
using DeviceHandle = util::GenericHandle<DeviceHandleTag>;

} // namespace quark::vk
36 changes: 2 additions & 34 deletions include/quark/vk/device/details/device_registry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,10 @@
#include "device.hpp"
#include "device_handle.hpp"

#include <cstdint>
#include <quark/utils/raii.hpp>
#include <quark/utils/result.hpp>
#include <vector>
#include <quark/utils/generational_registry.hpp>

namespace quark::vk {

class DeviceRegistry final {
public:
DeviceRegistry() = default;
~DeviceRegistry() = default;

QUARK_MOVE_ONLY(DeviceRegistry);

void clear() noexcept;

[[nodiscard]] util::Result<DeviceHandle> create(const Device::CreateInfo &ci);
void destroy(DeviceHandle handle) noexcept;

[[nodiscard]] bool alive(DeviceHandle handle) const noexcept;

Device *get(DeviceHandle handle) noexcept;
[[nodiscard]] const Device *get(DeviceHandle handle) const noexcept;

private:
struct Slot {
Device device;
uint32_t generation = 1;
bool live = false;
};

[[nodiscard]] static bool matches_(DeviceHandle handle,
const Slot &s) noexcept;

std::vector<Slot> slots_;
std::vector<uint32_t> free_;
};
using DeviceRegistry = util::GenerationalRegistry<Device, DeviceHandle>;

} // namespace quark::vk
5 changes: 5 additions & 0 deletions include/quark/vk/device/device_bundle.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include "quark/utils/result.hpp"
#include <memory_resource>
#include <quark/utils/allocator.hpp>
#include <quark/utils/raii.hpp>
#include <quark/vk/device/details/device.hpp>
#include <quark/vk/device/details/device_handle.hpp>
Expand All @@ -11,7 +13,10 @@ namespace quark::vk {
class DeviceBundle final {
public:
DeviceBundle() = default;
explicit DeviceBundle(std::pmr::memory_resource *memory_resource);
explicit DeviceBundle(const Device::CreateInfo &ci);
DeviceBundle(const Device::CreateInfo &ci,
std::pmr::memory_resource *memory_resource);
~DeviceBundle() { destroy(); }

QUARK_MOVE_ONLY(DeviceBundle);
Expand Down
1 change: 0 additions & 1 deletion include/quark/vk/instance/details/debug_messenger.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma once

#include <cstddef>
#include <quark/utils/raii.hpp>
#include <quark/utils/result.hpp>
#include <vulkan/vulkan.h>
Expand Down
16 changes: 3 additions & 13 deletions include/quark/vk/instance/details/instance_handle.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include <cstdint>
#include <quark/utils/generic_handle.hpp>

namespace quark::vk {

Expand All @@ -9,18 +9,8 @@ namespace quark::vk {
*
* Generation prevents use-after-free when slots are reused.
*/
struct InstanceHandle {
uint32_t index = 0xFFFF'FFFFU;
uint32_t generation = 0;
struct InstanceHandleTag;

[[nodiscard]] constexpr bool valid() const noexcept {
return index != 0xFFFF'FFFFU;
}

[[nodiscard]] friend constexpr bool operator==(InstanceHandle a,
InstanceHandle b) noexcept {
return a.index == b.index && a.generation == b.generation;
}
};
using InstanceHandle = util::GenericHandle<InstanceHandleTag>;

} // namespace quark::vk
Loading
Loading