💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

296 lines
8.8 KiB

  1. /*
  2. * Copyright (C) 2023 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <engine/app/sdl/sdl-window.hpp>
  20. #include <engine/config.hpp>
  21. #include <engine/debug/log.hpp>
  22. #include <engine/gl/pipeline.hpp>
  23. #include <glad/gl.h>
  24. #include <stdexcept>
  25. namespace app {
  26. sdl_window::sdl_window
  27. (
  28. const std::string& title,
  29. const math::ivec2& windowed_position,
  30. const math::ivec2& windowed_size,
  31. bool maximized,
  32. bool fullscreen,
  33. bool v_sync
  34. )
  35. {
  36. // Determine SDL window creation flags
  37. Uint32 window_flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
  38. if (maximized)
  39. {
  40. window_flags |= SDL_WINDOW_MAXIMIZED;
  41. }
  42. if (fullscreen)
  43. {
  44. window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
  45. }
  46. // Create SDL window
  47. debug::log_trace("Creating SDL window...");
  48. m_internal_window = SDL_CreateWindow
  49. (
  50. title.c_str(),
  51. windowed_position.x(),
  52. windowed_position.y(),
  53. windowed_size.x(),
  54. windowed_size.y(),
  55. window_flags
  56. );
  57. if (!m_internal_window)
  58. {
  59. debug::log_fatal("Failed to create SDL window: {}", SDL_GetError());
  60. throw std::runtime_error("Failed to create SDL window");
  61. }
  62. debug::log_trace("Created SDL window");
  63. // Create OpenGL context
  64. debug::log_trace("Creating OpenGL context...");
  65. m_internal_context = SDL_GL_CreateContext(m_internal_window);
  66. if (!m_internal_context)
  67. {
  68. debug::log_fatal("Failed to create OpenGL context: {}", SDL_GetError());
  69. throw std::runtime_error("Failed to create OpenGL context");
  70. }
  71. debug::log_trace("Created OpenGL context");
  72. // Query OpenGL context info
  73. int opengl_context_version_major = -1;
  74. int opengl_context_version_minor = -1;
  75. int opengl_context_red_size = -1;
  76. int opengl_context_green_size = -1;
  77. int opengl_context_blue_size = -1;
  78. int opengl_context_alpha_size = -1;
  79. int opengl_context_depth_size = -1;
  80. int opengl_context_stencil_size = -1;
  81. SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &opengl_context_version_major);
  82. SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &opengl_context_version_minor);
  83. SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &opengl_context_red_size);
  84. SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &opengl_context_green_size);
  85. SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &opengl_context_blue_size);
  86. SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &opengl_context_alpha_size);
  87. SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &opengl_context_depth_size);
  88. SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &opengl_context_stencil_size);
  89. // Log OpenGL context info
  90. debug::log_info
  91. (
  92. "OpenGL context version: {}.{}; format: R{}G{}B{}A{}D{}S{}",
  93. opengl_context_version_major,
  94. opengl_context_version_minor,
  95. opengl_context_red_size,
  96. opengl_context_green_size,
  97. opengl_context_blue_size,
  98. opengl_context_alpha_size,
  99. opengl_context_depth_size,
  100. opengl_context_stencil_size
  101. );
  102. // Compare OpenGL context version with requested version
  103. if (opengl_context_version_major != config::opengl_version_major ||
  104. opengl_context_version_minor != config::opengl_version_minor)
  105. {
  106. debug::log_warning("Requested OpenGL context version {}.{} but got version {}.{}", config::opengl_version_major, config::opengl_version_minor, opengl_context_version_major, opengl_context_version_minor);
  107. }
  108. // Compare OpenGL context format with requested format
  109. if (opengl_context_red_size < config::opengl_min_red_size ||
  110. opengl_context_green_size < config::opengl_min_green_size ||
  111. opengl_context_blue_size < config::opengl_min_blue_size ||
  112. opengl_context_alpha_size < config::opengl_min_alpha_size ||
  113. opengl_context_depth_size < config::opengl_min_depth_size ||
  114. opengl_context_stencil_size < config::opengl_min_stencil_size)
  115. {
  116. debug::log_warning
  117. (
  118. "OpenGL context format (R{}G{}B{}A{}D{}S{}) does not meet minimum requested format (R{}G{}B{}A{}D{}S{})",
  119. opengl_context_red_size,
  120. opengl_context_green_size,
  121. opengl_context_blue_size,
  122. opengl_context_alpha_size,
  123. opengl_context_depth_size,
  124. opengl_context_stencil_size,
  125. config::opengl_min_red_size,
  126. config::opengl_min_green_size,
  127. config::opengl_min_blue_size,
  128. config::opengl_min_alpha_size,
  129. config::opengl_min_depth_size,
  130. config::opengl_min_stencil_size
  131. );
  132. }
  133. // Load OpenGL functions via GLAD
  134. debug::log_trace("Loading OpenGL functions...");
  135. if (!gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress)))
  136. {
  137. debug::log_fatal("Failed to load OpenGL functions", SDL_GetError());
  138. throw std::runtime_error("Failed to load OpenGL functions");
  139. }
  140. debug::log_trace("Loaded OpenGL functions");
  141. // Log OpenGL information
  142. debug::log_info
  143. (
  144. "OpenGL vendor: {}; renderer: {}; version: {}; shading language version: {}",
  145. reinterpret_cast<const char*>(glGetString(GL_VENDOR)),
  146. reinterpret_cast<const char*>(glGetString(GL_RENDERER)),
  147. reinterpret_cast<const char*>(glGetString(GL_VERSION)),
  148. reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION))
  149. );
  150. // Allocate graphics pipeline
  151. m_graphics_pipeline = std::make_unique<gl::pipeline>();
  152. // Clear default framebuffer to black
  153. m_graphics_pipeline->clear_attachments(gl::color_clear_bit, {{0.0f, 0.0f, 0.0f, 0.0f}});
  154. swap_buffers();
  155. // Enable or disable v-sync
  156. set_v_sync(v_sync);
  157. // Update window state
  158. this->m_title = title;
  159. this->m_windowed_position = windowed_position;
  160. this->m_windowed_size = windowed_size;
  161. this->m_maximized = maximized;
  162. this->m_fullscreen = fullscreen;
  163. SDL_GetWindowPosition(m_internal_window, &this->m_position.x(), &this->m_position.y());
  164. SDL_GetWindowSize(m_internal_window, &this->m_size.x(), &this->m_size.y());
  165. SDL_GetWindowMinimumSize(m_internal_window, &this->m_minimum_size.x(), &this->m_minimum_size.y());
  166. SDL_GetWindowMaximumSize(m_internal_window, &this->m_maximum_size.x(), &this->m_maximum_size.y());
  167. SDL_GL_GetDrawableSize(m_internal_window, &this->m_viewport_size.x(), &this->m_viewport_size.y());
  168. }
  169. sdl_window::~sdl_window()
  170. {
  171. // Deallocate graphics pipeline
  172. m_graphics_pipeline.reset();
  173. // Destruct the OpenGL context
  174. SDL_GL_DeleteContext(m_internal_context);
  175. // Destruct the SDL window
  176. SDL_DestroyWindow(m_internal_window);
  177. }
  178. void sdl_window::set_title(const std::string& title)
  179. {
  180. SDL_SetWindowTitle(m_internal_window, title.c_str());
  181. this->m_title = title;
  182. }
  183. void sdl_window::set_position(const math::ivec2& position)
  184. {
  185. SDL_SetWindowPosition(m_internal_window, position.x(), position.y());
  186. }
  187. void sdl_window::set_size(const math::ivec2& size)
  188. {
  189. SDL_SetWindowSize(m_internal_window, size.x(), size.y());
  190. }
  191. void sdl_window::set_minimum_size(const math::ivec2& size)
  192. {
  193. SDL_SetWindowMinimumSize(m_internal_window, size.x(), size.y());
  194. this->m_minimum_size = size;
  195. }
  196. void sdl_window::set_maximum_size(const math::ivec2& size)
  197. {
  198. SDL_SetWindowMaximumSize(m_internal_window, size.x(), size.y());
  199. this->m_maximum_size = size;
  200. }
  201. void sdl_window::set_maximized(bool maximized)
  202. {
  203. if (maximized)
  204. {
  205. SDL_MaximizeWindow(m_internal_window);
  206. }
  207. else
  208. {
  209. SDL_RestoreWindow(m_internal_window);
  210. }
  211. }
  212. void sdl_window::set_fullscreen(bool fullscreen)
  213. {
  214. //SDL_HideWindow(m_internal_window);
  215. SDL_SetWindowFullscreen(m_internal_window, (fullscreen) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
  216. //SDL_ShowWindow(m_internal_window);
  217. this->m_fullscreen = fullscreen;
  218. }
  219. void sdl_window::set_v_sync(bool v_sync)
  220. {
  221. if (v_sync)
  222. {
  223. debug::log_trace("Enabling adaptive v-sync...");
  224. if (SDL_GL_SetSwapInterval(-1) != 0)
  225. {
  226. debug::log_error("Failed to enable adaptive v-sync: {}", SDL_GetError());
  227. debug::log_trace("Enabling synchronized v-sync...");
  228. if (SDL_GL_SetSwapInterval(1) != 0)
  229. {
  230. debug::log_error("Failed to enable synchronized v-sync: {}", SDL_GetError());
  231. v_sync = false;
  232. }
  233. else
  234. {
  235. debug::log_debug("Enabled synchronized v-sync");
  236. }
  237. }
  238. else
  239. {
  240. debug::log_debug("Enabled adaptive v-sync");
  241. }
  242. }
  243. else
  244. {
  245. debug::log_trace("Disabling v-sync...");
  246. if (SDL_GL_SetSwapInterval(0) != 0)
  247. {
  248. debug::log_error("Failed to disable v-sync: {}", SDL_GetError());
  249. v_sync = true;
  250. }
  251. else
  252. {
  253. debug::log_debug("Disabled v-sync");
  254. }
  255. }
  256. this->m_v_sync = v_sync;
  257. }
  258. void sdl_window::make_current()
  259. {
  260. SDL_GL_MakeCurrent(m_internal_window, m_internal_context);
  261. }
  262. void sdl_window::swap_buffers()
  263. {
  264. SDL_GL_SwapWindow(m_internal_window);
  265. }
  266. } // namespace app