5#if defined(EMSCRIPTEN) && !defined(COGS_SINGLETHREADED)
6#include <emscripten/threading.h>
7#include <emscripten/proxying.h>
11#include "EngineSystems.h"
12#include "EntityStore.h"
13#include "ExtensionRegistry.h"
14#include "ResourceManifest.h"
16#include "Editor/IEditor.h"
18#include "Platform/Instrumentation.h"
20#include "Resources/AnimationManager.h"
21#include "Resources/AssetManager.h"
22#include "Resources/BufferManager.h"
23#include "Resources/BasicBlueNoiseManager.h"
24#include "Resources/EffectManager.h"
25#include "Resources/FontManager.h"
26#include "Resources/MaterialManager.h"
27#include "Resources/MeshManager.h"
28#include "Resources/ModelManager.h"
29#include "Resources/ResourceStore.h"
30#include "Resources/TextureManager.h"
32#include "Renderer/IRenderer.h"
34#include "Services/Variables.h"
35#include "Services/Time.h"
36#include "Services/QualityService.h"
37#include "Services/ResourceUsageLogger.h"
39#include "Systems/Core/TransformSystem.h"
41#include "Foundation/Logging/Logger.h"
49 struct SystemDefinition
55 template<
typename ResourceManagerType>
56 ResourceManagerType* createResourceManager(
Engine* engine,
Context* context)
58 auto manager = std::make_unique<ResourceManagerType>(context);
60 auto ptr = manager.get();
73 std::vector<SystemDefinition> systems;
77 std::deque<std::function<void()>> tasks;
87#ifdef COGS_SINGLETHREADED
88 workParallelValue =
false;
90 workParallelValue =
true;
105 for (
auto & system :data->systems) {
106 system.system->cleanup(context);
108 Memory::destroy<ComponentSystemBase>(system.system, context->
memory->baseAllocator);
111 data->systems.clear();
115 auto globalQuota = context->
variables->get(
"resources.globalFrameUpdateQuota", 1000);
116 context->
variables->set(
"resources.globalFrameUpdateQuota", 0);
119 if (context->materialManager) {
120 context->materialManager->releaseAll();
123 for (
int i = 0; i < 4; ++i) {
131 context->bufferManager =
nullptr;
132 context->textureManager =
nullptr;
133 context->blueNoiseManager =
nullptr;
134 context->animationManager =
nullptr;
135 context->meshManager =
nullptr;
136 context->modelManager =
nullptr;
137 context->effectManager =
nullptr;
138 context->materialManager =
nullptr;
139 context->materialDefinitionManager =
nullptr;
140 context->materialInstanceManager =
nullptr;
141 context->fontManager =
nullptr;
142 context->assetManager =
nullptr;
143 resourceManagers.
clear();
145 context->
variables->set(
"resources.globalFrameUpdateQuota", globalQuota);
151void Cogs::Core::Engine::runTaskInMainThread(std::function<
void()>&& task)
154 Cogs::LockGuard guard(data->mainThreadTasks.lock);
155 data->mainThreadTasks.tasks.emplace_back(std::move(task));
160#if defined(EMSCRIPTEN) && !defined(COGS_SINGLETHREADED)
164 static pthread_t mainThread = emscripten_main_runtime_thread_id();
165 if (mainThread == pthread_self()) {
169 emscripten_proxy_async(emscripten_proxy_get_system_queue(), mainThread, callback, data);
177 struct alignas(8) InvokeComponentNotifyProxyPayload {
184 static_assert((
sizeof(InvokeComponentNotifyProxyPayload) % 8) == 0);
186 void invokeComponentNotifyProxy(
void* payload)
188 InvokeComponentNotifyProxyPayload* p =
reinterpret_cast<InvokeComponentNotifyProxyPayload*
>(payload);
189 const void* data = p->dataSize ? ((
const char*)p +
sizeof(InvokeComponentNotifyProxyPayload)) :
nullptr;
190 p->context->callbacks.componentNotifyCallback(p->context, p->componentTypeId, p->entityId, p->notification, data, p->dataSize);
197 static pthread_t mainThread = emscripten_main_runtime_thread_id();
198 if (context->callbacks.componentNotifyCallback) {
199 if (mainThread == pthread_self()) {
200 context->callbacks.componentNotifyCallback(context, component.
getTypeId(), component.
getContainer()->
getId(), notification, data, dataSize);
203 if (dataSize) { assert(data); }
209 char* payload = (
char*)malloc(
sizeof(InvokeComponentNotifyProxyPayload) + dataSize);
211 InvokeComponentNotifyProxyPayload* p = (InvokeComponentNotifyProxyPayload*)payload;
212 *p = InvokeComponentNotifyProxyPayload{ .context = context,
213 .componentTypeId = component.
getTypeId(),
215 .notification = notification,
220 std::memcpy(payload +
sizeof(InvokeComponentNotifyProxyPayload), data, dataSize);
223 emscripten_proxy_async(emscripten_proxy_get_system_queue(), mainThread, invokeComponentNotifyProxy, payload);
229 struct InvokeResourceLoadProxyPayload {
236 void invokeResourceLoadProxy(
void* payload)
238 InvokeResourceLoadProxyPayload* p =
reinterpret_cast<InvokeResourceLoadProxyPayload*
>(payload);
239 p->context->callbacks.resourceLoadCallback(p->context, p->resourceType, p->id, p->code);
246 static pthread_t mainThread = emscripten_main_runtime_thread_id();
247 if (context->callbacks.resourceLoadCallback) {
248 if (mainThread == pthread_self()) {
249 context->callbacks.resourceLoadCallback(context, resourceType,
id, code);
254 InvokeResourceLoadProxyPayload* payload =
new InvokeResourceLoadProxyPayload{
256 .resourceType = resourceType,
260 emscripten_proxy_async(emscripten_proxy_get_system_queue(), mainThread, invokeResourceLoadProxy, payload);
267void Cogs::Core::Engine::setDirtyState()
269 lastDirtyFrame = context->
time->getFrame();
273void Cogs::Core::Engine::checkAndUpdateResources()
278 resourceManifest = getResourceManifest(context);
285 if (!initializing_device && context->
resourceStore->hasResources(resourceManifest)) {
286 LOG_INFO(logger,
"Found all resources. Running...");
290 context->
resourceStore->addResourceArchive(context->
variables->get(
"resources.zipPath",
"Cogs.Resources.zip"));
294 initializing_device =
true;
297 if(initializing_device && context->device->isInitializationFinished()){
298 initializing_device =
false;
299 LOG_TRACE(logger,
"Initializing resource managers...");
301 context->bufferManager = createResourceManager<BufferManager>(
this, context);
302 context->fontManager = createResourceManager<FontManager>(
this, context);
303 context->animationManager = createResourceManager<AnimationManager>(
this, context);
304 context->modelManager = createResourceManager<ModelManager>(
this, context);
305 context->effectManager = createResourceManager<EffectManager>(
this, context);
306 context->materialManager = createResourceManager<MaterialManager>(
this, context);
307 context->materialInstanceManager = createResourceManager<MaterialInstanceManager>(
this, context);
308 context->meshManager = createResourceManager<MeshManager>(
this, context);
309 context->textureManager = createResourceManager<TextureManager>(
this, context);
310 context->assetManager = createResourceManager<AssetManager>(
this, context);
311 context->blueNoiseManager = createResourceManager<BasicBlueNoiseManager>(
this, context);
313 context->materialDefinitionManager = context->materialManager->materialDefinitionManager.get();
315 initializeResources();
317 LOG_TRACE(logger,
"Resource managers initialized.");
319 context->
renderer->initialize(context->device);
321 LOG_TRACE(logger,
"Initializing engine subsystems...");
323 EngineSystems::initialize(context,
this);
325 LOG_TRACE(logger,
"Engine subsystems initialized.");
327 Instrumentation::initialize(context);
328 Instrumentation::initializeThread(
"Main");
336 for (SystemDefinition& system : data->systems) {
337 system.system->initialize(context);
343 static bool loggedDelay =
false;
345 LOG_INFO(logger,
"Delaying initialization for resource load...");
351void Cogs::Core::Engine::setEditor(
IEditor * editor) { this->editor.reset(editor); }
355 resourceManagers.push_back(std::move(resourceManager));
360 CpuInstrumentationScope(SCOPE_ENGINE,
"Engine::update");
361 OsoMP_Message(
"Engine::update", 0xFFFF9911,
nullptr, 0);
365 workParallelValue = context->
variables->get(
"engine.workParallel", workParallelValue);
368 bool tasksDone =
true;
370 const int mainThreadTasksFrameLimitMs = context->
variables->get(
"engine.mainThreadTasksFrameLimitMs", 5);
374 std::function<void()> f;
376 Cogs::LockGuard guard(data->mainThreadTasks.lock);
377 if (data->mainThreadTasks.tasks.empty())
break;
378 f = std::move(data->mainThreadTasks.tasks.front());
379 data->mainThreadTasks.tasks.pop_front();
389 if ((0 < mainThreadTasksFrameLimitMs) && (mainThreadTasksFrameLimitMs < taskTimer.elapsedMilliseconds())) {
390 Cogs::LockGuard guard(data->mainThreadTasks.lock);
391 tasksDone = data->mainThreadTasks.tasks.empty();
397 checkAndUpdateResources();
401 updateRequested =
false;
407 editor->beginFrame();
410 bool vsync = context->
variables->get(
"renderer.vSync",
false);
411 bool rendererDisable = context->
variables->get(
"renderer.disable",
false);
414 if (viewportSize.x < 2 || viewportSize.y < 2) {
415 rendererDisable =
true;
418 if (!rendererDisable) {
419 CpuInstrumentationScope(SCOPE_RENDERING,
"Engine::beginFrame");
426 if (!rendererDisable) {
428 CpuInstrumentationScope(SCOPE_RENDERING,
"Engine::render");
436 CpuInstrumentationScope(SCOPE_RENDERING,
"Engine::present");
449 if (systemFlags & SystemFlags::Changed) {
450 std::sort(data->systems.begin(), data->systems.end(), [&](
const SystemDefinition & left,
const SystemDefinition & right)
452 return left.priority < right.priority;
458 static thread_local std::vector<SystemDefinition> localSystems;
462 localSystems = data->systems;
464 context->transformSystem->handleOriginUpdate(context);
466 CpuInstrumentationScope(SCOPE_ENGINE,
"Engine::preUpdateSystems");
467 for (
auto & system : localSystems) {
468 system.system->instrumentedPreUpdate();
473 CpuInstrumentationScope(SCOPE_ENGINE,
"Engine::updateSystems");
474 for (
auto & system : localSystems) {
475 system.system->instrumentedUpdate();
479 if (context->callbacks.postSystemsUpdateCallback) {
480 context->callbacks.postSystemsUpdateCallback(context);
484 CpuInstrumentationScope(SCOPE_ENGINE,
"Engine::postUpdateSystems");
485 for (
auto & system : localSystems) {
486 system.system->instrumentedPostUpdate();
491void Cogs::Core::Engine::preRender()
493 CpuInstrumentationScope(SCOPE_ENGINE,
"PreRender");
495 context->preRender();
497 for (
auto & resourceManager : resourceManagers) {
498 resourceManager->processLoading();
502 CpuInstrumentationScope(SCOPE_ENGINE,
"processSwapping");
504 for (
auto & resourceManager : resourceManagers) {
505 resourceManager->processSwapping();
510 CpuInstrumentationScope(SCOPE_ENGINE,
"activateResources");
512 for (
auto & resourceManager : resourceManagers) {
513 resourceManager->activateResources();
518 CpuInstrumentationScope(SCOPE_ENGINE,
"processDeletion");
520 for (
auto & resourceManager : resourceManagers) {
521 resourceManager->processDeletion();
526void Cogs::Core::Engine::swapResources()
528 CpuInstrumentationScope(SCOPE_ENGINE,
"SwapResources");
530 for (
auto & resourceManager : resourceManagers) {
531 resourceManager->processSwapping();
536void Cogs::Core::Engine::render()
538 CpuInstrumentationScope(SCOPE_ENGINE,
"Render");
543void Cogs::Core::Engine::postRender()
550 SystemDefinition definition = { system, priority };
552 if (registerInStore) {
556 data->systems.push_back(definition);
558 systemFlags |= SystemFlags::Changed;
563 for (
auto & resourceManager : resourceManagers) {
564 resourceManager->initialize();
570 for (
auto & resourceManager : resourceManagers) {
571 resourceManager->clearSwapping();
574 for (
auto & resourceManager : resourceManagers) {
575 resourceManager->clear();
577 for (
auto & resourceManager : resourceManagers) {
578 resourceManager->processDeletion();
585 for (
auto & mgt : resourceManagers) {
587 if (ptr && ptr->doesManage(valueType_))
return ptr;
Base class for Component instances.
class Entity * getContainer() const
Get the container currently owning this component instance.
constexpr Reflection::TypeId getTypeId() const
Get the Reflection::TypeId of the component.
constexpr size_t getId() const noexcept
Get the unique identifier of this entity.
Base class for component systems.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
class IRenderer * renderer
Renderer.
void initialize()
Initialize all services and component systems.
void clear()
Does clearing of context.
std::unique_ptr< class QualityService > qualityService
Quality service instance.
class EntityStore * store
Entity store.
std::unique_ptr< class ResourceUsageLogger > resourceUsageLogger
Resource usage logger service instance.
std::unique_ptr< struct MemoryContext > memory
Memory and allocation info.
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
std::unique_ptr< class Variables > variables
Variables service instance.
std::unique_ptr< class Time > time
Time service instance.
std::unique_ptr< class ResourceStore > resourceStore
ResourceStore service instance.
The engine owns all the systems and resource managers, and is responsible for executing different sta...
~Engine()
Destructs the engine instance.
void invokeResourceLoadCallback(int resourceType, ResourceId id, int code)
Invoke the resourceLoadCallback if it exists, proxied from emscripten worker threads.
void updateSystems()
Performs updates of all the systems.
void update()
Update the engine, advancing the system state a single frame.
void clearResources()
Clears all resource managers pending reinitialization of resources.
void invokeCallback(void(*callback)(void *), void *data)
Generic callback invoke wrapper for callbacks with just a data-wrapper, proxied from emscripten worke...
void addResourceManager(std::unique_ptr< IResourceManager > &&resourceManager)
Add the given resource manager to the engine.
void initializeResources()
Initializes all resource managers and default resources.
Engine(Context *context)
Constructs a new engine instance, living in the given Context.
void registerSystem(ComponentSystemBase *system, int priority, bool registerInStore=true)
Register the given component system in the engine with the given priority.
void invokeComponentNotifyCallback(const ComponentModel::Component &component, int notification, const void *data, size_t dataSize)
Invoke the componentNotifyCallback if it exists, proxied from emscripten worker threads.
void addSystem(const Reflection::TypeId typeId, ComponentCreator creator, ComponentDestroyer destroyer)
Adds the given creator and destroyer functions for handling components of the given typeId.
static void cleanup(Context *context)
Cleanup the given context.
static void remove(Context *context)
Removes the given context.
virtual void endFrame(uint32_t syncInterval=0, Cogs::PresentFlags presentFlags=Cogs::PresentFlags::None)=0
Signals the end of the current frame.
virtual void beginFrame()=0
Signals the beginning of a new frame.
virtual glm::vec2 getSize() const =0
Get the output surface size of the renderer.
virtual void render()=0
Kick off the actual rendering, allowing the renderer to produce its output.
Log implementation class.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
DefaultValueType
Defines value types for default values.
constexpr Log getLogger(const char(&name)[LEN]) noexcept