SDLGLVisibleSurface.cc 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #include "SDLGLVisibleSurface.hh"
  2. #include "SDLGLOffScreenSurface.hh"
  3. #include "GLContext.hh"
  4. #include "GLSnow.hh"
  5. #include "OSDConsoleRenderer.hh"
  6. #include "OSDGUILayer.hh"
  7. #include "Display.hh"
  8. #include "RenderSettings.hh"
  9. #include "CliComm.hh"
  10. #include "PNG.hh"
  11. #include "build-info.hh"
  12. #include "MemBuffer.hh"
  13. #include "outer.hh"
  14. #include "vla.hh"
  15. #include "InitException.hh"
  16. #include "unreachable.hh"
  17. #include <memory>
  18. namespace openmsx {
  19. SDLGLVisibleSurface::SDLGLVisibleSurface(
  20. unsigned width, unsigned height,
  21. Display& display_,
  22. RTScheduler& rtScheduler_,
  23. EventDistributor& eventDistributor_,
  24. InputEventGenerator& inputEventGenerator_,
  25. CliComm& cliComm_,
  26. VideoSystem& videoSystem_)
  27. : SDLVisibleSurfaceBase(display_, rtScheduler_, eventDistributor_,
  28. inputEventGenerator_, cliComm_, videoSystem_)
  29. {
  30. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  31. SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
  32. SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
  33. int flags = SDL_WINDOW_OPENGL;
  34. //flags |= SDL_RESIZABLE;
  35. createSurface(width, height, flags);
  36. // Create an OpenGL 3.3 Core profile
  37. SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
  38. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
  39. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
  40. glContext = SDL_GL_CreateContext(window.get());
  41. if (!glContext) {
  42. throw InitException(
  43. "Failed to create openGL-3.3 context: ", SDL_GetError());
  44. }
  45. // From the glew documentation:
  46. // GLEW obtains information on the supported extensions from the
  47. // graphics driver. Experimental or pre-release drivers, however,
  48. // might not report every available extension through the standard
  49. // mechanism, in which case GLEW will report it unsupported. To
  50. // circumvent this situation, the glewExperimental global switch can
  51. // be turned on by setting it to GL_TRUE before calling glewInit(),
  52. // which ensures that all extensions with valid entry points will be
  53. // exposed.
  54. // The 'glewinfo' utility also sets this flag before reporting results,
  55. // so I believe it would cause less confusion to do the same here.
  56. glewExperimental = GL_TRUE;
  57. // Initialise GLEW library.
  58. GLenum glew_error = glewInit();
  59. if (glew_error != GLEW_OK) {
  60. throw InitException(
  61. "Failed to init GLEW: ",
  62. reinterpret_cast<const char*>(
  63. glewGetErrorString(glew_error)));
  64. }
  65. // The created surface may be larger than requested.
  66. // If that happens, center the area that we actually use.
  67. int w, h;
  68. SDL_GL_GetDrawableSize(window.get(), &w, &h);
  69. calculateViewPort(gl::ivec2(width, height), gl::ivec2(w, h));
  70. auto [vx, vy] = getViewOffset();
  71. auto [vw, vh] = getViewSize();
  72. glViewport(vx, vy, vw, vh);
  73. glMatrixMode(GL_PROJECTION);
  74. glLoadIdentity();
  75. glOrtho(0, width, height, 0, -1, 1);
  76. glMatrixMode(GL_MODELVIEW);
  77. setOpenGlPixelFormat();
  78. gl::context = std::make_unique<gl::Context>(width, height);
  79. getDisplay().getRenderSettings().getVSyncSetting().attach(vSyncObserver);
  80. // set initial value
  81. vSyncObserver.update(getDisplay().getRenderSettings().getVSyncSetting());
  82. }
  83. SDLGLVisibleSurface::~SDLGLVisibleSurface()
  84. {
  85. getDisplay().getRenderSettings().getVSyncSetting().detach(vSyncObserver);
  86. gl::context.reset();
  87. SDL_GL_DeleteContext(glContext);
  88. }
  89. void SDLGLVisibleSurface::saveScreenshot(const std::string& filename)
  90. {
  91. saveScreenshotGL(*this, filename);
  92. }
  93. void SDLGLVisibleSurface::saveScreenshotGL(
  94. const OutputSurface& output, const std::string& filename)
  95. {
  96. auto [x, y] = output.getViewOffset();
  97. auto [w, h] = output.getViewSize();
  98. VLA(const void*, rowPointers, h);
  99. MemBuffer<uint8_t> buffer(w * h * 3);
  100. for (int i = 0; i < h; ++i) {
  101. rowPointers[h - 1 - i] = &buffer[w * 3 * i];
  102. }
  103. glReadPixels(x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, buffer.data());
  104. PNG::save(w, h, rowPointers, filename);
  105. }
  106. void SDLGLVisibleSurface::finish()
  107. {
  108. SDL_GL_SwapWindow(window.get());
  109. }
  110. std::unique_ptr<Layer> SDLGLVisibleSurface::createSnowLayer()
  111. {
  112. return std::make_unique<GLSnow>(getDisplay());
  113. }
  114. std::unique_ptr<Layer> SDLGLVisibleSurface::createConsoleLayer(
  115. Reactor& reactor, CommandConsole& console)
  116. {
  117. const bool openGL = true;
  118. auto [width, height] = getLogicalSize();
  119. return std::make_unique<OSDConsoleRenderer>(
  120. reactor, console, width, height, openGL);
  121. }
  122. std::unique_ptr<Layer> SDLGLVisibleSurface::createOSDGUILayer(OSDGUI& gui)
  123. {
  124. return std::make_unique<GLOSDGUILayer>(gui);
  125. }
  126. std::unique_ptr<OutputSurface> SDLGLVisibleSurface::createOffScreenSurface()
  127. {
  128. return std::make_unique<SDLGLOffScreenSurface>(*this);
  129. }
  130. void SDLGLVisibleSurface::VSyncObserver::update(const Setting& setting)
  131. {
  132. auto& surface = OUTER(SDLGLVisibleSurface, vSyncObserver);
  133. auto& syncSetting = surface.getDisplay().getRenderSettings().getVSyncSetting();
  134. assert(&setting == &syncSetting); (void)setting;
  135. // for now, we assume that adaptive vsync is the best kind of vsync, so when
  136. // vsync is enabled, we attempt adaptive vsync.
  137. int interval = syncSetting.getBoolean() ? -1 : 0;
  138. if ((SDL_GL_SetSwapInterval(interval) < 0) && (interval == -1)) {
  139. // "Adaptive vsync" is not supported by all drivers. SDL
  140. // documentation suggests to fallback to "regular vsync" in
  141. // that case.
  142. SDL_GL_SetSwapInterval(1);
  143. }
  144. }
  145. } // namespace openmsx