Compare commits
2 Commits
16f7bb7da3
...
2569eff130
| Author | SHA1 | Date | |
|---|---|---|---|
| 2569eff130 | |||
| f3c0f61eb1 |
+37
-8
@@ -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,11 +276,13 @@ 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
|
||||||
|
└── Pool<Panel, PanelHandle>
|
||||||
└── Slot { Panel, generation } → PanelHandle
|
└── Slot { Panel, generation } → PanelHandle
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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);
|
||||||
+26
-77
@@ -8,86 +8,35 @@
|
|||||||
|
|
||||||
namespace sophia {
|
namespace sophia {
|
||||||
|
|
||||||
void Image::transitionLayout(vk::CommandBuffer commandBuffer,
|
vk::ImageSubresourceRange Image::getSubresourceRange() const {
|
||||||
vk::ImageLayout newLayout) {
|
return vk::ImageSubresourceRange{this->aspectMask, 0, this->mipLevels, 0,
|
||||||
vk::ImageMemoryBarrier barrier{};
|
this->arrayLayers};
|
||||||
barrier.oldLayout = this->layout;
|
|
||||||
barrier.newLayout = newLayout;
|
|
||||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
||||||
barrier.image = this->get();
|
|
||||||
barrier.subresourceRange.aspectMask = this->aspectMask;
|
|
||||||
barrier.subresourceRange.baseMipLevel = 0;
|
|
||||||
barrier.subresourceRange.levelCount = this->mipLevels;
|
|
||||||
barrier.subresourceRange.baseArrayLayer = 0;
|
|
||||||
barrier.subresourceRange.layerCount = this->arrayLayers;
|
|
||||||
|
|
||||||
vk::PipelineStageFlags srcStage;
|
|
||||||
vk::PipelineStageFlags dstStage;
|
|
||||||
|
|
||||||
if (this->layout == vk::ImageLayout::eUndefined &&
|
|
||||||
newLayout == vk::ImageLayout::eTransferDstOptimal) {
|
|
||||||
barrier.srcAccessMask = {};
|
|
||||||
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
|
|
||||||
srcStage = vk::PipelineStageFlagBits::eTopOfPipe;
|
|
||||||
dstStage = vk::PipelineStageFlagBits::eTransfer;
|
|
||||||
} else if (this->layout == vk::ImageLayout::eTransferDstOptimal &&
|
|
||||||
newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) {
|
|
||||||
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
|
||||||
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
|
||||||
srcStage = vk::PipelineStageFlagBits::eTransfer;
|
|
||||||
dstStage = vk::PipelineStageFlagBits::eFragmentShader;
|
|
||||||
} else if (this->layout == vk::ImageLayout::eUndefined &&
|
|
||||||
newLayout == vk::ImageLayout::eDepthStencilAttachmentOptimal) {
|
|
||||||
barrier.srcAccessMask = {};
|
|
||||||
barrier.dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentRead |
|
|
||||||
vk::AccessFlagBits::eDepthStencilAttachmentWrite;
|
|
||||||
srcStage = vk::PipelineStageFlagBits::eTopOfPipe;
|
|
||||||
dstStage = vk::PipelineStageFlagBits::eEarlyFragmentTests;
|
|
||||||
} else if (this->layout == vk::ImageLayout::eUndefined &&
|
|
||||||
newLayout == vk::ImageLayout::eColorAttachmentOptimal) {
|
|
||||||
barrier.srcAccessMask = {};
|
|
||||||
barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
|
|
||||||
srcStage = vk::PipelineStageFlagBits::eTopOfPipe;
|
|
||||||
dstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
||||||
} else if (this->layout == vk::ImageLayout::eUndefined &&
|
|
||||||
newLayout == vk::ImageLayout::eGeneral) {
|
|
||||||
barrier.srcAccessMask = {};
|
|
||||||
barrier.dstAccessMask =
|
|
||||||
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
|
|
||||||
srcStage = vk::PipelineStageFlagBits::eTopOfPipe;
|
|
||||||
dstStage = vk::PipelineStageFlagBits::eComputeShader;
|
|
||||||
} else if (this->layout == vk::ImageLayout::eColorAttachmentOptimal &&
|
|
||||||
newLayout == vk::ImageLayout::ePresentSrcKHR) {
|
|
||||||
barrier.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
|
|
||||||
barrier.dstAccessMask = {};
|
|
||||||
srcStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
||||||
dstStage = vk::PipelineStageFlagBits::eBottomOfPipe;
|
|
||||||
} else if (this->layout == vk::ImageLayout::eColorAttachmentOptimal &&
|
|
||||||
newLayout == vk::ImageLayout::eShaderReadOnlyOptimal) {
|
|
||||||
barrier.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
|
|
||||||
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
|
||||||
srcStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
||||||
dstStage = vk::PipelineStageFlagBits::eFragmentShader;
|
|
||||||
} else if (this->layout == vk::ImageLayout::eShaderReadOnlyOptimal &&
|
|
||||||
newLayout == vk::ImageLayout::eColorAttachmentOptimal) {
|
|
||||||
barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
|
|
||||||
barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
|
|
||||||
srcStage = vk::PipelineStageFlagBits::eFragmentShader;
|
|
||||||
dstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
||||||
} else if (this->layout == vk::ImageLayout::ePresentSrcKHR &&
|
|
||||||
newLayout == vk::ImageLayout::eColorAttachmentOptimal) {
|
|
||||||
barrier.srcAccessMask = {};
|
|
||||||
barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
|
|
||||||
srcStage = vk::PipelineStageFlagBits::eTopOfPipe;
|
|
||||||
dstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
||||||
} else {
|
|
||||||
log::fatal("Unsupported image layout transition.");
|
|
||||||
throw std::runtime_error("Unsupported image layout transition.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commandBuffer.pipelineBarrier(srcStage, dstStage, {}, nullptr, nullptr,
|
AccessState Image::getAccessState() const {
|
||||||
|
AccessState state = getStateForLayout(this->layout);
|
||||||
|
|
||||||
|
// A wrapped (swapchain) image is handed to us by the acquire semaphore, so
|
||||||
|
// whatever we do next has to be ordered after that wait.
|
||||||
|
if (this->isExternal() && (this->layout == vk::ImageLayout::eUndefined ||
|
||||||
|
this->layout == vk::ImageLayout::ePresentSrcKHR)) {
|
||||||
|
state.stage = SWAPCHAIN_ACQUIRE_STAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::transitionLayout(vk::CommandBuffer commandBuffer,
|
||||||
|
vk::ImageLayout newLayout) {
|
||||||
|
AccessState from = this->getAccessState();
|
||||||
|
AccessState to = getStateForLayout(newLayout);
|
||||||
|
|
||||||
|
if (needsBarrier(from, to)) {
|
||||||
|
vk::ImageMemoryBarrier barrier =
|
||||||
|
makeImageBarrier(this->get(), this->getSubresourceRange(), from, to);
|
||||||
|
commandBuffer.pipelineBarrier(from.stage, to.stage, {}, nullptr, nullptr,
|
||||||
barrier);
|
barrier);
|
||||||
|
}
|
||||||
|
|
||||||
this->layout = newLayout;
|
this->layout = newLayout;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <sophia/types.hpp>
|
#include <sophia/types.hpp>
|
||||||
#include <vulkan/vulkan.hpp>
|
#include <vulkan/vulkan.hpp>
|
||||||
|
|
||||||
|
#include "rendering/access.hpp"
|
||||||
|
|
||||||
namespace sophia {
|
namespace sophia {
|
||||||
|
|
||||||
class Device;
|
class Device;
|
||||||
@@ -28,6 +30,20 @@ class Image {
|
|||||||
vk::Format getFormat() const { return this->format; }
|
vk::Format getFormat() const { return this->format; }
|
||||||
vk::Extent3D getExtent() const { return this->extent; }
|
vk::Extent3D getExtent() const { return this->extent; }
|
||||||
vk::ImageLayout getLayout() const { return this->layout; }
|
vk::ImageLayout getLayout() const { return this->layout; }
|
||||||
|
vk::ImageSubresourceRange getSubresourceRange() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The layout this image was last left in, plus the stages and accesses that
|
||||||
|
* a barrier out of it has to wait on.
|
||||||
|
*/
|
||||||
|
AccessState getAccessState() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the tracked layout without recording a barrier. For code that
|
||||||
|
* does its own transitions (the RenderGraph) and needs to hand the final
|
||||||
|
* layout back.
|
||||||
|
*/
|
||||||
|
void recordLayout(vk::ImageLayout newLayout) { this->layout = newLayout; }
|
||||||
|
|
||||||
void transitionLayout(vk::CommandBuffer commandBuffer,
|
void transitionLayout(vk::CommandBuffer commandBuffer,
|
||||||
vk::ImageLayout newLayout);
|
vk::ImageLayout newLayout);
|
||||||
|
|||||||
@@ -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{};
|
||||||
|
|||||||
@@ -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(); }
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
#include "rendering/access.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <sophia/util/logger.hpp>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace sophia {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr std::array<Access, 7> ALL_ACCESSES = {
|
||||||
|
Access::ColorWrite, Access::DepthWrite, Access::SampledRead,
|
||||||
|
Access::StorageReadWrite, Access::TransferSrc, Access::TransferDst,
|
||||||
|
Access::Present,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr vk::AccessFlags WRITE_ACCESSES =
|
||||||
|
vk::AccessFlagBits::eColorAttachmentWrite |
|
||||||
|
vk::AccessFlagBits::eDepthStencilAttachmentWrite |
|
||||||
|
vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eTransferWrite;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
AccessState getAccessState(Access access) {
|
||||||
|
switch (access) {
|
||||||
|
case Access::ColorWrite:
|
||||||
|
return {vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
|
vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||||
|
vk::AccessFlagBits::eColorAttachmentRead |
|
||||||
|
vk::AccessFlagBits::eColorAttachmentWrite};
|
||||||
|
case Access::DepthWrite:
|
||||||
|
return {vk::ImageLayout::eDepthStencilAttachmentOptimal,
|
||||||
|
vk::PipelineStageFlagBits::eEarlyFragmentTests |
|
||||||
|
vk::PipelineStageFlagBits::eLateFragmentTests,
|
||||||
|
vk::AccessFlagBits::eDepthStencilAttachmentRead |
|
||||||
|
vk::AccessFlagBits::eDepthStencilAttachmentWrite};
|
||||||
|
case Access::SampledRead:
|
||||||
|
return {vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
|
vk::PipelineStageFlagBits::eFragmentShader,
|
||||||
|
vk::AccessFlagBits::eShaderRead};
|
||||||
|
case Access::StorageReadWrite:
|
||||||
|
return {
|
||||||
|
vk::ImageLayout::eGeneral, vk::PipelineStageFlagBits::eComputeShader,
|
||||||
|
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite};
|
||||||
|
case Access::TransferSrc:
|
||||||
|
return {vk::ImageLayout::eTransferSrcOptimal,
|
||||||
|
vk::PipelineStageFlagBits::eTransfer,
|
||||||
|
vk::AccessFlagBits::eTransferRead};
|
||||||
|
case Access::TransferDst:
|
||||||
|
return {vk::ImageLayout::eTransferDstOptimal,
|
||||||
|
vk::PipelineStageFlagBits::eTransfer,
|
||||||
|
vk::AccessFlagBits::eTransferWrite};
|
||||||
|
case Access::Present:
|
||||||
|
return {vk::ImageLayout::ePresentSrcKHR,
|
||||||
|
vk::PipelineStageFlagBits::eBottomOfPipe, vk::AccessFlags{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
log::fatal("Unknown Access value.");
|
||||||
|
throw std::runtime_error("Unknown Access value.");
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessState getStateForLayout(vk::ImageLayout layout) {
|
||||||
|
if (layout == vk::ImageLayout::eUndefined) {
|
||||||
|
return AccessState{};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Access access : ALL_ACCESSES) {
|
||||||
|
AccessState state = getAccessState(access);
|
||||||
|
if (state.layout == layout) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::fatal("Unsupported image layout:", vk::to_string(layout));
|
||||||
|
throw std::runtime_error("Unsupported image layout.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool writesImage(const AccessState& state) {
|
||||||
|
return static_cast<bool>(state.access & WRITE_ACCESSES);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool needsBarrier(const AccessState& from, const AccessState& to) {
|
||||||
|
return from.layout != to.layout || writesImage(from) || writesImage(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ImageMemoryBarrier makeImageBarrier(vk::Image image,
|
||||||
|
const vk::ImageSubresourceRange& range,
|
||||||
|
const AccessState& from,
|
||||||
|
const AccessState& to) {
|
||||||
|
vk::ImageMemoryBarrier barrier{};
|
||||||
|
barrier.oldLayout = from.layout;
|
||||||
|
barrier.newLayout = to.layout;
|
||||||
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.image = image;
|
||||||
|
barrier.subresourceRange = range;
|
||||||
|
// Only writes need to be made available; reads just need the execution
|
||||||
|
// dependency the stage masks already give.
|
||||||
|
barrier.srcAccessMask = from.access & WRITE_ACCESSES;
|
||||||
|
barrier.dstAccessMask = to.access;
|
||||||
|
return barrier;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sophia
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
|
||||||
|
namespace sophia {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How an image is used at a point in a frame. Each value implies an image
|
||||||
|
* layout plus the pipeline stages and memory accesses that go with it, so a
|
||||||
|
* barrier between any two of them can be derived instead of hand-written.
|
||||||
|
*/
|
||||||
|
enum class Access {
|
||||||
|
ColorWrite,
|
||||||
|
DepthWrite,
|
||||||
|
SampledRead,
|
||||||
|
StorageReadWrite,
|
||||||
|
TransferSrc,
|
||||||
|
TransferDst,
|
||||||
|
Present,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The layout, stages and accesses an image is in after some use. Barriers go
|
||||||
|
* from one of these to another.
|
||||||
|
*/
|
||||||
|
struct AccessState {
|
||||||
|
vk::ImageLayout layout = vk::ImageLayout::eUndefined;
|
||||||
|
vk::PipelineStageFlags stage = vk::PipelineStageFlagBits::eTopOfPipe;
|
||||||
|
vk::AccessFlags access;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stage RenderContext waits on for the swapchain image-available
|
||||||
|
* semaphore. A barrier that takes a swapchain image out of its
|
||||||
|
* just-acquired state has to use this as its source stage, otherwise the
|
||||||
|
* layout transition isn't ordered after the acquire.
|
||||||
|
*/
|
||||||
|
inline constexpr vk::PipelineStageFlags SWAPCHAIN_ACQUIRE_STAGE =
|
||||||
|
vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||||||
|
|
||||||
|
AccessState getAccessState(Access access);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of an image sitting in `layout` with no outstanding work.
|
||||||
|
* eUndefined maps to "nothing to wait on"; layouts with no matching Access
|
||||||
|
* throw.
|
||||||
|
*/
|
||||||
|
AccessState getStateForLayout(vk::ImageLayout layout);
|
||||||
|
|
||||||
|
bool writesImage(const AccessState& state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether going from `from` to `to` needs a barrier: a layout change, or any
|
||||||
|
* hazard involving a write. Read-after-read in the same layout doesn't.
|
||||||
|
*/
|
||||||
|
bool needsBarrier(const AccessState& from, const AccessState& to);
|
||||||
|
|
||||||
|
vk::ImageMemoryBarrier makeImageBarrier(vk::Image image,
|
||||||
|
const vk::ImageSubresourceRange& range,
|
||||||
|
const AccessState& from,
|
||||||
|
const AccessState& to);
|
||||||
|
|
||||||
|
} // namespace sophia
|
||||||
@@ -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,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,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 {
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <entt/entt.hpp>
|
|
||||||
|
|
||||||
namespace sophia {
|
|
||||||
|
|
||||||
class Scene {
|
|
||||||
public:
|
|
||||||
Scene();
|
|
||||||
~Scene();
|
|
||||||
|
|
||||||
private:
|
|
||||||
entt::registry registry;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace sophia
|
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user