Add offscreen render targets and rename AssetRegistry to ResourceRegistry
Add a RenderTarget resource so the scene can render offscreen instead of straight to the swapchain. The editor uses it to show the scene inside the Viewport panel. - Rename AssetRegistry to ResourceRegistry (AssetTraits -> ResourceTraits, assets() -> resources()) and move the pools from engine/src/assets to engine/src/resources. It now covers non-asset GPU resources as well. - Replace loadMesh() with create<T>(...), and add create<RenderTarget>(), requestResize(), getExtent() and isAlive(). - Add RenderTarget and RenderTargetPool with RenderTargetHandle. Resizes are deferred and applied at the start of the next frame, when the GPU is idle. - Add Application::setSceneTarget(). With no target set, the scene still renders to the swapchain. With a target set, the scene renders offscreen and the swapchain receives only the UI. Frame extent and camera projection follow the target's size. - Add sophia::UI (Application::ui()) with image() and getTextureId() to draw a render target in an ImGui window, sizing the target to the available space. ImGuiRenderer now registers the target textures for this. - ViewportPanel takes the UI and a target, and draws the scene image in place of the flat background fill. - Update the testbed, the boids example, and the glossary for the rename.
This commit is contained in:
@@ -3,14 +3,15 @@
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sophia/asset_registry.hpp>
|
||||
#include <sophia/camera_2d.hpp>
|
||||
#include <sophia/canvas.hpp>
|
||||
#include <sophia/console/console_command_registry.hpp>
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/resource_registry.hpp>
|
||||
#include <sophia/types.hpp>
|
||||
#include <sophia/ui/debug_ui.hpp>
|
||||
#include <sophia/ui/panel_registry.hpp>
|
||||
#include <sophia/ui/ui.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -44,8 +45,9 @@ class Application {
|
||||
virtual void onRender() {}
|
||||
virtual void onDestroy() {}
|
||||
|
||||
AssetRegistry& assets() { return this->assetRegistry; }
|
||||
ResourceRegistry& resources() { return this->resourceRegistry; }
|
||||
PanelRegistry& panels() { return this->panelRegistry; }
|
||||
UI& ui() { return this->_ui; }
|
||||
|
||||
DebugUI& debugUI() { return this->_debugUI; }
|
||||
|
||||
@@ -57,6 +59,15 @@ class Application {
|
||||
|
||||
void drawMesh(MeshHandle handle, const glm::mat4& transform);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* swapchain then only receives the UI. FrameInfo::currentFramebufferExtent
|
||||
* and the camera projection follow the target's size.
|
||||
*/
|
||||
void setSceneTarget(RenderTargetHandle handle);
|
||||
|
||||
private:
|
||||
friend class Canvas;
|
||||
|
||||
@@ -66,8 +77,9 @@ class Application {
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
AssetRegistry assetRegistry;
|
||||
ResourceRegistry resourceRegistry;
|
||||
PanelRegistry panelRegistry;
|
||||
UI _ui;
|
||||
|
||||
DebugUI _debugUI;
|
||||
};
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sophia/handles.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "sophia/canvas.hpp"
|
||||
#include "sophia/types.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class Device;
|
||||
class Mesh;
|
||||
|
||||
template <typename T>
|
||||
struct AssetTraits;
|
||||
|
||||
template <>
|
||||
struct AssetTraits<Mesh> {
|
||||
using Handle = MeshHandle;
|
||||
};
|
||||
|
||||
class AssetRegistry {
|
||||
public:
|
||||
~AssetRegistry();
|
||||
|
||||
AssetRegistry(const AssetRegistry&) = delete;
|
||||
AssetRegistry& operator=(const AssetRegistry&) = delete;
|
||||
|
||||
template <typename T>
|
||||
typename AssetTraits<T>::Handle load(const std::string& path);
|
||||
|
||||
/**
|
||||
* TEMPORARY: Remove once load<Mesh> can read real model files from disk.
|
||||
*/
|
||||
MeshHandle loadMesh(std::vector<CanvasVertex> vertices,
|
||||
std::vector<u32> indices);
|
||||
|
||||
template <typename T>
|
||||
void release(typename AssetTraits<T>::Handle handle);
|
||||
|
||||
private:
|
||||
friend class Application;
|
||||
|
||||
explicit AssetRegistry(Device& device);
|
||||
|
||||
template <typename T>
|
||||
const T& get(typename AssetTraits<T>::Handle handle) const;
|
||||
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
template <>
|
||||
MeshHandle AssetRegistry::load<Mesh>(const std::string& path);
|
||||
|
||||
template <>
|
||||
void AssetRegistry::release<Mesh>(MeshHandle handle);
|
||||
|
||||
template <>
|
||||
const Mesh& AssetRegistry::get<Mesh>(MeshHandle handle) const;
|
||||
|
||||
} // namespace sophia
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <sophia/asset_registry.hpp>
|
||||
#include <sophia/resource_registry.hpp>
|
||||
#include <string>
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
|
||||
@@ -26,8 +26,10 @@ struct Handle {
|
||||
|
||||
struct MeshTag {};
|
||||
struct PanelTag {};
|
||||
struct RenderTargetTag {};
|
||||
|
||||
using MeshHandle = Handle<MeshTag>;
|
||||
using PanelHandle = Handle<PanelTag>;
|
||||
using RenderTargetHandle = Handle<RenderTargetTag>;
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sophia/handles.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;
|
||||
|
||||
template <typename T>
|
||||
struct ResourceTraits;
|
||||
|
||||
template <>
|
||||
struct ResourceTraits<Mesh> {
|
||||
using Handle = MeshHandle;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ResourceTraits<RenderTarget> {
|
||||
using Handle = RenderTargetHandle;
|
||||
};
|
||||
|
||||
class ResourceRegistry {
|
||||
public:
|
||||
~ResourceRegistry();
|
||||
|
||||
ResourceRegistry(const ResourceRegistry&) = delete;
|
||||
ResourceRegistry& operator=(const ResourceRegistry&) = delete;
|
||||
|
||||
template <typename T>
|
||||
typename ResourceTraits<T>::Handle load(const std::string& path);
|
||||
|
||||
template <typename T, typename... Args>
|
||||
typename ResourceTraits<T>::Handle create(Args&&... args) {
|
||||
return createResource(std::type_identity<T>{}, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void requestResize(RenderTargetHandle handle, u32 width, u32 height);
|
||||
|
||||
glm::vec2 getExtent(RenderTargetHandle handle) const;
|
||||
|
||||
bool isAlive(RenderTargetHandle 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);
|
||||
|
||||
MeshHandle createResource(std::type_identity<Mesh>,
|
||||
std::vector<CanvasVertex> vertices,
|
||||
std::vector<u32> indices = {});
|
||||
|
||||
RenderTargetHandle createResource(std::type_identity<RenderTarget>,
|
||||
u32 width = 1, u32 height = 1);
|
||||
|
||||
RenderTarget& getRenderTarget(RenderTargetHandle handle);
|
||||
const RenderTarget& getRenderTarget(RenderTargetHandle handle) const;
|
||||
void applyPendingResizes();
|
||||
|
||||
template <typename T>
|
||||
const T& get(typename ResourceTraits<T>::Handle handle) const;
|
||||
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
template <>
|
||||
MeshHandle ResourceRegistry::load<Mesh>(const std::string& path);
|
||||
|
||||
template <>
|
||||
void ResourceRegistry::release<Mesh>(MeshHandle handle);
|
||||
|
||||
template <>
|
||||
void ResourceRegistry::release<RenderTarget>(RenderTargetHandle handle);
|
||||
|
||||
template <>
|
||||
const Mesh& ResourceRegistry::get<Mesh>(MeshHandle handle) const;
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <sophia/handles.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class ImGuiRenderer;
|
||||
class ResourceRegistry;
|
||||
|
||||
class UI {
|
||||
public:
|
||||
UI(const UI&) = delete;
|
||||
UI& operator=(const UI&) = delete;
|
||||
|
||||
/**
|
||||
* Draws the target filling the space left in the current window, and keeps
|
||||
* the target sized to match. The target catches up on the next frame, so the
|
||||
* image may be a frame behind while the window is being resized.
|
||||
*/
|
||||
void image(RenderTargetHandle handle);
|
||||
|
||||
/**
|
||||
* Texture to hand to ImGui::Image() that shows the target's contents.
|
||||
* Changes when the target is resized, so fetch it each frame rather than
|
||||
* caching it.
|
||||
*/
|
||||
ImTextureID getTextureId(RenderTargetHandle handle);
|
||||
|
||||
private:
|
||||
friend class Application;
|
||||
|
||||
explicit UI(ResourceRegistry& resources) : resources{resources} {}
|
||||
|
||||
ResourceRegistry& resources;
|
||||
ImGuiRenderer* renderer = nullptr;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
Reference in New Issue
Block a user