// // Created by kj16609 on 11/27/23. // #include "EngineContext.hpp" #include #include #include #include #include "KeyboardMovementController.hpp" #include "camera/CameraManager.hpp" #include "camera/GBufferRenderer.hpp" #include "camera/RenderCamera.hpp" #include "camera/ShadowMap.hpp" #include "debug/timing/FlameGraph.hpp" #include "engine/assets/model/builders/SceneBuilder.hpp" #include "engine/assets/transfer/TransferManager.hpp" #include "engine/flags.hpp" #include "engine/math/Average.hpp" #include "engine/math/literals/size.hpp" #include "memory/buffers/VulkanBuffer.hpp" #include "systems/RenderGraph.hpp" namespace fgl::engine { constexpr float MAX_DELTA_TIME { 0.5 }; inline static EngineContext* instance { nullptr }; EngineContext::EngineContext() : m_ubo_buffer_pool( 1_MiB, vk::BufferUsageFlagBits::eUniformBuffer, vk::MemoryPropertyFlagBits::eHostVisible ), m_delta_time( 0.0 ), m_camera_manager(), m_sun( nullptr ) { ZoneScoped; using namespace fgl::literals::size_literals; instance = this; m_camera_manager.createPrimary(); m_sun = std::make_unique< Sun >(); // memory::TransferManager::createInstance( device, 128_MiB ); } void EngineContext::processInput() { auto timer = debug::timing::push( "Process Inputs" ); glfwPollEvents(); } void EngineContext::tickDeltaTime() { ZoneScoped; // Get delta time const auto now { fgl::Clock::now() }; const std::chrono::duration< DeltaTime, std::chrono::seconds::period > time_diff { now - m_last_tick }; m_last_tick = now; // Convert from ms to s m_delta_time = time_diff.count(); } /* World EngineContext::tickSimulation() { ZoneScoped; auto timer = debug::timing::push( "Tick Simulation" ); // TODO: This is where we'll start doing physics stuff. // The first step here should be culling things that aren't needed to be ticked. // Perhaps implementing a tick system that doesn't care about the refresh rate might be good? // That way we can still tick consistantly without actually needing to render anything. return {}; } */ void EngineContext::renderCameras( FrameInfo frame_info ) { ZoneScoped; auto timer = debug::timing::push( "Render Cameras" ); for ( auto& current_camera_ptr : m_camera_manager.getCameras() ) { if ( current_camera_ptr.expired() ) continue; auto sh_camera { current_camera_ptr.lock() }; RenderCamera& current_camera { *sh_camera }; current_camera.pass( frame_info ); } for ( auto& shadow_map : getShadowmaps() ) { if ( shadow_map.expired() ) continue; auto shadowmap { shadow_map.lock() }; shadowmap->pass( frame_info ); } } void EngineContext::renderFrame() { ZoneScoped; RenderGraph graph {}; if ( auto command_buffers_o = m_renderer.beginFrame(); command_buffers_o.has_value() ) { const auto timer = debug::timing::push( "Render Frame" ); const FrameIndex in_flight_idx { m_renderer.getFrameIndex() }; const PresentIndex present_idx { m_renderer.getPresentIndex() }; auto& command_buffers { command_buffers_o.value() }; // Begin by getting every single instance ready. DeviceVector< PrimitiveInstanceInfo >& instances { m_model_buffers.m_primitive_instances.vec() }; FrameInfo frame_info { in_flight_idx, present_idx, m_delta_time, graph, command_buffers, nullptr, // Camera m_camera_manager.getCameras(), m_renderer.getCurrentTracyCTX(), *m_model_buffers.m_primitives_desc, *m_model_buffers.m_instances_desc, instances, // *m_gpu_draw_cmds_desc[ in_flight_idx ], // m_gpu_draw_commands[ in_flight_idx ], m_game_objects, this->m_renderer.getSwapChain() }; { ZoneScopedN( "Pre frame hooks" ); for ( const auto& hook : m_pre_frame_hooks ) hook( frame_info ); } TracyVkCollect( frame_info.tracy_ctx, **command_buffers.transfer_cb ); for ( const auto& hook : m_early_render_hooks ) hook( frame_info ); //TODO: Add some way of 'activating' cameras. We don't need to render cameras that aren't active. renderCameras( frame_info ); for ( const auto& hook : m_render_hooks ) hook( frame_info ); m_renderer.beginSwapchainRendererPass( command_buffers.imgui_cb ); m_gui_system.pass( frame_info ); for ( const auto& hook : m_late_render_hooks ) hook( frame_info ); m_renderer.endSwapchainRendererPass( command_buffers.imgui_cb ); m_transfer_manager.recordOwnershipTransferDst( command_buffers.transfer_cb ); m_renderer.endFrame( command_buffers ); //TODO: Setup semaphores to make this pass not always required. m_transfer_manager.dump(); m_device.getCmdBufferPool().advanceInFlight(); { ZoneScopedN( "Post frame hooks" ); for ( const auto& hook : m_post_frame_hooks ) hook( frame_info ); } flags::resetFlags(); FrameMark; } //Trash handling descriptors::deleteQueuedDescriptors(); } void EngineContext::finishFrame() {} void EngineContext::waitIdle() { m_device->waitIdle(); } Window& EngineContext::getWindow() { return m_window; } float EngineContext::getWindowAspectRatio() { return m_renderer.getAspectRatio(); } CameraManager& EngineContext::cameraManager() { return m_camera_manager; } MaterialManager& EngineContext::getMaterialManager() { return m_material_manager; } EngineContext::~EngineContext() { log::info( "Destroying EngineContext" ); // Destroy all objects // m_game_objects_root.clear(); descriptors::deleteQueuedDescriptors(); log::info( "Performing {} destruction hooks", m_destruction_hooks.size() ); for ( const auto& hook : m_destruction_hooks ) hook(); } EngineContext& EngineContext::getInstance() { assert( instance ); return *instance; } bool EngineContext::good() { return !m_window.shouldClose(); } void EngineContext::handleTransfers() { memory::TransferManager::getInstance().submitNow(); } } // namespace fgl::engine