Support update of game.dll separately Made some alias and stuff still remains the shader compiler to add to the solution. Solution is also generated by fbuild (nice)
300 lines
9.3 KiB
C++
300 lines
9.3 KiB
C++
#include "main.h"
|
|
|
|
#include <Core/Application/ApplicationManager.h>
|
|
#include <Core/Common/EnumUtils.h>
|
|
#include <Core/HAL/Display/Display.h>
|
|
#include <Core/HAL/Event/SystemEvent.h>
|
|
#include <Core/HAL/Filesystem/Filesystem.h>
|
|
#include <Core/JulietInit.h>
|
|
#include <Core/Logging/LogManager.h>
|
|
#include <Core/Logging/LogTypes.h>
|
|
#include <Graphics/Graphics.h>
|
|
#include <Graphics/GraphicsConfig.h>
|
|
#include <Graphics/GraphicsPipeline.h>
|
|
#include <Graphics/RenderPass.h>
|
|
|
|
#include <Core/Common/String.h>
|
|
#include <Core/Memory/Utils.h>
|
|
#include <cstdlib>
|
|
|
|
// TODO : Replace with message box from framework + call main and not winmain + subsystem
|
|
// TODO : Think how to do the draw pipeline.
|
|
// Ex: Expose a Draw method ?
|
|
// Store a graphics context ?
|
|
// For now. Put everything in Update down below.
|
|
// Should split update from draw, update should have a != timestep than graphics (60fps or more)
|
|
|
|
// TODO : Remove main.h. Useless
|
|
// May be remove that Application class, useless too.
|
|
|
|
using namespace Juliet;
|
|
|
|
namespace
|
|
{
|
|
using GameInit_t = void (*)(void);
|
|
using GameShutdown_t = void (*)(void);
|
|
using GameUpdate_t = void (*)(float deltaTime);
|
|
struct GameFunctionTable
|
|
{
|
|
GameInit_t Init = nullptr;
|
|
GameShutdown_t Shutdown = nullptr;
|
|
GameUpdate_t Update = nullptr;
|
|
} Game;
|
|
const char* GameFunctionTable[] = { "GameInit", "GameShutdown", "GameUpdate" };
|
|
} // namespace
|
|
|
|
void JulietApplication::Init()
|
|
{
|
|
Log(LogLevel::Message, LogCategory::Tool, "Initializing Juliet Application...");
|
|
|
|
Log(LogLevel::Message, LogCategory::Tool, "%s", CStr(GetBasePath()));
|
|
|
|
GraphicsConfig config;
|
|
#if JULIET_DEBUG
|
|
config.EnableDebug = true;
|
|
#endif
|
|
GraphicsDevice = CreateGraphicsDevice(config);
|
|
|
|
MainWindow = CreatePlatformWindow("Juliet Editor", 1280, 720);
|
|
|
|
Running = MainWindow != nullptr && GraphicsDevice != nullptr;
|
|
|
|
if (Running)
|
|
{
|
|
AttachToWindow(GraphicsDevice, MainWindow);
|
|
|
|
{
|
|
// Create graphics pipeline
|
|
String entryPoint = WrapString("main");
|
|
ShaderCreateInfo shaderCI = {};
|
|
shaderCI.EntryPoint = entryPoint;
|
|
|
|
// TODO: Assets management that handles path to assets or something.
|
|
String shaderPath = WrapString("../../Assets/compiled/Triangle.vert.dxil");
|
|
shaderCI.Stage = ShaderStage::Vertex;
|
|
Shader* vertexShader = CreateShader(GraphicsDevice, shaderPath, shaderCI);
|
|
|
|
shaderPath = WrapString("../../Assets/compiled/SolidColor.frag.dxil");
|
|
shaderCI.Stage = ShaderStage::Fragment;
|
|
Shader* fragmentShader = CreateShader(GraphicsDevice, shaderPath, shaderCI);
|
|
|
|
ColorTargetDescription colorTargetDescription = {};
|
|
colorTargetDescription.Format = GetSwapChainTextureFormat(GraphicsDevice, MainWindow);
|
|
|
|
GraphicsPipelineCreateInfo pipelineCI = {};
|
|
pipelineCI.VertexShader = vertexShader;
|
|
pipelineCI.FragmentShader = fragmentShader;
|
|
pipelineCI.PrimitiveType = PrimitiveType::TriangleList;
|
|
pipelineCI.TargetInfo = { .ColorTargetDescriptions = &colorTargetDescription,
|
|
.NumColorTargets = 1,
|
|
.DepthStencilFormat = {},
|
|
.HasDepthStencilTarget = false };
|
|
pipelineCI.RasterizerState.FillMode = FillMode::Solid;
|
|
|
|
GraphicsPipeline = CreateGraphicsPipeline(GraphicsDevice, pipelineCI);
|
|
if (GraphicsPipeline == nullptr)
|
|
{
|
|
LogError(LogCategory::Game, "Failed to create graphics pipeline!");
|
|
Running = false;
|
|
}
|
|
|
|
if (vertexShader)
|
|
{
|
|
DestroyShader(GraphicsDevice, vertexShader);
|
|
}
|
|
if (fragmentShader)
|
|
{
|
|
DestroyShader(GraphicsDevice, fragmentShader);
|
|
}
|
|
|
|
if (Running == false)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
GameCode.Functions = reinterpret_cast<void**>(&Game);
|
|
GameCode.FunctionCount = ArraySize(GameFunctionTable);
|
|
GameCode.FunctionNames = GameFunctionTable;
|
|
InitHotReloadCode(GameCode, ConstString("Game.dll"), ConstString("Game_Temp.dll"), ConstString("lock.tmp"));
|
|
if ((Running = GameCode.IsValid))
|
|
{
|
|
Game.Init();
|
|
}
|
|
}
|
|
}
|
|
|
|
void JulietApplication::Shutdown()
|
|
{
|
|
Log(LogLevel::Message, LogCategory::Tool, "Shutting down Juliet Application...");
|
|
|
|
if (GameCode.IsValid)
|
|
{
|
|
Game.Shutdown();
|
|
ShutdownHotReloadCode(GameCode);
|
|
}
|
|
|
|
if (GraphicsPipeline)
|
|
{
|
|
DestroyGraphicsPipeline(GraphicsDevice, GraphicsPipeline);
|
|
}
|
|
|
|
if (MainWindow && GraphicsDevice)
|
|
{
|
|
DetachFromWindow(GraphicsDevice, MainWindow);
|
|
}
|
|
|
|
if (MainWindow)
|
|
{
|
|
DestroyPlatformWindow(MainWindow);
|
|
}
|
|
|
|
if (GraphicsDevice)
|
|
{
|
|
DestroyGraphicsDevice(GraphicsDevice);
|
|
}
|
|
|
|
Log(LogLevel::Message, LogCategory::Tool, "Juliet App shutdown Completed");
|
|
}
|
|
|
|
void JulietApplication::Update()
|
|
{
|
|
bool reloadShaders = false;
|
|
static bool reloadShadersDebounce = false;
|
|
|
|
SystemEvent evt;
|
|
while (GetEvent(evt))
|
|
{
|
|
if (evt.Type == EventType::Window_Close_Request)
|
|
{
|
|
if (evt.Data.Window.AssociatedWindowID == GetWindowID(MainWindow))
|
|
{
|
|
Running = false;
|
|
}
|
|
}
|
|
|
|
if (evt.Type == EventType::Key_Down)
|
|
{
|
|
}
|
|
|
|
// Shader hot reload using keyboard.
|
|
// TODO: Add Input debounce in the library. (Just pressed vs pressed)
|
|
if (!reloadShadersDebounce && ((GetKeyModState() & KeyMod::Alt) != KeyMod::None) && IsKeyDown(ScanCode::R))
|
|
{
|
|
reloadShaders = true;
|
|
reloadShadersDebounce = true;
|
|
}
|
|
|
|
if (reloadShadersDebounce && !IsKeyDown(ScanCode::R))
|
|
{
|
|
reloadShadersDebounce = false;
|
|
}
|
|
}
|
|
|
|
Game.Update(0.0f);
|
|
|
|
if (ShouldReloadCode(GameCode))
|
|
{
|
|
ReloadCode(GameCode);
|
|
}
|
|
|
|
if (reloadShaders)
|
|
{
|
|
// We need to wait for the gpu to be idle to recreate our graphics pipelines
|
|
WaitUntilGPUIsIdle(GraphicsDevice);
|
|
|
|
#if ALLOW_SHADER_HOT_RELOAD
|
|
String entryPoint = WrapString("main");
|
|
ShaderCreateInfo shaderCI = {};
|
|
shaderCI.EntryPoint = entryPoint;
|
|
String shaderPath = WrapString("../../Assets/compiled/Triangle.vert.dxil");
|
|
shaderCI.Stage = ShaderStage::Vertex;
|
|
Shader* vertexShader = CreateShader(GraphicsDevice, shaderPath, shaderCI);
|
|
|
|
shaderPath = WrapString("../../Assets/compiled/SolidColor.frag.dxil");
|
|
shaderCI.Stage = ShaderStage::Fragment;
|
|
Shader* fragmentShader = CreateShader(GraphicsDevice, shaderPath, shaderCI);
|
|
|
|
UpdateGraphicsPipelineShaders(GraphicsDevice, GraphicsPipeline, vertexShader, fragmentShader);
|
|
|
|
if (vertexShader)
|
|
{
|
|
DestroyShader(GraphicsDevice, vertexShader);
|
|
}
|
|
if (fragmentShader)
|
|
{
|
|
DestroyShader(GraphicsDevice, fragmentShader);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Draw here for now
|
|
// 1) Acquire a Command Buffer
|
|
CommandList* cmdList = AcquireCommandList(GraphicsDevice, QueueType::Graphics);
|
|
if (cmdList == nullptr)
|
|
{
|
|
Log(LogLevel::Error, LogCategory::Tool, "Failed to acquire command list.");
|
|
Running = false;
|
|
return;
|
|
}
|
|
|
|
Texture* swapChainTexture = nullptr;
|
|
if (!WaitAndAcquireSwapChainTexture(cmdList, MainWindow, &swapChainTexture))
|
|
{
|
|
Log(LogLevel::Error, LogCategory::Tool, "Failed to acquire swapchain texture.");
|
|
Running = false;
|
|
return;
|
|
}
|
|
|
|
if (swapChainTexture)
|
|
{
|
|
ColorTargetInfo colorTargetInfo = {};
|
|
colorTargetInfo.TargetTexture = swapChainTexture;
|
|
colorTargetInfo.ClearColor = { .R = .5f, .G = .8f, .B = .0f, .A = 1.f };
|
|
colorTargetInfo.LoadOperation = LoadOperation::Clear;
|
|
colorTargetInfo.StoreOperation = StoreOperation::Store;
|
|
|
|
RenderPass* renderPass = BeginRenderPass(cmdList, colorTargetInfo);
|
|
BindGraphicsPipeline(renderPass, GraphicsPipeline);
|
|
DrawPrimitives(renderPass, 3, 1, 0, 0);
|
|
EndRenderPass(renderPass);
|
|
}
|
|
|
|
// Submit Commands
|
|
SubmitCommandLists(cmdList);
|
|
}
|
|
|
|
bool JulietApplication::IsRunning()
|
|
{
|
|
return Running;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
JulietApplication EditorApplication;
|
|
}
|
|
|
|
JulietApplication& GetEditorApplication()
|
|
{
|
|
return EditorApplication;
|
|
}
|
|
|
|
int main(int /*argc*/, char** /*argv*/)
|
|
{
|
|
// Allow only one instance to be launched.
|
|
// Need to create Mutex code in the lib because i dont want to include windows.h in this file anymore
|
|
// CreateMutex(0, false, L"Local\\Juliet.App");
|
|
// if (GetLastError() == ERROR_ALREADY_EXISTS)
|
|
// {
|
|
// MessageBox(nullptr, L"An instance of Juliet is already running.", L"Juliet", MB_OK | MB_ICONEXCLAMATION);
|
|
// return EXIT_FAILURE;
|
|
// }
|
|
|
|
StartApplication(EditorApplication, JulietInit_Flags::Display);
|
|
|
|
// Pause here to not close the console window immediatly on stop
|
|
system("PAUSE");
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|