Add offscreen render targets and rename AssetRegistry to ResourceRegistry #8

Merged
burkkyy merged 26 commits from dev into main 2026-09-25 20:39:26 -07:00
18 changed files with 268 additions and 332 deletions
Showing only changes of commit 16f7bb7da3 - Show all commits
+31 -10
View File
@@ -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
+3 -4
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include <sophia/resource_registry.hpp>
#include <sophia/handles.hpp>
#include <string>
#define GLM_FORCE_RADIANS
+2
View File
@@ -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
@@ -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{&registry} {}
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 -3
View File
@@ -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;
};
+5 -4
View File
@@ -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);
+1 -1
View File
@@ -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
View File
@@ -1,7 +1,7 @@
#pragma once
#include <memory>
#include <sophia/resource_registry.hpp>
#include <sophia/handles.hpp>
#include <sophia/types.hpp>
#include <vector>
-58
View File
@@ -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
-35
View File
@@ -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
+90
View File
@@ -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
-104
View File
@@ -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
View File
@@ -1,4 +1,3 @@
#include <sophia/resource_registry.hpp>
#include <sophia/ui/ui.hpp>
#include <sophia/util/assert.hpp>