Mercurial > games > semicongine
comparison semiconginev2/old/engine.nim @ 1218:56781cc0fc7c compiletime-tests
did: renamge main package
| author | sam <sam@basx.dev> |
|---|---|
| date | Wed, 17 Jul 2024 21:01:37 +0700 |
| parents | semicongine/old/engine.nim@a3eb305bcac2 |
| children |
comparison
equal
deleted
inserted
replaced
| 1217:f819a874058f | 1218:56781cc0fc7c |
|---|---|
| 1 import std/compilesettings | |
| 2 import std/algorithm | |
| 3 import std/monotimes | |
| 4 import std/options | |
| 5 import std/strformat | |
| 6 import std/sequtils | |
| 7 import std/logging | |
| 8 import std/os | |
| 9 | |
| 10 import ./platform/window | |
| 11 | |
| 12 import ./core | |
| 13 import ./vulkan/instance | |
| 14 import ./vulkan/device | |
| 15 import ./vulkan/physicaldevice | |
| 16 import ./vulkan/shader | |
| 17 | |
| 18 import ./scene | |
| 19 import ./material | |
| 20 import ./renderer | |
| 21 import ./audio | |
| 22 import ./input | |
| 23 import ./text | |
| 24 import ./panel | |
| 25 | |
| 26 import ./steam | |
| 27 | |
| 28 const COUNT_N_RENDERTIMES = 199 | |
| 29 | |
| 30 type | |
| 31 EngineState* = enum | |
| 32 Starting | |
| 33 Running | |
| 34 Shutdown | |
| 35 Engine* = object | |
| 36 applicationName: string | |
| 37 showFps: bool | |
| 38 device: Device | |
| 39 debugger: Debugger | |
| 40 instance: Instance | |
| 41 window: NativeWindow | |
| 42 renderer: Option[Renderer] | |
| 43 fullscreen: bool | |
| 44 lastNRenderTimes: array[COUNT_N_RENDERTIMES, int64] | |
| 45 currentRenderTimeI: int = 0 | |
| 46 | |
| 47 # forward declarations | |
| 48 func GetAspectRatio*(engine: Engine): float32 | |
| 49 | |
| 50 proc Destroy*(engine: var Engine) = | |
| 51 checkVkResult engine.device.vk.vkDeviceWaitIdle() | |
| 52 if engine.renderer.isSome: | |
| 53 engine.renderer.get.Destroy() | |
| 54 engine.device.Destroy() | |
| 55 if engine.debugger.messenger.Valid: | |
| 56 engine.debugger.Destroy() | |
| 57 engine.window.Destroy() | |
| 58 engine.instance.Destroy() | |
| 59 if SteamAvailable(): | |
| 60 SteamShutdown() | |
| 61 | |
| 62 | |
| 63 proc InitEngine*( | |
| 64 applicationName = querySetting(projectName), | |
| 65 showFps = DEBUG, | |
| 66 vulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0), | |
| 67 vulkanLayers: openArray[string] = [], | |
| 68 ): Engine = | |
| 69 echo "Set log level to ", ENGINE_LOGLEVEL | |
| 70 setLogFilter(ENGINE_LOGLEVEL) | |
| 71 | |
| 72 TrySteamInit() | |
| 73 if SteamAvailable(): | |
| 74 echo "Starting with Steam" | |
| 75 else: | |
| 76 echo "Starting without Steam" | |
| 77 | |
| 78 result.applicationName = applicationName | |
| 79 result.showFps = showFps | |
| 80 result.window = CreateWindow(result.applicationName) | |
| 81 | |
| 82 var | |
| 83 layers = @vulkanLayers | |
| 84 instanceExtensions: seq[string] | |
| 85 | |
| 86 if DEBUG: | |
| 87 instanceExtensions.add "VK_EXT_debug_utils" | |
| 88 layers.add "VK_LAYER_KHRONOS_validation" | |
| 89 # This stuff might be usefull if we one day to smart GPU memory allocation, | |
| 90 # but right now it just clobbers up the console log: | |
| 91 # putEnv("VK_LAYER_ENABLES", "VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT") | |
| 92 putEnv("VK_LAYER_ENABLES", "VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD,VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_NVIDIA,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXTVK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT") | |
| 93 | |
| 94 result.instance = result.window.CreateInstance( | |
| 95 vulkanVersion = vulkanVersion, | |
| 96 instanceExtensions = instanceExtensions, | |
| 97 layers = layers.deduplicate(), | |
| 98 ) | |
| 99 if DEBUG: | |
| 100 result.debugger = result.instance.CreateDebugMessenger() | |
| 101 # create devices | |
| 102 let selectedPhysicalDevice = result.instance.GetPhysicalDevices().FilterBestGraphics() | |
| 103 result.device = result.instance.CreateDevice( | |
| 104 selectedPhysicalDevice, | |
| 105 enabledExtensions = @[], | |
| 106 selectedPhysicalDevice.FilterForGraphicsPresentationQueues() | |
| 107 ) | |
| 108 StartMixerThread() | |
| 109 | |
| 110 proc InitRenderer*( | |
| 111 engine: var Engine, | |
| 112 shaders: openArray[(MaterialType, ShaderConfiguration)], | |
| 113 clearColor = NewVec4f(0, 0, 0, 0), | |
| 114 backFaceCulling = true, | |
| 115 vSync = false, | |
| 116 inFlightFrames = 2, | |
| 117 samples = VK_SAMPLE_COUNT_1_BIT, | |
| 118 ) = | |
| 119 | |
| 120 assert not engine.renderer.isSome | |
| 121 var allShaders = @shaders | |
| 122 if not shaders.mapIt(it[0]).contains(EMPTY_MATERIAL): | |
| 123 allShaders.add (EMPTY_MATERIAL, EMPTY_SHADER) | |
| 124 if not shaders.mapIt(it[0]).contains(PANEL_MATERIAL_TYPE): | |
| 125 allShaders.add (PANEL_MATERIAL_TYPE, PANEL_SHADER) | |
| 126 if not shaders.mapIt(it[0]).contains(TEXT_MATERIAL_TYPE): | |
| 127 allShaders.add (TEXT_MATERIAL_TYPE, TEXT_SHADER) | |
| 128 engine.renderer = some(engine.device.InitRenderer( | |
| 129 shaders = allShaders, | |
| 130 clearColor = clearColor, | |
| 131 backFaceCulling = backFaceCulling, | |
| 132 vSync = vSync, | |
| 133 inFlightFrames = inFlightFrames, | |
| 134 samples = samples, | |
| 135 )) | |
| 136 | |
| 137 proc InitRenderer*(engine: var Engine, clearColor = NewVec4f(0, 0, 0, 0), vSync = false) = | |
| 138 checkVkResult engine.device.vk.vkDeviceWaitIdle() | |
| 139 engine.InitRenderer(@[], clearColor, vSync = vSync) | |
| 140 checkVkResult engine.device.vk.vkDeviceWaitIdle() | |
| 141 | |
| 142 proc LoadScene*(engine: var Engine, scene: var Scene) = | |
| 143 debug &"start loading scene '{scene.name}'" | |
| 144 assert engine.renderer.isSome | |
| 145 assert not scene.loaded | |
| 146 checkVkResult engine.device.vk.vkDeviceWaitIdle() | |
| 147 scene.AddShaderGlobal(ASPECT_RATIO_ATTRIBUTE, engine.GetAspectRatio) | |
| 148 engine.renderer.get.SetupDrawableBuffers(scene) | |
| 149 engine.renderer.get.UpdateMeshData(scene, forceAll = true) | |
| 150 engine.renderer.get.UpdateUniformData(scene, forceAll = true) | |
| 151 checkVkResult engine.device.vk.vkDeviceWaitIdle() | |
| 152 debug &"done loading scene '{scene.name}'" | |
| 153 | |
| 154 proc UnloadScene*(engine: var Engine, scene: Scene) = | |
| 155 debug &"unload scene '{scene.name}'" | |
| 156 engine.renderer.get.Destroy(scene) | |
| 157 | |
| 158 proc RenderScene*(engine: var Engine, scene: var Scene) = | |
| 159 if WindowIsMinimized(): | |
| 160 return | |
| 161 assert engine.renderer.isSome, "Renderer has not yet been initialized, call 'engine.InitRenderer' first" | |
| 162 assert engine.renderer.get.HasScene(scene), &"Scene '{scene.name}' has not been loaded yet" | |
| 163 let t0 = getMonoTime() | |
| 164 | |
| 165 if engine.renderer.get.StartNewFrame(): | |
| 166 scene.SetShaderGlobal(ASPECT_RATIO_ATTRIBUTE, engine.GetAspectRatio) | |
| 167 engine.renderer.get.UpdateMeshData(scene) | |
| 168 engine.renderer.get.UpdateUniformData(scene) | |
| 169 engine.renderer.get.Render(scene) | |
| 170 | |
| 171 if engine.showFps: | |
| 172 let nanoSecs = getMonoTime().ticks - t0.ticks | |
| 173 engine.lastNRenderTimes[engine.currentRenderTimeI] = nanoSecs | |
| 174 inc engine.currentRenderTimeI | |
| 175 if engine.currentRenderTimeI >= engine.lastNRenderTimes.len: | |
| 176 engine.currentRenderTimeI = 0 | |
| 177 engine.lastNRenderTimes.sort | |
| 178 let | |
| 179 min = float(engine.lastNRenderTimes[0]) / 1_000_000 | |
| 180 median = float(engine.lastNRenderTimes[engine.lastNRenderTimes.len div 2]) / 1_000_000 | |
| 181 max = float(engine.lastNRenderTimes[^1]) / 1_000_000 | |
| 182 engine.window.SetTitle(&"{engine.applicationName} ({min:.2}, {median:.2}, {max:.2})") | |
| 183 | |
| 184 | |
| 185 # wrappers for internal things | |
| 186 func GpuDevice*(engine: Engine): Device = engine.device | |
| 187 func GetWindow*(engine: Engine): auto = engine.window | |
| 188 func GetAspectRatio*(engine: Engine): float32 = engine.GetWindow().Size[0] / engine.GetWindow().Size[1] | |
| 189 proc ShowSystemCursor*(engine: Engine) = engine.window.ShowSystemCursor() | |
| 190 proc HideSystemCursor*(engine: Engine) = engine.window.HideSystemCursor() | |
| 191 func Fullscreen*(engine: Engine): bool = engine.fullscreen | |
| 192 proc `Fullscreen=`*(engine: var Engine, enable: bool) = | |
| 193 if enable != engine.fullscreen: | |
| 194 engine.fullscreen = enable | |
| 195 engine.window.Fullscreen(engine.fullscreen) | |
| 196 | |
| 197 func Limits*(engine: Engine): VkPhysicalDeviceLimits = | |
| 198 engine.device.physicalDevice.properties.limits | |
| 199 | |
| 200 func MaxFramebufferSampleCount*(engine: Engine, maxSamples = VK_SAMPLE_COUNT_8_BIT): VkSampleCountFlagBits = | |
| 201 let available = VkSampleCountFlags( | |
| 202 engine.Limits.framebufferColorSampleCounts.uint32 and engine.Limits.framebufferDepthSampleCounts.uint32 | |
| 203 ).toEnums | |
| 204 return min(max(available), maxSamples) | |
| 205 | |
| 206 proc UpdateInputs*(engine: Engine): bool = | |
| 207 UpdateInputs(engine.window.PendingEvents()) | |
| 208 | |
| 209 proc ProcessEvents*(engine: Engine, panel: var Panel) = | |
| 210 let hasMouseNow = panel.Contains(MousePositionNormalized(engine.window.Size), engine.GetAspectRatio) | |
| 211 | |
| 212 # enter/leave events | |
| 213 if hasMouseNow: | |
| 214 if panel.hasMouse: | |
| 215 if panel.onMouseMove != nil: panel.onMouseMove(panel) | |
| 216 else: | |
| 217 if panel.onMouseEnter != nil: panel.onMouseEnter(panel) | |
| 218 else: | |
| 219 if panel.hasMouse: | |
| 220 if panel.onMouseLeave != nil: panel.onMouseLeave(panel) | |
| 221 | |
| 222 # button events | |
| 223 if hasMouseNow: | |
| 224 if MouseWasPressed(): | |
| 225 if panel.onMouseDown != nil: panel.onMouseDown(panel, MousePressedButtons()) | |
| 226 if MouseWasReleased(): | |
| 227 if panel.onMouseUp != nil: panel.onMouseUp(panel, MouseReleasedButtons()) | |
| 228 | |
| 229 panel.hasMouse = hasMouseNow |
