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);
413 if (!rendererDisable) {
414 CpuInstrumentationScope(SCOPE_RENDERING,
"Engine::beginFrame");
421 if (!rendererDisable) {
423 CpuInstrumentationScope(SCOPE_RENDERING,
"Engine::render");
431 CpuInstrumentationScope(SCOPE_RENDERING,
"Engine::present");
444 if (systemFlags & SystemFlags::Changed) {
445 std::sort(data->systems.begin(), data->systems.end(), [&](
const SystemDefinition & left,
const SystemDefinition & right)
447 return left.priority < right.priority;
453 static thread_local std::vector<SystemDefinition> localSystems;
457 localSystems = data->systems;
459 context->transformSystem->handleOriginUpdate(context);
461 CpuInstrumentationScope(SCOPE_ENGINE,
"Engine::preUpdateSystems");
462 for (
auto & system : localSystems) {
463 system.system->instrumentedPreUpdate();
468 CpuInstrumentationScope(SCOPE_ENGINE,
"Engine::updateSystems");
469 for (
auto & system : localSystems) {
470 system.system->instrumentedUpdate();
474 if (context->callbacks.postSystemsUpdateCallback) {
475 context->callbacks.postSystemsUpdateCallback(context);
479 CpuInstrumentationScope(SCOPE_ENGINE,
"Engine::postUpdateSystems");
480 for (
auto & system : localSystems) {
481 system.system->instrumentedPostUpdate();
486void Cogs::Core::Engine::preRender()
488 CpuInstrumentationScope(SCOPE_ENGINE,
"PreRender");
490 context->preRender();
492 for (
auto & resourceManager : resourceManagers) {
493 resourceManager->processLoading();
497 CpuInstrumentationScope(SCOPE_ENGINE,
"processSwapping");
499 for (
auto & resourceManager : resourceManagers) {
500 resourceManager->processSwapping();
505 CpuInstrumentationScope(SCOPE_ENGINE,
"activateResources");
507 for (
auto & resourceManager : resourceManagers) {
508 resourceManager->activateResources();
513 CpuInstrumentationScope(SCOPE_ENGINE,
"processDeletion");
515 for (
auto & resourceManager : resourceManagers) {
516 resourceManager->processDeletion();
521void Cogs::Core::Engine::swapResources()
523 CpuInstrumentationScope(SCOPE_ENGINE,
"SwapResources");
525 for (
auto & resourceManager : resourceManagers) {
526 resourceManager->processSwapping();
531void Cogs::Core::Engine::render()
533 CpuInstrumentationScope(SCOPE_ENGINE,
"Render");
538void Cogs::Core::Engine::postRender()
545 SystemDefinition definition = { system, priority };
547 if (registerInStore) {
551 data->systems.push_back(definition);
553 systemFlags |= SystemFlags::Changed;
558 for (
auto & resourceManager : resourceManagers) {
559 resourceManager->initialize();
565 for (
auto & resourceManager : resourceManagers) {
566 resourceManager->clearSwapping();
569 for (
auto & resourceManager : resourceManagers) {
570 resourceManager->clear();
572 for (
auto & resourceManager : resourceManagers) {
573 resourceManager->processDeletion();
580 for (
auto & mgt : resourceManagers) {
582 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, uint32_t presentFlags=Cogs::PresentFlags::None)=0
Signals the end of the current frame.
virtual void beginFrame()=0
Signals the beginning of a new frame.
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