openMSX
SDLGLVisibleSurface.cc
Go to the documentation of this file.
1 #include "SDLGLVisibleSurface.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 
19 namespace openmsx {
20 
22  unsigned width, unsigned height,
23  Display& display_,
24  RTScheduler& rtScheduler_,
25  EventDistributor& eventDistributor_,
26  InputEventGenerator& inputEventGenerator_,
27  CliComm& cliComm_,
28  VideoSystem& videoSystem_)
29  : SDLVisibleSurfaceBase(display_, rtScheduler_, eventDistributor_,
30  inputEventGenerator_, cliComm_, videoSystem_)
31 {
32  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
33  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
34  SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
35  int flags = SDL_WINDOW_OPENGL;
36  //flags |= SDL_RESIZABLE;
37  createSurface(width, height, flags);
38 
39  // Create an OpenGL 3.3 Core profile
40  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
41  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
42  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
43  glContext = SDL_GL_CreateContext(window.get());
44  if (!glContext) {
45  throw InitException(
46  "Failed to create openGL-3.3 context: ", SDL_GetError());
47  }
48 
49  // From the glew documentation:
50  // GLEW obtains information on the supported extensions from the
51  // graphics driver. Experimental or pre-release drivers, however,
52  // might not report every available extension through the standard
53  // mechanism, in which case GLEW will report it unsupported. To
54  // circumvent this situation, the glewExperimental global switch can
55  // be turned on by setting it to GL_TRUE before calling glewInit(),
56  // which ensures that all extensions with valid entry points will be
57  // exposed.
58  // The 'glewinfo' utility also sets this flag before reporting results,
59  // so I believe it would cause less confusion to do the same here.
60  glewExperimental = GL_TRUE;
61 
62  // Initialise GLEW library.
63  GLenum glew_error = glewInit();
64  if (glew_error != GLEW_OK) {
65  throw InitException(
66  "Failed to init GLEW: ",
67  reinterpret_cast<const char*>(
68  glewGetErrorString(glew_error)));
69  }
70 
71  // The created surface may be larger than requested.
72  // If that happens, center the area that we actually use.
73  int w, h;
74  SDL_GL_GetDrawableSize(window.get(), &w, &h);
75  calculateViewPort(gl::ivec2(width, height), gl::ivec2(w, h));
76  auto [vx, vy] = getViewOffset();
77  auto [vw, vh] = getViewSize();
78  glViewport(vx, vy, vw, vh);
79 
80  glMatrixMode(GL_PROJECTION);
81  glLoadIdentity();
82  glOrtho(0, width, height, 0, -1, 1);
83  glMatrixMode(GL_MODELVIEW);
84 
86  gl::context = std::make_unique<gl::Context>(width, height);
87 
89  // set initial value
90  vSyncObserver.update(getDisplay().getRenderSettings().getVSyncSetting());
91 }
92 
94 {
96 
97  gl::context.reset();
98  SDL_GL_DeleteContext(glContext);
99 }
100 
102 {
103  saveScreenshotGL(*this, filename);
104 }
105 
107  const OutputSurface& output, const std::string& filename)
108 {
109  auto [x, y] = output.getViewOffset();
110  auto [w, h] = output.getViewSize();
111 
112  VLA(const void*, rowPointers, h);
113  MemBuffer<uint8_t> buffer(w * h * 3);
114  for (int i = 0; i < h; ++i) {
115  rowPointers[h - 1 - i] = &buffer[w * 3 * i];
116  }
117  glReadPixels(x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, buffer.data());
118  PNG::save(w, h, rowPointers, filename);
119 }
120 
122 {
123  SDL_GL_SwapWindow(window.get());
124 }
125 
126 std::unique_ptr<Layer> SDLGLVisibleSurface::createSnowLayer()
127 {
128  return std::make_unique<GLSnow>(getDisplay());
129 }
130 
132  Reactor& reactor, CommandConsole& console)
133 {
134  const bool openGL = true;
135  auto [width, height] = getLogicalSize();
136  return std::make_unique<OSDConsoleRenderer>(
137  reactor, console, width, height, openGL);
138 }
139 
140 std::unique_ptr<Layer> SDLGLVisibleSurface::createOSDGUILayer(OSDGUI& gui)
141 {
142  return std::make_unique<GLOSDGUILayer>(gui);
143 }
144 
145 std::unique_ptr<OutputSurface> SDLGLVisibleSurface::createOffScreenSurface()
146 {
147  return std::make_unique<SDLGLOffScreenSurface>(*this);
148 }
149 
150 void SDLGLVisibleSurface::VSyncObserver::update(const Setting& setting)
151 {
152  auto& surface = OUTER(SDLGLVisibleSurface, vSyncObserver);
153  auto& syncSetting = surface.getDisplay().getRenderSettings().getVSyncSetting();
154  assert(&setting == &syncSetting); (void)setting;
155 
156  // for now, we assume that adaptive vsync is the best kind of vsync, so when
157  // vsync is enabled, we attempt adaptive vsync.
158  int interval = syncSetting.getBoolean() ? -1 : 0;
159 
160  if ((SDL_GL_SetSwapInterval(interval) < 0) && (interval == -1)) {
161  // "Adaptive vsync" is not supported by all drivers. SDL
162  // documentation suggests to fallback to "regular vsync" in
163  // that case.
164  SDL_GL_SetSwapInterval(1);
165  }
166 }
167 
168 } // namespace openmsx
openmsx::SDLGLVisibleSurface::saveScreenshotGL
static void saveScreenshotGL(const OutputSurface &output, const std::string &filename)
Definition: SDLGLVisibleSurface.cc:106
openmsx::SDLVisibleSurfaceBase
Common functionality for the plain SDL and SDLGL VisibleSurface classes.
Definition: SDLVisibleSurfaceBase.hh:13
openmsx::OutputSurface::calculateViewPort
void calculateViewPort(gl::ivec2 logSize, gl::ivec2 physSize)
Definition: OutputSurface.cc:6
openmsx::Subject::detach
void detach(Observer< T > &observer)
Definition: Subject.hh:56
Display.hh
openmsx::SDLGLVisibleSurface::finish
void finish() override
When a complete frame is finished, call this method.
Definition: SDLGLVisibleSurface.cc:121
SDLGLOffScreenSurface.hh
openmsx::InputEventGenerator
Definition: InputEventGenerator.hh:18
GLSnow.hh
openmsx::SDLGLVisibleSurface::createOSDGUILayer
std::unique_ptr< Layer > createOSDGUILayer(OSDGUI &gui) override
Definition: SDLGLVisibleSurface.cc:140
openmsx::SDLGLVisibleSurface
Visible surface for SDL openGL renderers.
Definition: SDLGLVisibleSurface.hh:11
openmsx::SDLGLVisibleSurface::createConsoleLayer
std::unique_ptr< Layer > createConsoleLayer(Reactor &reactor, CommandConsole &console) override
Definition: SDLGLVisibleSurface.cc:131
openmsx::RTScheduler
Definition: RTScheduler.hh:20
openmsx::OutputSurface::getViewOffset
gl::ivec2 getViewOffset() const
Definition: OutputSurface.hh:32
openmsx::VideoSystem
Video back-end system.
Definition: VideoSystem.hh:21
openmsx::Subject::attach
void attach(Observer< T > &observer)
Definition: Subject.hh:50
openmsx::SDLGLVisibleSurface::createSnowLayer
std::unique_ptr< Layer > createSnowLayer() override
Definition: SDLGLVisibleSurface.cc:126
openmsx::Setting
Definition: Setting.hh:120
openmsx::EventDistributor
Definition: EventDistributor.hh:17
PNG.hh
openmsx::OutputSurface::setOpenGlPixelFormat
void setOpenGlPixelFormat()
Definition: OutputSurface.cc:24
gl::vecN
Definition: gl_vec.hh:36
openmsx::Reactor
Contains the main loop of openMSX.
Definition: Reactor.hh:67
openmsx::OSDGUI
Definition: OSDGUI.hh:14
vla.hh
OUTER
#define OUTER(type, member)
Definition: outer.hh:41
openmsx::MemBuffer< uint8_t >
OSDGUILayer.hh
SDLGLVisibleSurface.hh
openmsx::filename
constexpr const char *const filename
Definition: FirmwareSwitch.cc:10
openmsx::SDLGLVisibleSurface::SDLGLVisibleSurface
SDLGLVisibleSurface(unsigned width, unsigned height, Display &display, RTScheduler &rtScheduler, EventDistributor &eventDistributor, InputEventGenerator &inputEventGenerator, CliComm &cliComm, VideoSystem &videoSystem)
Definition: SDLGLVisibleSurface.cc:21
openmsx::InitException
Thrown when a subsystem initialisation fails.
Definition: InitException.hh:12
build-info.hh
RenderSettings.hh
MemBuffer.hh
openmsx::SDLVisibleSurfaceBase::createSurface
void createSurface(int width, int height, unsigned flags)
Definition: SDLVisibleSurfaceBase.cc:30
outer.hh
openmsx::SDLVisibleSurfaceBase::window
SDLWindowPtr window
Definition: SDLVisibleSurfaceBase.hh:24
openmsx::x
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:1419
openmsx::SDLGLVisibleSurface::saveScreenshot
void saveScreenshot(const std::string &filename) override
Save the content of this OutputSurface to a PNG file.
Definition: SDLGLVisibleSurface.cc:101
gl::context
std::unique_ptr< Context > context
Definition: GLContext.cc:9
InitException.hh
openmsx::Display
Represents the output window/screen of openMSX.
Definition: Display.hh:33
openmsx::Display::getRenderSettings
RenderSettings & getRenderSettings()
Definition: Display.hh:44
openmsx::SDLGLVisibleSurface::~SDLGLVisibleSurface
~SDLGLVisibleSurface() override
Definition: SDLGLVisibleSurface.cc:93
openmsx::MemBuffer::data
const T * data() const
Returns pointer to the start of the memory buffer.
Definition: MemBuffer.hh:90
openmsx::OutputSurface
A frame buffer where pixels can be written to.
Definition: OutputSurface.hh:20
openmsx::CliComm
Definition: CliComm.hh:11
unreachable.hh
CliComm.hh
openmsx::VisibleSurface::getDisplay
Display & getDisplay() const
Definition: VisibleSurface.hh:52
VLA
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
OSDConsoleRenderer.hh
openmsx
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
GLContext.hh
openmsx::SDLGLVisibleSurface::createOffScreenSurface
std::unique_ptr< OutputSurface > createOffScreenSurface() override
Create an off-screen OutputSurface which has similar properties as this VisibleSurface.
Definition: SDLGLVisibleSurface.cc:145
openmsx::RenderSettings::getVSyncSetting
BooleanSetting & getVSyncSetting()
VSync [on, off] ATM this only works when using the SDLGL-PP renderer.
Definition: RenderSettings.hh:149
openmsx::OutputSurface::getLogicalSize
gl::ivec2 getLogicalSize() const
Definition: OutputSurface.hh:29
openmsx::OutputSurface::getViewSize
gl::ivec2 getViewSize() const
Definition: OutputSurface.hh:33
openmsx::CommandConsole
Definition: CommandConsole.hh:66