openMSX
SDLGLOutputSurface.cc
Go to the documentation of this file.
1 #include "SDLGLOutputSurface.hh"
2 #include "GLContext.hh"
3 #include "OutputSurface.hh"
4 #include "PNG.hh"
5 #include "build-info.hh"
6 #include "Math.hh"
7 #include "MemBuffer.hh"
8 #include "vla.hh"
9 #include <SDL.h>
10 
11 using namespace gl;
12 
13 namespace openmsx {
14 
15 SDLGLOutputSurface::SDLGLOutputSurface(FrameBuffer frameBuffer_)
16  : fbTex(Null())
17  , frameBuffer(frameBuffer_)
18 {
19 }
20 
22 {
23  // This is logically a part of the constructor, but the constructor
24  // of the child class needs to run before this code (to create a
25  // openGL context). So we split the constructor in two parts, the
26  // child class is responsible for calling this second part.
27 
28  SDLAllocFormatPtr format(SDL_AllocFormat(
29  (frameBuffer == FB_16BPP) ? SDL_PIXELFORMAT_RGB24 :
30  OPENMSX_BIGENDIAN ? SDL_PIXELFORMAT_RGBA8888 :
31  SDL_PIXELFORMAT_ARGB8888));
32  output.setSDLFormat(*format);
33 
34  if (frameBuffer == FB_NONE) {
35  output.setBufferPtr(nullptr, 0); // direct access not allowed
36  } else {
37  // TODO 64 byte aligned (see RawFrame)
38  unsigned width = output.getWidth();
39  unsigned height = output.getHeight();
40  unsigned texW = Math::ceil2(width);
41  unsigned texH = Math::ceil2(height);
42  fbBuf.resize(format->BytesPerPixel * texW * texH);
43  unsigned pitch = width * format->BytesPerPixel;
44  output.setBufferPtr(fbBuf.data(), pitch);
45 
46  texCoordX = float(width) / texW;
47  texCoordY = float(height) / texH;
48 
49  fbTex.allocate();
50  fbTex.setInterpolation(false);
51  if (frameBuffer == FB_16BPP) {
52  // TODO: Why use RGB texture instead of RGBA?
53  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texW, texH, 0,
54  GL_RGB, GL_UNSIGNED_SHORT_5_6_5, fbBuf.data());
55  } else {
56  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texW, texH, 0,
57  GL_BGRA, GL_UNSIGNED_BYTE, fbBuf.data());
58  }
59  }
60 }
61 
62 void SDLGLOutputSurface::flushFrameBuffer(unsigned width, unsigned height)
63 {
64  assert((frameBuffer == FB_16BPP) || (frameBuffer == FB_32BPP));
65 
66  fbTex.bind();
67  if (frameBuffer == FB_16BPP) {
68  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,
69  GL_RGB, GL_UNSIGNED_SHORT_5_6_5, fbBuf.data());
70  } else {
71  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,
72  GL_BGRA, GL_UNSIGNED_BYTE, fbBuf.data());
73  }
74 
75  vec2 pos[4] = {
76  vec2(0, height),
77  vec2(width, height),
78  vec2(width, 0 ),
79  vec2(0, 0 ),
80  };
81  vec2 tex[4] = {
82  vec2(0.0f, texCoordY),
83  vec2(texCoordX, texCoordY),
84  vec2(texCoordX, 0.0f ),
85  vec2(0.0f, 0.0f ),
86  };
87  gl::context->progTex.activate();
88  glUniform4f(gl::context->unifTexColor, 1.0f, 1.0f, 1.0f, 1.0f);
89  glUniformMatrix4fv(gl::context->unifTexMvp, 1, GL_FALSE,
90  &gl::context->pixelMvp[0][0]);
91  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, pos);
92  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, tex);
93  glEnableVertexAttribArray(0);
94  glEnableVertexAttribArray(1);
95  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
96  glDisableVertexAttribArray(1);
97  glDisableVertexAttribArray(0);
98 }
99 
101 {
102  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
103  glClear(GL_COLOR_BUFFER_BIT);
104 }
105 
107  const std::string& filename, const OutputSurface& output) const
108 {
109  gl::ivec2 offset = output.getViewOffset();
110  gl::ivec2 size = output.getViewSize();
111 
112  VLA(const void*, rowPointers, size[1]);
113  MemBuffer<uint8_t> buffer(size[0] * size[1] * 3);
114  for (int i = 0; i < size[1]; ++i) {
115  rowPointers[size[1] - 1 - i] = &buffer[size[0] * 3 * i];
116  }
117  glReadPixels(offset[0], offset[1], size[0], size[1], GL_RGB, GL_UNSIGNED_BYTE, buffer.data());
118  PNG::save(size[0], size[1], rowPointers, filename);
119 }
120 
121 } // namespace openmsx
void setBufferPtr(char *data, unsigned pitch)
void flushFrameBuffer(unsigned width, unsigned height)
void allocate()
Allocate an openGL texture name.
Definition: GLUtil.cc:43
std::unique_ptr< SDL_PixelFormat, SDLFreeFormat > SDLAllocFormatPtr
A frame buffer where pixels can be written to.
void init(OutputSurface &output)
void saveScreenshot(const std::string &filename, const OutputSurface &output) const
gl::ivec2 getViewSize() const
void bind()
Makes this texture the active GL texture.
Definition: GLUtil.hh:77
void setSDLFormat(const SDL_PixelFormat &format)
std::unique_ptr< Context > context
Definition: GLContext.cc:9
void resize(size_t size)
Grow or shrink the memory block.
Definition: MemBuffer.hh:120
const T * data() const
Returns pointer to the start of the memory buffer.
Definition: MemBuffer.hh:90
gl::ivec2 getViewOffset() const
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void setInterpolation(bool interpolation)
Enable/disable bilinear interpolation for this texture.
Definition: GLUtil.cc:54
constexpr T ceil2(T x) noexcept
Returns the smallest number that is both >=a and a power of two.
Definition: Math.hh:84
void format(SectorAccessibleDisk &disk, bool dos1)
Format the given disk (= a single partition).
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.hh:62
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
vecN< 2, float > vec2
Definition: gl_vec.hh:139
Definition: gl_mat.hh:24
FrameBuffer
These correspond respectively with the renderers: SDLGL-PP, SDLGL-FB16, SDLGL-FB32.