Big changes

This commit is contained in:
2026-09-21 00:26:50 -07:00
parent 16f7bb7da3
commit f3c0f61eb1
16 changed files with 170 additions and 187 deletions
+39 -10
View File
@@ -12,16 +12,15 @@ pool in a thin class instead:
``` ```
ResourceRegistry ResourceRegistry
├── meshes Pool<Mesh, MeshHandle> ├── meshPool Pool<Mesh, MeshHandle>
├── renderTargets RenderTargetPool → RenderTargetHandle ├── renderTargetPool RenderTargetPool → RenderTargetHandle
│ (wraps Pool; waits for the GPU on release, applies resizes) │ (wraps Pool; waits for the GPU on release, applies resizes)
├── scenes Pool<Scene, SceneHandle> ├── texturePool (planned)
├── textures (planned) └── materialPool (planned)
└── materials (planned)
``` ```
Engine code (`Application`, `ImGuiRenderer`, `SceneRenderer`) uses the pools Engine code (`Application`, `ImGuiRenderer`, `SceneRenderer`) uses the pools
directly, e.g. `resources.renderTargets.get(handle)`. directly, e.g. `resources.renderTargetPool.get(handle)`.
A Handle is a small (index, generation) pair. The index locates the slot in 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 the pool; the generation is bumped when that slot is released, so a Handle
@@ -277,12 +276,14 @@ This is primarily a UI concept, rather than a GPU concept.
A PanelRegistry owns Panels on the caller's behalf and hands out lightweight A PanelRegistry owns Panels on the caller's behalf and hands out lightweight
PanelHandles instead of pointers or ownership. PanelHandles instead of pointers or ownership.
Internally it's a generation-checked slot pool, the same shape ResourceRegistry's Internally it wraps a `Pool<Panel, PanelHandle>` (the same generation-checked
`Pool<T, Handle>` uses: slot pool ResourceRegistry uses, in `sophia/util/pool.hpp`) and adds the
Panel-specific parts: lookup by type or name, and the default dock layout:
``` ```
PanelRegistry PanelRegistry
└── Slot { Panel, generation } → PanelHandle └── Pool<Panel, PanelHandle>
└── Slot { Panel, generation } → PanelHandle
``` ```
Typical lifecycle: Typical lifecycle:
@@ -300,6 +301,34 @@ Each frame, update(frameInfo) calls onUpdate on every visible Panel it holds.
Think: "Where do I register or look up a UI panel by handle, without owning Think: "Where do I register or look up a UI panel by handle, without owning
its lifetime directly?" its lifetime directly?"
## SceneRegistry
A SceneRegistry owns Scenes on the caller's behalf and hands out lightweight
SceneHandles instead of pointers or ownership. Like PanelRegistry, it is owned
by Application and reached from frontend code directly (`scenes()`), rather than
through ResourceRegistryRef: scenes are CPU-side entity data, not GPU resources.
Internally it wraps a `Pool<Scene, SceneHandle>`:
```
SceneRegistry
└── Pool<Scene, SceneHandle>
└── Slot { Scene, generation } → SceneHandle
```
Typical lifecycle:
```
create() → SceneHandle
│
get(handle) → Scene&
│
release(handle)
```
Think: "Where do I create or look up a Scene by handle, without owning its
lifetime directly?"
## Sprite ## Sprite
A Sprite is a 2D renderable representation of an image, usually a rectangular region of a texture. A Sprite is a 2D renderable representation of an image, usually a rectangular region of a texture.
+4
View File
@@ -8,6 +8,7 @@
#include <sophia/console/console_command_registry.hpp> #include <sophia/console/console_command_registry.hpp>
#include <sophia/frame_info.hpp> #include <sophia/frame_info.hpp>
#include <sophia/resource_registry_ref.hpp> #include <sophia/resource_registry_ref.hpp>
#include <sophia/scene_registry.hpp>
#include <sophia/types.hpp> #include <sophia/types.hpp>
#include <sophia/ui/debug_ui.hpp> #include <sophia/ui/debug_ui.hpp>
#include <sophia/ui/panel_registry.hpp> #include <sophia/ui/panel_registry.hpp>
@@ -47,6 +48,7 @@ class Application {
ResourceRegistryRef resources(); ResourceRegistryRef resources();
PanelRegistry& panels() { return this->panelRegistry; } PanelRegistry& panels() { return this->panelRegistry; }
SceneRegistry& scenes() { return this->sceneRegistry; }
UI& ui() { return this->_ui; } UI& ui() { return this->_ui; }
DebugUI& debugUI() { return this->_debugUI; } DebugUI& debugUI() { return this->_debugUI; }
@@ -60,6 +62,7 @@ class Application {
void drawMesh(MeshHandle handle, const glm::mat4& transform); void drawMesh(MeshHandle handle, const glm::mat4& transform);
/** /**
* !TEMP !TODO remove this func eventually
* Chooses where the scene is rendered. An invalid handle (the default) means * Chooses where the scene is rendered. An invalid handle (the default) means
* the swapchain; a target created with * the swapchain; a target created with
* ResourceRegistryRef::create<RenderTarget>() redirects it offscreen, and the * ResourceRegistryRef::create<RenderTarget>() redirects it offscreen, and the
@@ -78,6 +81,7 @@ class Application {
std::unique_ptr<Impl> impl; std::unique_ptr<Impl> impl;
PanelRegistry panelRegistry; PanelRegistry panelRegistry;
SceneRegistry sceneRegistry;
UI _ui; UI _ui;
DebugUI _debugUI; DebugUI _debugUI;
@@ -16,7 +16,6 @@ namespace sophia {
class Mesh; class Mesh;
class RenderTarget; class RenderTarget;
class Scene;
class ResourceRegistry; class ResourceRegistry;
template <typename T> template <typename T>
@@ -32,11 +31,6 @@ struct ResourceTraits<RenderTarget> {
using Handle = RenderTargetHandle; using Handle = RenderTargetHandle;
}; };
template <>
struct ResourceTraits<Scene> {
using Handle = SceneHandle;
};
class ResourceRegistryRef { class ResourceRegistryRef {
public: public:
template <typename T> template <typename T>
@@ -52,7 +46,6 @@ class ResourceRegistryRef {
glm::vec2 getExtent(RenderTargetHandle handle) const; glm::vec2 getExtent(RenderTargetHandle handle) const;
bool isAlive(RenderTargetHandle handle) const; bool isAlive(RenderTargetHandle handle) const;
bool isAlive(SceneHandle handle) const;
template <typename T> template <typename T>
void release(typename ResourceTraits<T>::Handle handle); void release(typename ResourceTraits<T>::Handle handle);
@@ -70,8 +63,6 @@ class ResourceRegistryRef {
RenderTargetHandle createResource(std::type_identity<RenderTarget>, RenderTargetHandle createResource(std::type_identity<RenderTarget>,
u32 width = 1, u32 height = 1); u32 width = 1, u32 height = 1);
SceneHandle createResource(std::type_identity<Scene>);
ResourceRegistry* registry; ResourceRegistry* registry;
}; };
@@ -84,7 +75,4 @@ void ResourceRegistryRef::release<Mesh>(MeshHandle handle);
template <> template <>
void ResourceRegistryRef::release<RenderTarget>(RenderTargetHandle handle); void ResourceRegistryRef::release<RenderTarget>(RenderTargetHandle handle);
template <>
void ResourceRegistryRef::release<Scene>(SceneHandle handle);
} // namespace sophia } // namespace sophia
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <entt/entt.hpp>
namespace sophia {
class Scene {
public:
Scene();
~Scene();
// The camera a render request uses when it doesn't name one. Null until set.
entt::entity getActiveCamera() const { return activeCamera; }
void setActiveCamera(entt::entity camera) { activeCamera = camera; }
private:
entt::registry registry;
entt::entity activeCamera = entt::null;
};
} // namespace sophia
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include <sophia/handles.hpp>
#include <sophia/scene.hpp>
#include <sophia/util/pool.hpp>
namespace sophia {
class SceneRegistry {
public:
SceneRegistry(const SceneRegistry&) = delete;
SceneRegistry& operator=(const SceneRegistry&) = delete;
SceneRegistry() = default;
~SceneRegistry() = default;
SceneHandle create() { return pool.create(); }
const Scene& get(SceneHandle handle) const { return pool.get(handle); }
Scene& get(SceneHandle handle) { return pool.get(handle); }
void release(SceneHandle handle) { pool.release(handle); }
bool isAlive(SceneHandle handle) const { return pool.isAlive(handle); }
u32 size() const { return pool.size(); }
private:
Pool<Scene, SceneHandle> pool{"Scene"};
};
} // namespace sophia
+21 -35
View File
@@ -6,6 +6,7 @@
#include <sophia/ui/dock_layout.hpp> #include <sophia/ui/dock_layout.hpp>
#include <sophia/ui/panel.hpp> #include <sophia/ui/panel.hpp>
#include <sophia/util/assert.hpp> #include <sophia/util/assert.hpp>
#include <sophia/util/pool.hpp>
#include <string_view> #include <string_view>
#include <typeinfo> #include <typeinfo>
#include <utility> #include <utility>
@@ -21,17 +22,17 @@ class PanelRegistry {
PanelRegistry() = default; PanelRegistry() = default;
~PanelRegistry() = default; ~PanelRegistry() = default;
PanelHandle add(std::unique_ptr<Panel> panel); PanelHandle add(std::unique_ptr<Panel> panel) {
return pool.add(std::move(panel));
}
template <typename T, typename... Args> template <typename T, typename... Args>
PanelHandle emplace(Args&&... args) { PanelHandle emplace(Args&&... args) {
return add(std::make_unique<T>(std::forward<Args>(args)...)); return add(std::make_unique<T>(std::forward<Args>(args)...));
} }
const Panel& get(PanelHandle handle) const; const Panel& get(PanelHandle handle) const { return pool.get(handle); }
Panel& get(PanelHandle handle) { Panel& get(PanelHandle handle) { return pool.get(handle); }
return const_cast<Panel&>(std::as_const(*this).get(handle));
}
template <typename T> template <typename T>
const T& get(PanelHandle handle) const { const T& get(PanelHandle handle) const {
@@ -46,19 +47,17 @@ class PanelRegistry {
return const_cast<T&>(std::as_const(*this).template get<T>(handle)); return const_cast<T&>(std::as_const(*this).template get<T>(handle));
} }
void remove(PanelHandle handle); void remove(PanelHandle handle) { pool.release(handle); }
bool isValid(PanelHandle handle) const; bool isValid(PanelHandle handle) const { return pool.isAlive(handle); }
template <typename T> template <typename T>
const T* find() const { const T* find() const {
for (const Slot& slot : slots) { const T* result = nullptr;
if (slot.panel) { pool.findIf([&result](const Panel& panel) {
if (const T* panel = dynamic_cast<const T*>(slot.panel.get())) { result = dynamic_cast<const T*>(&panel);
return panel; return result != nullptr;
} });
} return result;
}
return nullptr;
} }
template <typename T> template <typename T>
@@ -67,29 +66,22 @@ class PanelRegistry {
} }
const Panel* find(std::string_view name) const { const Panel* find(std::string_view name) const {
for (const Slot& slot : slots) { return pool.findIf(
if (slot.panel && slot.panel->getName() == name) { [name](const Panel& panel) { return panel.getName() == name; });
return slot.panel.get();
}
}
return nullptr;
} }
Panel* find(std::string_view name) { Panel* find(std::string_view name) {
return const_cast<Panel*>(std::as_const(*this).find(name)); return const_cast<Panel*>(std::as_const(*this).find(name));
} }
u32 size() const; u32 size() const { return pool.size(); }
u32 visibleCount() const; u32 visibleCount() const;
std::vector<std::string_view> names() const { std::vector<std::string_view> names() const {
std::vector<std::string_view> result; std::vector<std::string_view> result;
result.reserve(slots.size()); result.reserve(pool.size());
for (const Slot& slot : slots) { pool.forEach(
if (slot.panel) { [&result](const Panel& panel) { result.push_back(panel.getName()); });
result.push_back(slot.panel->getName());
}
}
return result; return result;
} }
@@ -101,13 +93,7 @@ class PanelRegistry {
const DockLayout& getDefaultLayout() const { return defaultLayout; } const DockLayout& getDefaultLayout() const { return defaultLayout; }
private: private:
struct Slot { Pool<Panel, PanelHandle> pool{"Panel"};
std::unique_ptr<Panel> panel;
u32 generation = 0;
};
std::vector<Slot> slots;
std::vector<u32> freeList;
DockLayout defaultLayout; DockLayout defaultLayout;
}; };
@@ -8,7 +8,7 @@
namespace sophia { namespace sophia {
template <typename T, typename HandleT> template <typename T, typename HandleType>
class Pool { class Pool {
public: public:
explicit Pool(const char* name) : name{name} {} explicit Pool(const char* name) : name{name} {}
@@ -17,27 +17,29 @@ class Pool {
Pool& operator=(const Pool&) = delete; Pool& operator=(const Pool&) = delete;
template <typename... Args> template <typename... Args>
HandleT create(Args&&... args) { HandleType create(Args&&... args) {
auto item = std::make_unique<T>(std::forward<Args>(args)...); return add(std::make_unique<T>(std::forward<Args>(args)...));
}
HandleType add(std::unique_ptr<T> item) {
if (!freeList.empty()) { if (!freeList.empty()) {
u32 index = freeList.back(); u32 index = freeList.back();
freeList.pop_back(); freeList.pop_back();
slots[index].item = std::move(item); slots[index].item = std::move(item);
return HandleT{index, slots[index].generation}; return HandleType{index, slots[index].generation};
} }
slots.push_back(Slot{std::move(item), 0}); slots.push_back(Slot{std::move(item), 0});
return HandleT{static_cast<u32>(slots.size() - 1), 0}; return HandleType{static_cast<u32>(slots.size() - 1), 0};
} }
T& get(HandleT handle) { T& get(HandleType handle) {
return const_cast<T&>(std::as_const(*this).get(handle)); return const_cast<T&>(std::as_const(*this).get(handle));
} }
const T& get(HandleT handle) const { return *validate(handle).item; } const T& get(HandleType handle) const { return *validate(handle).item; }
void release(HandleT handle) { void release(HandleType handle) {
validate(handle); validate(handle);
Slot& slot = slots[handle.index]; Slot& slot = slots[handle.index];
@@ -46,12 +48,14 @@ class Pool {
freeList.push_back(handle.index); freeList.push_back(handle.index);
} }
bool isAlive(HandleT handle) const { bool isAlive(HandleType handle) const {
return handle.isValid() && handle.index < slots.size() && return handle.isValid() && handle.index < slots.size() &&
slots[handle.index].generation == handle.generation && slots[handle.index].generation == handle.generation &&
slots[handle.index].item != nullptr; slots[handle.index].item != nullptr;
} }
u32 size() const { return static_cast<u32>(slots.size() - freeList.size()); }
template <typename Fn> template <typename Fn>
void forEach(Fn&& fn) { void forEach(Fn&& fn) {
for (Slot& slot : slots) { for (Slot& slot : slots) {
@@ -61,6 +65,31 @@ class Pool {
} }
} }
template <typename Fn>
void forEach(Fn&& fn) const {
for (const Slot& slot : slots) {
if (slot.item) {
fn(*slot.item);
}
}
}
template <typename Predicate>
T* findIf(Predicate&& predicate) {
return const_cast<T*>(
std::as_const(*this).findIf(std::forward<Predicate>(predicate)));
}
template <typename Predicate>
const T* findIf(Predicate&& predicate) const {
for (const Slot& slot : slots) {
if (slot.item && predicate(*slot.item)) {
return slot.item.get();
}
}
return nullptr;
}
private: private:
static constexpr u32 GENERATION_MASK = (1u << 12) - 1; static constexpr u32 GENERATION_MASK = (1u << 12) - 1;
@@ -69,7 +98,7 @@ class Pool {
u32 generation = 0; u32 generation = 0;
}; };
const Slot& validate(HandleT handle) const { const Slot& validate(HandleType handle) const {
SOPHIA_ASSERT(handle.isValid(), "invalid handle:", name); SOPHIA_ASSERT(handle.isValid(), "invalid handle:", name);
SOPHIA_ASSERT(handle.index < slots.size(), name, SOPHIA_ASSERT(handle.index < slots.size(), name,
"handle index out of range:", handle.index); "handle index out of range:", handle.index);
+3 -2
View File
@@ -5,6 +5,8 @@
#include <sophia/util/logger.hpp> #include <sophia/util/logger.hpp>
#include <stdexcept> #include <stdexcept>
#include "rendering/access.hpp"
namespace sophia { namespace sophia {
RenderContext::RenderContext(Window& window, Device& device) RenderContext::RenderContext(Window& window, Device& device)
@@ -80,8 +82,7 @@ void RenderContext::endFrame() {
vk::Semaphore waitSemaphore = frame.imageAvailable.get(); vk::Semaphore waitSemaphore = frame.imageAvailable.get();
vk::Semaphore signalSemaphore = vk::Semaphore signalSemaphore =
this->renderFinishedSemaphores.at(this->currentImageIndex).get(); this->renderFinishedSemaphores.at(this->currentImageIndex).get();
vk::PipelineStageFlags waitStage = vk::PipelineStageFlags waitStage = SWAPCHAIN_ACQUIRE_STAGE;
vk::PipelineStageFlagBits::eColorAttachmentOutput;
vk::CommandBuffer commandBuffer = frame.commandBuffer; vk::CommandBuffer commandBuffer = frame.commandBuffer;
vk::SubmitInfo submitInfo{}; vk::SubmitInfo submitInfo{};
+2 -8
View File
@@ -31,14 +31,8 @@ class RenderContext {
vk::CommandBuffer beginFrame(); vk::CommandBuffer beginFrame();
void endFrame(); void endFrame();
void transitionCurrentImage(vk::CommandBuffer commandBuffer, Image& getCurrentImage() const {
vk::ImageLayout newLayout) const { return this->swapchain->getImage(static_cast<int>(this->currentImageIndex));
this->swapchain->getImage(static_cast<int>(this->currentImageIndex))
.transitionLayout(commandBuffer, newLayout);
}
vk::ImageView getCurrentImageView() const {
return this->swapchain->getImageView(
static_cast<int>(this->currentImageIndex));
} }
vk::Extent2D getExtent() const { return this->swapchain->getExtent(); } vk::Extent2D getExtent() const { return this->swapchain->getExtent(); }
@@ -1,10 +1,10 @@
#pragma once #pragma once
#include <sophia/handles.hpp> #include <sophia/handles.hpp>
#include <sophia/util/pool.hpp>
#include "device.hpp" #include "device.hpp"
#include "render_target.hpp" #include "render_target.hpp"
#include "resources/pools/pool.hpp"
namespace sophia { namespace sophia {
+1 -3
View File
@@ -1,12 +1,11 @@
#pragma once #pragma once
#include <sophia/handles.hpp> #include <sophia/handles.hpp>
#include <sophia/util/pool.hpp>
#include "device.hpp" #include "device.hpp"
#include "mesh.hpp" #include "mesh.hpp"
#include "resources/pools/pool.hpp"
#include "resources/pools/render_target_pool.hpp" #include "resources/pools/render_target_pool.hpp"
#include "scene.hpp"
namespace sophia { namespace sophia {
@@ -21,7 +20,6 @@ class ResourceRegistry {
Device& device; Device& device;
Pool<Mesh, MeshHandle> meshPool{"Mesh"}; Pool<Mesh, MeshHandle> meshPool{"Mesh"};
RenderTargetPool renderTargetPool; RenderTargetPool renderTargetPool;
Pool<Scene, SceneHandle> scenePool{"Scene"};
// TexturePool textures; // TexturePool textures;
// MaterialPool materials; // MaterialPool materials;
}; };
@@ -24,11 +24,6 @@ void ResourceRegistryRef::release<RenderTarget>(RenderTargetHandle handle) {
registry->renderTargetPool.release(handle); registry->renderTargetPool.release(handle);
} }
template <>
void ResourceRegistryRef::release<Scene>(SceneHandle handle) {
registry->scenePool.release(handle);
}
MeshHandle ResourceRegistryRef::createResource( MeshHandle ResourceRegistryRef::createResource(
std::type_identity<Mesh>, std::vector<CanvasVertex> vertices, std::type_identity<Mesh>, std::vector<CanvasVertex> vertices,
std::vector<u32> indices) { std::vector<u32> indices) {
@@ -47,10 +42,6 @@ RenderTargetHandle ResourceRegistryRef::createResource(
return registry->renderTargetPool.create(width, 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, void ResourceRegistryRef::requestResize(RenderTargetHandle handle, u32 width,
u32 height) { u32 height) {
registry->renderTargetPool.get(handle).requestResize(width, height); registry->renderTargetPool.get(handle).requestResize(width, height);
@@ -65,8 +56,4 @@ bool ResourceRegistryRef::isAlive(RenderTargetHandle handle) const {
return registry->renderTargetPool.isAlive(handle); return registry->renderTargetPool.isAlive(handle);
} }
bool ResourceRegistryRef::isAlive(SceneHandle handle) const {
return registry->scenePool.isAlive(handle);
}
} // namespace sophia } // namespace sophia
+1 -2
View File
@@ -1,6 +1,5 @@
#include "scene.hpp"
#include <sophia/components.hpp> #include <sophia/components.hpp>
#include <sophia/scene.hpp>
#include <sophia/util/logger.hpp> #include <sophia/util/logger.hpp>
namespace sophia { namespace sophia {
-16
View File
@@ -1,16 +0,0 @@
#pragma once
#include <entt/entt.hpp>
namespace sophia {
class Scene {
public:
Scene();
~Scene();
private:
entt::registry registry;
};
} // namespace sophia
-3
View File
@@ -23,9 +23,6 @@ class Swapchain {
Image& getImage(int index) const { return *this->images.at(index); } Image& getImage(int index) const { return *this->images.at(index); }
Image& getDepthImage(int index) const { return *this->depthImages.at(index); } Image& getDepthImage(int index) const { return *this->depthImages.at(index); }
vk::ImageView getImageView(int index) const {
return this->images.at(index)->getView();
}
vk::ImageView getDepthImageView(int index) const { vk::ImageView getDepthImageView(int index) const {
return this->depthImages.at(index)->getView(); return this->depthImages.at(index)->getView();
} }
+7 -72
View File
@@ -1,88 +1,23 @@
#include <sophia/ui/panel_registry.hpp> #include <sophia/ui/panel_registry.hpp>
#include <sophia/util/assert.hpp>
namespace sophia { namespace sophia {
namespace {
constexpr u32 GENERATION_MASK = (1u << 12) - 1;
} // namespace
PanelHandle PanelRegistry::add(std::unique_ptr<Panel> panel) {
if (!freeList.empty()) {
u32 index = freeList.back();
freeList.pop_back();
slots[index].panel = std::move(panel);
return PanelHandle{index, slots[index].generation};
}
slots.push_back(Slot{std::move(panel), 0});
return PanelHandle{static_cast<u32>(slots.size() - 1), 0};
}
const Panel& PanelRegistry::get(PanelHandle handle) const {
SOPHIA_ASSERT(handle.isValid(), "invalid PanelHandle");
SOPHIA_ASSERT(handle.index < slots.size(),
"PanelHandle index out of range:", handle.index);
const Slot& slot = slots[handle.index];
SOPHIA_ASSERT(slot.generation == handle.generation,
"stale PanelHandle (generation mismatch):", handle.index);
SOPHIA_ASSERT(slot.panel != nullptr,
"PanelHandle refers to a removed panel:", handle.index);
return *slot.panel;
}
void PanelRegistry::remove(PanelHandle handle) {
SOPHIA_ASSERT(handle.isValid(), "invalid PanelHandle");
SOPHIA_ASSERT(handle.index < slots.size(),
"PanelHandle index out of range:", handle.index);
Slot& slot = slots[handle.index];
SOPHIA_ASSERT(slot.generation == handle.generation,
"stale PanelHandle (generation mismatch):", handle.index);
SOPHIA_ASSERT(slot.panel != nullptr,
"PanelHandle already removed:", handle.index);
slot.panel.reset();
slot.generation = (slot.generation + 1) & GENERATION_MASK;
freeList.push_back(handle.index);
}
bool PanelRegistry::isValid(PanelHandle handle) const {
if (!handle.isValid() || handle.index >= slots.size()) {
return false;
}
const Slot& slot = slots[handle.index];
return slot.generation == handle.generation && slot.panel != nullptr;
}
u32 PanelRegistry::size() const {
u32 count = 0;
for (const Slot& slot : slots) {
if (slot.panel) {
count++;
}
}
return count;
}
u32 PanelRegistry::visibleCount() const { u32 PanelRegistry::visibleCount() const {
u32 count = 0; u32 count = 0;
for (const Slot& slot : slots) { pool.forEach([&count](const Panel& panel) {
if (slot.panel && slot.panel->isVisible()) { if (panel.isVisible()) {
count++; count++;
} }
} });
return count; return count;
} }
void PanelRegistry::update(const FrameInfo& frameInfo) { void PanelRegistry::update(const FrameInfo& frameInfo) {
for (auto& slot : slots) { pool.forEach([&frameInfo](Panel& panel) {
if (slot.panel && slot.panel->isVisible()) { if (panel.isVisible()) {
slot.panel->draw(frameInfo); panel.draw(frameInfo);
} }
} });
} }
} // namespace sophia } // namespace sophia