Upgrading pool pattern
This commit is contained in:
+31
-10
@@ -3,34 +3,55 @@
|
||||
## ResourceRegistry
|
||||
|
||||
A ResourceRegistry owns GPU-backed resources on the caller's behalf and hands out
|
||||
lightweight Handles instead of pointers or ownership.
|
||||
lightweight Handles instead of pointers or ownership. It is engine-internal:
|
||||
frontend code never sees it, and reaches it through a ResourceRegistryRef.
|
||||
|
||||
Internally it's a collection of per-type pools, each responsible for one
|
||||
resource type:
|
||||
Internally it's a collection of per-type pools, each a `Pool<T, Handle>`
|
||||
(a generation-checked slot pool). A type that needs extra behaviour wraps its
|
||||
pool in a thin class instead:
|
||||
|
||||
```
|
||||
ResourceRegistry
|
||||
├── MeshPool → MeshHandle
|
||||
├── TexturePool → TextureHandle (planned)
|
||||
└── MaterialPool → MaterialHandle (planned)
|
||||
├── meshes Pool<Mesh, MeshHandle>
|
||||
├── renderTargets RenderTargetPool → RenderTargetHandle
|
||||
│ (wraps Pool; waits for the GPU on release, applies resizes)
|
||||
├── scenes Pool<Scene, SceneHandle>
|
||||
├── textures (planned)
|
||||
└── materials (planned)
|
||||
```
|
||||
|
||||
Engine code (`Application`, `ImGuiRenderer`, `SceneRenderer`) uses the pools
|
||||
directly, e.g. `resources.renderTargets.get(handle)`.
|
||||
|
||||
A Handle is a small (index, generation) pair. The index locates the slot in
|
||||
the pool; the generation is bumped when that slot is released, so a Handle
|
||||
kept around after release() is detected as stale instead of resolving to
|
||||
someone else's resource (or freed memory).
|
||||
|
||||
Think: "Where does the engine keep every GPU resource, and how does it look one
|
||||
up by handle?"
|
||||
|
||||
## ResourceRegistryRef
|
||||
|
||||
A ResourceRegistryRef is the frontend's non-owning, cheap-to-copy view of the
|
||||
ResourceRegistry, returned by `Application::resources()`. It exposes only what
|
||||
frontend code needs and hands out no pools, so no Vulkan-facing types leak
|
||||
through the public API:
|
||||
|
||||
```
|
||||
create<T>(...) load<T>(path) release<T>(handle)
|
||||
requestResize(target, w, h) getExtent(target) isAlive(handle)
|
||||
```
|
||||
|
||||
Typical lifecycle:
|
||||
|
||||
```
|
||||
load<Mesh>(path) / create<Mesh>(...) → MeshHandle
|
||||
│
|
||||
get<Mesh>(handle) → const Mesh&
|
||||
│
|
||||
release<Mesh>(handle)
|
||||
```
|
||||
|
||||
Think: "Where do I get or release a GPU resource by handle, without owning its
|
||||
Think: "How do I create or release a GPU resource by handle, without owning its
|
||||
lifetime directly?"
|
||||
|
||||
## Buffer
|
||||
@@ -257,7 +278,7 @@ A PanelRegistry owns Panels on the caller's behalf and hands out lightweight
|
||||
PanelHandles instead of pointers or ownership.
|
||||
|
||||
Internally it's a generation-checked slot pool, the same shape ResourceRegistry's
|
||||
per-type pools use:
|
||||
`Pool<T, Handle>` uses:
|
||||
|
||||
```
|
||||
PanelRegistry
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <sophia/canvas.hpp>
|
||||
#include <sophia/console/console_command_registry.hpp>
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/resource_registry.hpp>
|
||||
#include <sophia/resource_registry_ref.hpp>
|
||||
#include <sophia/types.hpp>
|
||||
#include <sophia/ui/debug_ui.hpp>
|
||||
#include <sophia/ui/panel_registry.hpp>
|
||||
@@ -45,7 +45,7 @@ class Application {
|
||||
virtual void onRender() {}
|
||||
virtual void onDestroy() {}
|
||||
|
||||
ResourceRegistry& resources() { return this->resourceRegistry; }
|
||||
ResourceRegistryRef resources();
|
||||
PanelRegistry& panels() { return this->panelRegistry; }
|
||||
UI& ui() { return this->_ui; }
|
||||
|
||||
@@ -62,7 +62,7 @@ class Application {
|
||||
/**
|
||||
* Chooses where the scene is rendered. An invalid handle (the default) means
|
||||
* the swapchain; a target created with
|
||||
* ResourceRegistry::create<RenderTarget>() redirects it offscreen, and the
|
||||
* ResourceRegistryRef::create<RenderTarget>() redirects it offscreen, and the
|
||||
* swapchain then only receives the UI. FrameInfo::currentFramebufferExtent
|
||||
* and the camera projection follow the target's size.
|
||||
*/
|
||||
@@ -77,7 +77,6 @@ class Application {
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
ResourceRegistry resourceRegistry;
|
||||
PanelRegistry panelRegistry;
|
||||
UI _ui;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <sophia/resource_registry.hpp>
|
||||
#include <sophia/handles.hpp>
|
||||
#include <string>
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
|
||||
@@ -27,9 +27,11 @@ struct Handle {
|
||||
struct MeshTag {};
|
||||
struct PanelTag {};
|
||||
struct RenderTargetTag {};
|
||||
struct SceneTag {};
|
||||
|
||||
using MeshHandle = Handle<MeshTag>;
|
||||
using PanelHandle = Handle<PanelTag>;
|
||||
using RenderTargetHandle = Handle<RenderTargetTag>;
|
||||
using SceneHandle = Handle<SceneTag>;
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
+19
-32
@@ -1,25 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sophia/canvas.hpp>
|
||||
#include <sophia/handles.hpp>
|
||||
#include <sophia/types.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "sophia/canvas.hpp"
|
||||
#include "sophia/types.hpp"
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class Device;
|
||||
class Mesh;
|
||||
class RenderTarget;
|
||||
class ImGuiRenderer;
|
||||
class Scene;
|
||||
class ResourceRegistry;
|
||||
|
||||
template <typename T>
|
||||
struct ResourceTraits;
|
||||
@@ -34,13 +32,13 @@ struct ResourceTraits<RenderTarget> {
|
||||
using Handle = RenderTargetHandle;
|
||||
};
|
||||
|
||||
class ResourceRegistry {
|
||||
template <>
|
||||
struct ResourceTraits<Scene> {
|
||||
using Handle = SceneHandle;
|
||||
};
|
||||
|
||||
class ResourceRegistryRef {
|
||||
public:
|
||||
~ResourceRegistry();
|
||||
|
||||
ResourceRegistry(const ResourceRegistry&) = delete;
|
||||
ResourceRegistry& operator=(const ResourceRegistry&) = delete;
|
||||
|
||||
template <typename T>
|
||||
typename ResourceTraits<T>::Handle load(const std::string& path);
|
||||
|
||||
@@ -54,21 +52,16 @@ class ResourceRegistry {
|
||||
glm::vec2 getExtent(RenderTargetHandle handle) const;
|
||||
|
||||
bool isAlive(RenderTargetHandle handle) const;
|
||||
bool isAlive(SceneHandle handle) const;
|
||||
|
||||
/**
|
||||
* Waits for the GPU to go idle, so don't call this every frame. Don't release
|
||||
* the target the scene is currently rendering into.
|
||||
*/
|
||||
template <typename T>
|
||||
void release(typename ResourceTraits<T>::Handle handle);
|
||||
|
||||
private:
|
||||
friend class Application;
|
||||
friend class ImGuiRenderer;
|
||||
|
||||
// `renderTargetFormat` is a VkFormat value; it must match the color format
|
||||
// the scene pipelines are built against.
|
||||
ResourceRegistry(Device& device, u32 renderTargetFormat);
|
||||
explicit ResourceRegistryRef(ResourceRegistry& registry)
|
||||
: registry{®istry} {}
|
||||
|
||||
MeshHandle createResource(std::type_identity<Mesh>,
|
||||
std::vector<CanvasVertex> vertices,
|
||||
@@ -77,27 +70,21 @@ class ResourceRegistry {
|
||||
RenderTargetHandle createResource(std::type_identity<RenderTarget>,
|
||||
u32 width = 1, u32 height = 1);
|
||||
|
||||
RenderTarget& getRenderTarget(RenderTargetHandle handle);
|
||||
const RenderTarget& getRenderTarget(RenderTargetHandle handle) const;
|
||||
void applyPendingResizes();
|
||||
SceneHandle createResource(std::type_identity<Scene>);
|
||||
|
||||
template <typename T>
|
||||
const T& get(typename ResourceTraits<T>::Handle handle) const;
|
||||
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
ResourceRegistry* registry;
|
||||
};
|
||||
|
||||
template <>
|
||||
MeshHandle ResourceRegistry::load<Mesh>(const std::string& path);
|
||||
MeshHandle ResourceRegistryRef::load<Mesh>(const std::string& path);
|
||||
|
||||
template <>
|
||||
void ResourceRegistry::release<Mesh>(MeshHandle handle);
|
||||
void ResourceRegistryRef::release<Mesh>(MeshHandle handle);
|
||||
|
||||
template <>
|
||||
void ResourceRegistry::release<RenderTarget>(RenderTargetHandle handle);
|
||||
void ResourceRegistryRef::release<RenderTarget>(RenderTargetHandle handle);
|
||||
|
||||
template <>
|
||||
const Mesh& ResourceRegistry::get<Mesh>(MeshHandle handle) const;
|
||||
void ResourceRegistryRef::release<Scene>(SceneHandle handle);
|
||||
|
||||
} // namespace sophia
|
||||
@@ -3,11 +3,11 @@
|
||||
#include <imgui.h>
|
||||
|
||||
#include <sophia/handles.hpp>
|
||||
#include <sophia/resource_registry_ref.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class ImGuiRenderer;
|
||||
class ResourceRegistry;
|
||||
|
||||
class UI {
|
||||
public:
|
||||
@@ -31,9 +31,9 @@ class UI {
|
||||
private:
|
||||
friend class Application;
|
||||
|
||||
explicit UI(ResourceRegistry& resources) : resources{resources} {}
|
||||
explicit UI(ResourceRegistryRef resources) : resources{resources} {}
|
||||
|
||||
ResourceRegistry& resources;
|
||||
ResourceRegistryRef resources;
|
||||
ImGuiRenderer* renderer = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ void ImGuiRenderer::beginFrame() {
|
||||
|
||||
ImTextureID ImGuiRenderer::getTextureId(RenderTargetHandle handle) {
|
||||
vk::ImageView view =
|
||||
std::as_const(resources).getRenderTarget(handle).getImage().getView();
|
||||
resources.renderTargetPool.get(handle).getImage().getView();
|
||||
|
||||
auto it = textures.find(handle.index);
|
||||
if (it != textures.end()) {
|
||||
@@ -116,10 +116,11 @@ ImTextureID ImGuiRenderer::getTextureId(RenderTargetHandle handle) {
|
||||
void ImGuiRenderer::releaseStaleTextures() {
|
||||
for (auto it = textures.begin(); it != textures.end();) {
|
||||
RenderTargetHandle handle{it->first, it->second.generation};
|
||||
RenderTargetPool& renderTargetPool = resources.renderTargetPool;
|
||||
|
||||
bool stale =
|
||||
!resources.isAlive(handle) ||
|
||||
std::as_const(resources).getRenderTarget(handle).getImage().getView() !=
|
||||
it->second.view;
|
||||
!renderTargetPool.isAlive(handle) ||
|
||||
renderTargetPool.get(handle).getImage().getView() != it->second.view;
|
||||
if (stale) {
|
||||
ImGui_ImplVulkan_RemoveTexture(it->second.descriptorSet);
|
||||
it = textures.erase(it);
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
#include <imgui.h>
|
||||
|
||||
#include <sophia/handles.hpp>
|
||||
#include <sophia/resource_registry.hpp>
|
||||
#include <sophia/types.hpp>
|
||||
#include <sophia/ui/dock_layout.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "resources/resource_registry.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sophia/resource_registry.hpp>
|
||||
#include <sophia/handles.hpp>
|
||||
#include <sophia/types.hpp>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
#include "resources/pools/mesh_pool.hpp"
|
||||
|
||||
#include <sophia/util/assert.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
namespace {
|
||||
constexpr u32 GENERATION_MASK = (1u << 12) - 1;
|
||||
} // namespace
|
||||
|
||||
MeshPool::MeshPool(Device& device) : device{device} {}
|
||||
|
||||
MeshPool::~MeshPool() = default;
|
||||
|
||||
MeshHandle MeshPool::create(const Mesh::Builder& builder) {
|
||||
auto mesh = std::make_unique<Mesh>(device, builder);
|
||||
|
||||
if (!freeList.empty()) {
|
||||
u32 index = freeList.back();
|
||||
freeList.pop_back();
|
||||
slots[index].mesh = std::move(mesh);
|
||||
return MeshHandle{index, slots[index].generation};
|
||||
}
|
||||
|
||||
slots.push_back(Slot{std::move(mesh), 0});
|
||||
return MeshHandle{static_cast<u32>(slots.size() - 1), 0};
|
||||
}
|
||||
|
||||
const Mesh& MeshPool::get(MeshHandle handle) const {
|
||||
SOPHIA_ASSERT(handle.isValid(), "invalid MeshHandle");
|
||||
SOPHIA_ASSERT(handle.index < slots.size(),
|
||||
"MeshHandle index out of range:", handle.index);
|
||||
|
||||
const Slot& slot = slots[handle.index];
|
||||
SOPHIA_ASSERT(slot.generation == handle.generation,
|
||||
"stale MeshHandle (generation mismatch):", handle.index);
|
||||
SOPHIA_ASSERT(slot.mesh != nullptr,
|
||||
"MeshHandle refers to a released mesh:", handle.index);
|
||||
return *slot.mesh;
|
||||
}
|
||||
|
||||
void MeshPool::release(MeshHandle handle) {
|
||||
SOPHIA_ASSERT(handle.isValid(), "invalid MeshHandle");
|
||||
SOPHIA_ASSERT(handle.index < slots.size(),
|
||||
"MeshHandle index out of range:", handle.index);
|
||||
|
||||
Slot& slot = slots[handle.index];
|
||||
SOPHIA_ASSERT(slot.generation == handle.generation,
|
||||
"stale MeshHandle (generation mismatch):", handle.index);
|
||||
SOPHIA_ASSERT(slot.mesh != nullptr,
|
||||
"MeshHandle already released:", handle.index);
|
||||
|
||||
slot.mesh.reset();
|
||||
slot.generation = (slot.generation + 1) & GENERATION_MASK;
|
||||
freeList.push_back(handle.index);
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "mesh.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class MeshPool {
|
||||
public:
|
||||
explicit MeshPool(Device& device);
|
||||
~MeshPool();
|
||||
|
||||
MeshPool(const MeshPool&) = delete;
|
||||
MeshPool& operator=(const MeshPool&) = delete;
|
||||
|
||||
MeshHandle create(const Mesh::Builder& builder);
|
||||
const Mesh& get(MeshHandle handle) const;
|
||||
void release(MeshHandle handle);
|
||||
|
||||
private:
|
||||
struct Slot {
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
u32 generation = 0;
|
||||
};
|
||||
|
||||
Device& device;
|
||||
|
||||
std::vector<Slot> slots;
|
||||
std::vector<u32> freeList;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sophia/types.hpp>
|
||||
#include <sophia/util/assert.hpp>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
template <typename T, typename HandleT>
|
||||
class Pool {
|
||||
public:
|
||||
explicit Pool(const char* name) : name{name} {}
|
||||
|
||||
Pool(const Pool&) = delete;
|
||||
Pool& operator=(const Pool&) = delete;
|
||||
|
||||
template <typename... Args>
|
||||
HandleT create(Args&&... args) {
|
||||
auto item = std::make_unique<T>(std::forward<Args>(args)...);
|
||||
|
||||
if (!freeList.empty()) {
|
||||
u32 index = freeList.back();
|
||||
freeList.pop_back();
|
||||
slots[index].item = std::move(item);
|
||||
return HandleT{index, slots[index].generation};
|
||||
}
|
||||
|
||||
slots.push_back(Slot{std::move(item), 0});
|
||||
return HandleT{static_cast<u32>(slots.size() - 1), 0};
|
||||
}
|
||||
|
||||
T& get(HandleT handle) {
|
||||
return const_cast<T&>(std::as_const(*this).get(handle));
|
||||
}
|
||||
|
||||
const T& get(HandleT handle) const { return *validate(handle).item; }
|
||||
|
||||
void release(HandleT handle) {
|
||||
validate(handle);
|
||||
|
||||
Slot& slot = slots[handle.index];
|
||||
slot.item.reset();
|
||||
slot.generation = (slot.generation + 1) & GENERATION_MASK;
|
||||
freeList.push_back(handle.index);
|
||||
}
|
||||
|
||||
bool isAlive(HandleT handle) const {
|
||||
return handle.isValid() && handle.index < slots.size() &&
|
||||
slots[handle.index].generation == handle.generation &&
|
||||
slots[handle.index].item != nullptr;
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void forEach(Fn&& fn) {
|
||||
for (Slot& slot : slots) {
|
||||
if (slot.item) {
|
||||
fn(*slot.item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr u32 GENERATION_MASK = (1u << 12) - 1;
|
||||
|
||||
struct Slot {
|
||||
std::unique_ptr<T> item;
|
||||
u32 generation = 0;
|
||||
};
|
||||
|
||||
const Slot& validate(HandleT handle) const {
|
||||
SOPHIA_ASSERT(handle.isValid(), "invalid handle:", name);
|
||||
SOPHIA_ASSERT(handle.index < slots.size(), name,
|
||||
"handle index out of range:", handle.index);
|
||||
|
||||
const Slot& slot = slots[handle.index];
|
||||
SOPHIA_ASSERT(slot.generation == handle.generation, "stale", name,
|
||||
"handle (generation mismatch):", handle.index);
|
||||
SOPHIA_ASSERT(slot.item != nullptr, name,
|
||||
"handle refers to a released item:", handle.index);
|
||||
return slot;
|
||||
}
|
||||
|
||||
const char* name;
|
||||
std::vector<Slot> slots;
|
||||
std::vector<u32> freeList;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -1,81 +1,22 @@
|
||||
#include "resources/pools/render_target_pool.hpp"
|
||||
|
||||
#include <sophia/util/assert.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
namespace {
|
||||
constexpr u32 GENERATION_MASK = (1u << 12) - 1;
|
||||
} // namespace
|
||||
|
||||
RenderTargetPool::RenderTargetPool(Device& device, vk::Format format)
|
||||
: device{device}, format{format} {}
|
||||
|
||||
RenderTargetPool::~RenderTargetPool() = default;
|
||||
|
||||
RenderTargetHandle RenderTargetPool::create(u32 width, u32 height) {
|
||||
auto target = std::make_unique<RenderTarget>(device, format, width, height);
|
||||
|
||||
if (!freeList.empty()) {
|
||||
u32 index = freeList.back();
|
||||
freeList.pop_back();
|
||||
slots[index].target = std::move(target);
|
||||
return RenderTargetHandle{index, slots[index].generation};
|
||||
}
|
||||
|
||||
slots.push_back(Slot{std::move(target), 0});
|
||||
return RenderTargetHandle{static_cast<u32>(slots.size() - 1), 0};
|
||||
}
|
||||
|
||||
const RenderTargetPool::Slot& RenderTargetPool::validate(
|
||||
RenderTargetHandle handle) const {
|
||||
SOPHIA_ASSERT(handle.isValid(), "invalid RenderTargetHandle");
|
||||
SOPHIA_ASSERT(handle.index < slots.size(),
|
||||
"RenderTargetHandle index out of range:", handle.index);
|
||||
|
||||
const Slot& slot = slots[handle.index];
|
||||
SOPHIA_ASSERT(
|
||||
slot.generation == handle.generation,
|
||||
"stale RenderTargetHandle (generation mismatch):", handle.index);
|
||||
SOPHIA_ASSERT(
|
||||
slot.target != nullptr,
|
||||
"RenderTargetHandle refers to a released target:", handle.index);
|
||||
return slot;
|
||||
}
|
||||
|
||||
RenderTarget& RenderTargetPool::get(RenderTargetHandle handle) {
|
||||
return const_cast<RenderTarget&>(std::as_const(*this).get(handle));
|
||||
}
|
||||
|
||||
const RenderTarget& RenderTargetPool::get(RenderTargetHandle handle) const {
|
||||
return *validate(handle).target;
|
||||
return pool.create(device, format, width, height);
|
||||
}
|
||||
|
||||
void RenderTargetPool::release(RenderTargetHandle handle) {
|
||||
validate(handle);
|
||||
|
||||
// Earlier frames may still be rendering into or sampling the target.
|
||||
SOPHIA_ASSERT(pool.isAlive(handle), "invalid RenderTargetHandle");
|
||||
device.waitIdle();
|
||||
|
||||
Slot& slot = slots[handle.index];
|
||||
slot.target.reset();
|
||||
slot.generation = (slot.generation + 1) & GENERATION_MASK;
|
||||
freeList.push_back(handle.index);
|
||||
}
|
||||
|
||||
bool RenderTargetPool::isAlive(RenderTargetHandle handle) const {
|
||||
return handle.isValid() && handle.index < slots.size() &&
|
||||
slots[handle.index].generation == handle.generation &&
|
||||
slots[handle.index].target != nullptr;
|
||||
pool.release(handle);
|
||||
}
|
||||
|
||||
void RenderTargetPool::applyPendingResizes() {
|
||||
for (Slot& slot : slots) {
|
||||
if (slot.target) {
|
||||
slot.target->applyPendingResize();
|
||||
}
|
||||
}
|
||||
pool.forEach([](RenderTarget& target) { target.applyPendingResize(); });
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -1,44 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sophia/handles.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "render_target.hpp"
|
||||
#include "resources/pools/pool.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class RenderTargetPool {
|
||||
public:
|
||||
RenderTargetPool(Device& device, vk::Format format);
|
||||
~RenderTargetPool();
|
||||
|
||||
RenderTargetPool(const RenderTargetPool&) = delete;
|
||||
RenderTargetPool& operator=(const RenderTargetPool&) = delete;
|
||||
|
||||
RenderTargetHandle create(u32 width, u32 height);
|
||||
RenderTarget& get(RenderTargetHandle handle);
|
||||
const RenderTarget& get(RenderTargetHandle handle) const;
|
||||
RenderTarget& get(RenderTargetHandle handle) { return pool.get(handle); }
|
||||
const RenderTarget& get(RenderTargetHandle handle) const {
|
||||
return pool.get(handle);
|
||||
}
|
||||
void release(RenderTargetHandle handle);
|
||||
|
||||
bool isAlive(RenderTargetHandle handle) const;
|
||||
bool isAlive(RenderTargetHandle handle) const { return pool.isAlive(handle); }
|
||||
|
||||
void applyPendingResizes();
|
||||
|
||||
private:
|
||||
struct Slot {
|
||||
std::unique_ptr<RenderTarget> target;
|
||||
u32 generation = 0;
|
||||
};
|
||||
|
||||
const Slot& validate(RenderTargetHandle handle) const;
|
||||
|
||||
Device& device;
|
||||
vk::Format format;
|
||||
|
||||
std::vector<Slot> slots;
|
||||
std::vector<u32> freeList;
|
||||
Pool<RenderTarget, RenderTargetHandle> pool{"RenderTarget"};
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
#include <sophia/resource_registry.hpp>
|
||||
#include <sophia/util/assert.hpp>
|
||||
|
||||
#include "mesh.hpp"
|
||||
#include "pools/mesh_pool.hpp"
|
||||
#include "pools/render_target_pool.hpp"
|
||||
#include "render_target.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class ResourceRegistry::Impl {
|
||||
public:
|
||||
Impl(Device& device, vk::Format renderTargetFormat)
|
||||
: meshPool{device}, renderTargetPool{device, renderTargetFormat} {}
|
||||
|
||||
MeshPool meshPool;
|
||||
RenderTargetPool renderTargetPool;
|
||||
// TexturePool texturePool;
|
||||
// MaterialPool materialPool;
|
||||
};
|
||||
|
||||
ResourceRegistry::ResourceRegistry(Device& device, u32 renderTargetFormat)
|
||||
: impl{std::make_unique<Impl>(
|
||||
device, static_cast<vk::Format>(renderTargetFormat))} {}
|
||||
|
||||
ResourceRegistry::~ResourceRegistry() = default;
|
||||
|
||||
template <>
|
||||
MeshHandle ResourceRegistry::load<Mesh>(const std::string& path) {
|
||||
(void)path;
|
||||
SOPHIA_ASSERT(false,
|
||||
"Mesh::load(path) is not implemented yet - no model file "
|
||||
"format is supported; build one via Mesh::Builder for now");
|
||||
return MeshHandle{};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename ResourceTraits<T>::Handle load(const std::string&) {
|
||||
static_assert(sizeof(T) == 0,
|
||||
"load<T> is not implemented for this resource type");
|
||||
}
|
||||
|
||||
template <>
|
||||
void ResourceRegistry::release<Mesh>(MeshHandle handle) {
|
||||
impl->meshPool.release(handle);
|
||||
}
|
||||
|
||||
template <>
|
||||
void ResourceRegistry::release<RenderTarget>(RenderTargetHandle handle) {
|
||||
impl->renderTargetPool.release(handle);
|
||||
}
|
||||
|
||||
template <>
|
||||
const Mesh& ResourceRegistry::get<Mesh>(MeshHandle handle) const {
|
||||
return impl->meshPool.get(handle);
|
||||
}
|
||||
|
||||
MeshHandle ResourceRegistry::createResource(std::type_identity<Mesh>,
|
||||
std::vector<CanvasVertex> vertices,
|
||||
std::vector<u32> indices) {
|
||||
Mesh::Builder builder{};
|
||||
builder.vertices.reserve(vertices.size());
|
||||
for (const CanvasVertex& vertex : vertices) {
|
||||
builder.vertices.push_back({vertex.position, vertex.color});
|
||||
}
|
||||
builder.indices = std::move(indices);
|
||||
|
||||
return impl->meshPool.create(builder);
|
||||
}
|
||||
|
||||
RenderTargetHandle ResourceRegistry::createResource(
|
||||
std::type_identity<RenderTarget>, u32 width, u32 height) {
|
||||
return impl->renderTargetPool.create(width, height);
|
||||
}
|
||||
|
||||
void ResourceRegistry::requestResize(RenderTargetHandle handle, u32 width,
|
||||
u32 height) {
|
||||
impl->renderTargetPool.get(handle).requestResize(width, height);
|
||||
}
|
||||
|
||||
glm::vec2 ResourceRegistry::getExtent(RenderTargetHandle handle) const {
|
||||
vk::Extent2D extent =
|
||||
std::as_const(impl->renderTargetPool).get(handle).getExtent();
|
||||
return {static_cast<float>(extent.width), static_cast<float>(extent.height)};
|
||||
}
|
||||
|
||||
bool ResourceRegistry::isAlive(RenderTargetHandle handle) const {
|
||||
return impl->renderTargetPool.isAlive(handle);
|
||||
}
|
||||
|
||||
RenderTarget& ResourceRegistry::getRenderTarget(RenderTargetHandle handle) {
|
||||
return impl->renderTargetPool.get(handle);
|
||||
}
|
||||
|
||||
const RenderTarget& ResourceRegistry::getRenderTarget(
|
||||
RenderTargetHandle handle) const {
|
||||
return std::as_const(impl->renderTargetPool).get(handle);
|
||||
}
|
||||
|
||||
void ResourceRegistry::applyPendingResizes() {
|
||||
impl->renderTargetPool.applyPendingResizes();
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <sophia/handles.hpp>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "mesh.hpp"
|
||||
#include "resources/pools/pool.hpp"
|
||||
#include "resources/pools/render_target_pool.hpp"
|
||||
#include "scene.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class ResourceRegistry {
|
||||
public:
|
||||
ResourceRegistry(const ResourceRegistry&) = delete;
|
||||
ResourceRegistry& operator=(const ResourceRegistry&) = delete;
|
||||
|
||||
ResourceRegistry(Device& device, vk::Format renderTargetFormat)
|
||||
: device{device}, renderTargetPool{device, renderTargetFormat} {}
|
||||
|
||||
Device& device;
|
||||
Pool<Mesh, MeshHandle> meshPool{"Mesh"};
|
||||
RenderTargetPool renderTargetPool;
|
||||
Pool<Scene, SceneHandle> scenePool{"Scene"};
|
||||
// TexturePool textures;
|
||||
// MaterialPool materials;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,72 @@
|
||||
#include <sophia/resource_registry_ref.hpp>
|
||||
#include <sophia/util/assert.hpp>
|
||||
|
||||
#include "resources/resource_registry.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
template <>
|
||||
MeshHandle ResourceRegistryRef::load<Mesh>(const std::string& path) {
|
||||
(void)path;
|
||||
SOPHIA_ASSERT(false,
|
||||
"Mesh::load(path) is not implemented yet - no model file "
|
||||
"format is supported; build one via Mesh::Builder for now");
|
||||
return MeshHandle{};
|
||||
}
|
||||
|
||||
template <>
|
||||
void ResourceRegistryRef::release<Mesh>(MeshHandle handle) {
|
||||
registry->meshPool.release(handle);
|
||||
}
|
||||
|
||||
template <>
|
||||
void ResourceRegistryRef::release<RenderTarget>(RenderTargetHandle handle) {
|
||||
registry->renderTargetPool.release(handle);
|
||||
}
|
||||
|
||||
template <>
|
||||
void ResourceRegistryRef::release<Scene>(SceneHandle handle) {
|
||||
registry->scenePool.release(handle);
|
||||
}
|
||||
|
||||
MeshHandle ResourceRegistryRef::createResource(
|
||||
std::type_identity<Mesh>, std::vector<CanvasVertex> vertices,
|
||||
std::vector<u32> indices) {
|
||||
Mesh::Builder builder{};
|
||||
builder.vertices.reserve(vertices.size());
|
||||
for (const CanvasVertex& vertex : vertices) {
|
||||
builder.vertices.push_back({vertex.position, vertex.color});
|
||||
}
|
||||
builder.indices = std::move(indices);
|
||||
|
||||
return registry->meshPool.create(registry->device, builder);
|
||||
}
|
||||
|
||||
RenderTargetHandle ResourceRegistryRef::createResource(
|
||||
std::type_identity<RenderTarget>, u32 width, u32 height) {
|
||||
return registry->renderTargetPool.create(width, height);
|
||||
}
|
||||
|
||||
SceneHandle ResourceRegistryRef::createResource(std::type_identity<Scene>) {
|
||||
return registry->scenePool.create();
|
||||
}
|
||||
|
||||
void ResourceRegistryRef::requestResize(RenderTargetHandle handle, u32 width,
|
||||
u32 height) {
|
||||
registry->renderTargetPool.get(handle).requestResize(width, height);
|
||||
}
|
||||
|
||||
glm::vec2 ResourceRegistryRef::getExtent(RenderTargetHandle handle) const {
|
||||
vk::Extent2D extent = registry->renderTargetPool.get(handle).getExtent();
|
||||
return {static_cast<float>(extent.width), static_cast<float>(extent.height)};
|
||||
}
|
||||
|
||||
bool ResourceRegistryRef::isAlive(RenderTargetHandle handle) const {
|
||||
return registry->renderTargetPool.isAlive(handle);
|
||||
}
|
||||
|
||||
bool ResourceRegistryRef::isAlive(SceneHandle handle) const {
|
||||
return registry->scenePool.isAlive(handle);
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <sophia/resource_registry.hpp>
|
||||
#include <sophia/ui/ui.hpp>
|
||||
#include <sophia/util/assert.hpp>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user