openMSX
GLPostProcessor.cc
Go to the documentation of this file.
1 #include "GLPostProcessor.hh"
2 #include "GLContext.hh"
3 #include "GLScaler.hh"
4 #include "GLScalerFactory.hh"
5 #include "FloatSetting.hh"
6 #include "OutputSurface.hh"
7 #include "RawFrame.hh"
8 #include "Math.hh"
9 #include "InitException.hh"
10 #include "gl_transform.hh"
11 #include "random.hh"
12 #include "ranges.hh"
13 #include "stl.hh"
14 #include "vla.hh"
15 #include <cassert>
16 #include <cstdint>
17 
18 using namespace gl;
19 
20 namespace openmsx {
21 
22 GLPostProcessor::GLPostProcessor(
23  MSXMotherBoard& motherBoard_, Display& display_,
24  OutputSurface& screen_, const std::string& videoSource,
25  unsigned maxWidth_, unsigned height_, bool canDoInterlace_)
26  : PostProcessor(motherBoard_, display_, screen_,
27  videoSource, maxWidth_, height_, canDoInterlace_)
28  , noiseTextureA(true, true) // interpolate + wrap
29  , noiseTextureB(true, true)
30  , height(height_)
31 {
32  if (!glewIsSupported("GL_EXT_framebuffer_object")) {
33  throw InitException(
34  "The OpenGL framebuffer object is not supported by "
35  "this glew library. Please upgrade your glew library.\n"
36  "It's also possible (but less likely) your video card "
37  "or video card driver doesn't support framebuffer "
38  "objects.");
39  }
40 
41  scaleAlgorithm = static_cast<RenderSettings::ScaleAlgorithm>(-1); // not a valid scaler
42 
43  frameCounter = 0;
44  noiseX = noiseY = 0.0f;
45  preCalcNoise(renderSettings.getNoise());
46 
47  storedFrame = false;
48  for (int i = 0; i < 2; ++i) {
49  colorTex[i].bind();
50  colorTex[i].setInterpolation(true);
51  glTexImage2D(GL_TEXTURE_2D, // target
52  0, // level
53  GL_RGB8, // internal format
54  screen.getWidth(), // width
55  screen.getHeight(),// height
56  0, // border
57  GL_RGB, // format
58  GL_UNSIGNED_BYTE, // type
59  nullptr); // data
60  fbo[i] = FrameBufferObject(colorTex[i]);
61  }
62 
63  VertexShader vertexShader ("monitor3D.vert");
64  FragmentShader fragmentShader("monitor3D.frag");
65  monitor3DProg.attach(vertexShader);
66  monitor3DProg.attach(fragmentShader);
67  monitor3DProg.bindAttribLocation(0, "a_position");
68  monitor3DProg.bindAttribLocation(1, "a_normal");
69  monitor3DProg.bindAttribLocation(2, "a_texCoord");
70  monitor3DProg.link();
71  preCalcMonitor3D(renderSettings.getHorizontalStretch());
72 
75 }
76 
78 {
81 }
82 
83 void GLPostProcessor::createRegions()
84 {
85  regions.clear();
86 
87  const unsigned srcHeight = paintFrame->getHeight();
88  const unsigned dstHeight = screen.getHeight();
89 
90  unsigned g = Math::gcd(srcHeight, dstHeight);
91  unsigned srcStep = srcHeight / g;
92  unsigned dstStep = dstHeight / g;
93 
94  // TODO: Store all MSX lines in RawFrame and only scale the ones that fit
95  // on the PC screen, as a preparation for resizable output window.
96  unsigned srcStartY = 0;
97  unsigned dstStartY = 0;
98  while (dstStartY < dstHeight) {
99  // Currently this is true because the source frame height
100  // is always >= dstHeight/(dstStep/srcStep).
101  assert(srcStartY < srcHeight);
102 
103  // get region with equal lineWidth
104  unsigned lineWidth = getLineWidth(paintFrame, srcStartY, srcStep);
105  unsigned srcEndY = srcStartY + srcStep;
106  unsigned dstEndY = dstStartY + dstStep;
107  while ((srcEndY < srcHeight) && (dstEndY < dstHeight) &&
108  (getLineWidth(paintFrame, srcEndY, srcStep) == lineWidth)) {
109  srcEndY += srcStep;
110  dstEndY += dstStep;
111  }
112 
113  regions.emplace_back(srcStartY, srcEndY,
114  dstStartY, dstEndY,
115  lineWidth);
116 
117  // next region
118  srcStartY = srcEndY;
119  dstStartY = dstEndY;
120  }
121 }
122 
124 {
126  interleaveCount ^= 1;
127  if (interleaveCount) {
128  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
129  glClear(GL_COLOR_BUFFER_BIT);
130  return;
131  }
132  }
133 
134  auto deform = renderSettings.getDisplayDeform();
135  float horStretch = renderSettings.getHorizontalStretch();
136  int glow = renderSettings.getGlow();
137  bool renderToTexture = (deform != RenderSettings::DEFORM_NORMAL) ||
138  (horStretch != 320.0f) ||
139  (glow != 0) ||
141 
142  if ((screen.getViewOffset() != ivec2()) || // any part of the screen not covered by the viewport?
143  (deform == RenderSettings::DEFORM_3D) || !paintFrame) {
144  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
145  glClear(GL_COLOR_BUFFER_BIT);
146  if (!paintFrame) {
147  return;
148  }
149  }
150 
151  // New scaler algorithm selected?
152  auto algo = renderSettings.getScaleAlgorithm();
153  if (scaleAlgorithm != algo) {
154  scaleAlgorithm = algo;
156 
157  // Re-upload frame data, this is both
158  // - Chunks of RawFrame with a specific linewidth, possibly
159  // with some extra lines above and below each chunk that are
160  // also converted to this linewidth.
161  // - Extra data that is specific for the scaler (ATM only the
162  // hq and hqlite scalers require this).
163  // Re-uploading the first is not strictly needed. But switching
164  // scalers doesn't happen that often, so it also doesn't hurt
165  // and it keeps the code simpler.
166  uploadFrame();
167  }
168 
169  if (renderToTexture) {
170  glViewport(0, 0, screen.getWidth(), screen.getHeight());
171  glBindTexture(GL_TEXTURE_2D, 0);
172  fbo[frameCounter & 1].push();
173  }
174 
175  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
176 
177  for (auto& r : regions) {
178  //fprintf(stderr, "post processing lines %d-%d: %d\n",
179  // r.srcStartY, r.srcEndY, r.lineWidth);
180  auto it = find_if_unguarded(textures,
181  EqualTupleValue<0>(r.lineWidth));
182  auto superImpose = superImposeVideoFrame
183  ? &superImposeTex : nullptr;
184  currScaler->scaleImage(
185  it->second.tex, superImpose,
186  r.srcStartY, r.srcEndY, r.lineWidth, // src
187  r.dstStartY, r.dstEndY, screen.getWidth(), // dst
188  paintFrame->getHeight()); // dst
189  //GLUtil::checkGLError("GLPostProcessor::paint");
190  }
191 
192  drawNoise();
193  drawGlow(glow);
194 
195  if (renderToTexture) {
196  fbo[frameCounter & 1].pop();
197  colorTex[frameCounter & 1].bind();
198  gl::ivec2 viewOffset = screen.getViewOffset();
199  gl::ivec2 viewSize = screen.getViewSize();
200  glViewport(viewOffset[0], viewOffset[1], viewSize[0], viewSize[1]);
201 
202  if (deform == RenderSettings::DEFORM_3D) {
203  drawMonitor3D();
204  } else {
205  float x1 = (320.0f - float(horStretch)) / (2.0f * 320.0f);
206  float x2 = 1.0f - x1;
207 
208  static const vec2 pos[4] = {
209  vec2(-1, 1), vec2(-1,-1), vec2( 1,-1), vec2( 1, 1)
210  };
211  vec2 tex[4] = {
212  vec2(x1, 1), vec2(x1, 0), vec2(x2, 0), vec2(x2, 1)
213  };
214 
215  gl::context->progTex.activate();
216  glUniform4f(gl::context->unifTexColor,
217  1.0f, 1.0f, 1.0f, 1.0f);
218  mat4 I;
219  glUniformMatrix4fv(gl::context->unifTexMvp,
220  1, GL_FALSE, &I[0][0]);
221  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, pos);
222  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, tex);
223  glEnableVertexAttribArray(0);
224  glEnableVertexAttribArray(1);
225  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
226  glDisableVertexAttribArray(1);
227  glDisableVertexAttribArray(0);
228  }
229  storedFrame = true;
230  } else {
231  storedFrame = false;
232  }
233 }
234 
235 std::unique_ptr<RawFrame> GLPostProcessor::rotateFrames(
236  std::unique_ptr<RawFrame> finishedFrame, EmuTime::param time)
237 {
238  std::unique_ptr<RawFrame> reuseFrame =
239  PostProcessor::rotateFrames(std::move(finishedFrame), time);
240  uploadFrame();
241  ++frameCounter;
242  noiseX = random_float(0.0f, 1.0f);
243  noiseY = random_float(0.0f, 1.0f);
244  return reuseFrame;
245 }
246 
247 void GLPostProcessor::update(const Setting& setting)
248 {
249  VideoLayer::update(setting);
250  auto& noiseSetting = renderSettings.getNoiseSetting();
251  auto& horizontalStretch = renderSettings.getHorizontalStretchSetting();
252  if (&setting == &noiseSetting) {
253  preCalcNoise(noiseSetting.getDouble());
254  } else if (&setting == &horizontalStretch) {
255  preCalcMonitor3D(horizontalStretch.getDouble());
256  }
257 }
258 
259 void GLPostProcessor::uploadFrame()
260 {
261  createRegions();
262 
263  const unsigned srcHeight = paintFrame->getHeight();
264  for (auto& r : regions) {
265  // upload data
266  // TODO get before/after data from scaler
267  unsigned before = 1;
268  unsigned after = 1;
269  uploadBlock(std::max<int>(0, r.srcStartY - before),
270  std::min<int>(srcHeight, r.srcEndY + after),
271  r.lineWidth);
272  }
273 
274  if (superImposeVideoFrame) {
275  int w = superImposeVideoFrame->getWidth();
276  int h = superImposeVideoFrame->getHeight();
277  if (superImposeTex.getWidth() != w ||
278  superImposeTex.getHeight() != h) {
279  superImposeTex.resize(w, h);
280  superImposeTex.setInterpolation(true);
281  }
282  superImposeTex.bind();
283  glTexSubImage2D(
284  GL_TEXTURE_2D, // target
285  0, // level
286  0, // offset x
287  0, // offset y
288  w, // width
289  h, // height
290  GL_BGRA, // format
291  GL_UNSIGNED_BYTE, // type
292  const_cast<RawFrame*>(superImposeVideoFrame)->getLinePtrDirect<unsigned>(0)); // data
293  }
294 }
295 
296 void GLPostProcessor::uploadBlock(
297  unsigned srcStartY, unsigned srcEndY, unsigned lineWidth)
298 {
299  // create texture/pbo if needed
300  auto it = ranges::find_if(textures, EqualTupleValue<0>(lineWidth));
301  if (it == end(textures)) {
302  TextureData textureData;
303 
304  textureData.tex.resize(lineWidth, height * 2); // *2 for interlace
305 
306  if (textureData.pbo.openGLSupported()) {
307  textureData.pbo.setImage(lineWidth, height * 2);
308  }
309 
310  textures.emplace_back(lineWidth, std::move(textureData));
311  it = end(textures) - 1;
312  }
313  auto& tex = it->second.tex;
314  auto& pbo = it->second.pbo;
315 
316  // bind texture
317  tex.bind();
318 
319  // upload data
320  uint32_t* mapped;
321  if (pbo.openGLSupported()) {
322  pbo.bind();
323  mapped = pbo.mapWrite();
324  } else {
325  mapped = nullptr;
326  }
327  if (mapped) {
328  for (unsigned y = srcStartY; y < srcEndY; ++y) {
329  auto* dest = mapped + y * lineWidth;
330  auto* data = paintFrame->getLinePtr(y, lineWidth, dest);
331  if (data != dest) {
332  memcpy(dest, data, lineWidth * sizeof(uint32_t));
333  }
334  }
335  pbo.unmap();
336 #if defined(__APPLE__)
337  // The nVidia GL driver for the GeForce 8000/9000 series seems to hang
338  // on texture data replacements that are 1 pixel wide and start on a
339  // line number that is a non-zero multiple of 16.
340  if (lineWidth == 1 && srcStartY != 0 && srcStartY % 16 == 0) {
341  srcStartY--;
342  }
343 #endif
344  glTexSubImage2D(
345  GL_TEXTURE_2D, // target
346  0, // level
347  0, // offset x
348  srcStartY, // offset y
349  lineWidth, // width
350  srcEndY - srcStartY, // height
351  GL_BGRA, // format
352  GL_UNSIGNED_BYTE, // type
353  pbo.getOffset(0, srcStartY)); // data
354  }
355  if (pbo.openGLSupported()) {
356  pbo.unbind();
357  }
358  if (!mapped) {
359  glPixelStorei(GL_UNPACK_ROW_LENGTH, paintFrame->getRowLength());
360  unsigned y = srcStartY;
361  unsigned remainingLines = srcEndY - srcStartY;
362  VLA_SSE_ALIGNED(uint32_t, buf, lineWidth);
363  while (remainingLines) {
364  unsigned lines;
366  y, remainingLines, lines, lineWidth, buf);
367  glTexSubImage2D(
368  GL_TEXTURE_2D, // target
369  0, // level
370  0, // offset x
371  y, // offset y
372  lineWidth, // width
373  lines, // height
374  GL_BGRA, // format
375  GL_UNSIGNED_BYTE, // type
376  data); // data
377 
378  y += lines;
379  remainingLines -= lines;
380  }
381  glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // restore default
382  }
383 
384  // possibly upload scaler specific data
385  if (currScaler) {
386  currScaler->uploadBlock(srcStartY, srcEndY, lineWidth, *paintFrame);
387  }
388 }
389 
390 void GLPostProcessor::drawGlow(int glow)
391 {
392  if ((glow == 0) || !storedFrame) return;
393 
394  static const vec2 pos[4] = {
395  vec2(-1, 1), vec2(-1,-1), vec2( 1,-1), vec2( 1, 1)
396  };
397  static const vec2 tex[4] = {
398  vec2( 0, 1), vec2( 0, 0), vec2( 1, 0), vec2( 1, 1)
399  };
400 
401  gl::context->progTex.activate();
402  glEnable(GL_BLEND);
403  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
404  colorTex[(frameCounter & 1) ^ 1].bind();
405  glUniform4f(gl::context->unifTexColor,
406  1.0f, 1.0f, 1.0f, glow * 31 / 3200.0f);
407  mat4 I;
408  glUniformMatrix4fv(gl::context->unifTexMvp, 1, GL_FALSE, &I[0][0]);
409  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, pos);
410  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, tex);
411  glEnableVertexAttribArray(0);
412  glEnableVertexAttribArray(1);
413  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
414  glDisableVertexAttribArray(1);
415  glDisableVertexAttribArray(0);
416  glDisable(GL_BLEND);
417 }
418 
419 void GLPostProcessor::preCalcNoise(float factor)
420 {
421  GLbyte buf1[256 * 256];
422  GLbyte buf2[256 * 256];
423  auto& generator = global_urng(); // fast (non-cryptographic) random numbers
424  std::normal_distribution<float> distribution(0.0f, 1.0f);
425  for (int i = 0; i < 256 * 256; ++i) {
426  float r = distribution(generator);
427  int s = Math::clip<-255, 255>(roundf(r * factor));
428  buf1[i] = (s > 0) ? s : 0;
429  buf2[i] = (s < 0) ? -s : 0;
430  }
431 
432  noiseTextureA.bind();
433  glTexImage2D(
434  GL_TEXTURE_2D, // target
435  0, // level
436  GL_LUMINANCE, // internal format
437  256, // width
438  256, // height
439  0, // border
440  GL_LUMINANCE, // format
441  GL_UNSIGNED_BYTE, // type
442  buf1); // data
443 
444  noiseTextureB.bind();
445  glTexImage2D(
446  GL_TEXTURE_2D, // target
447  0, // level
448  GL_LUMINANCE, // internal format
449  256, // width
450  256, // height
451  0, // border
452  GL_LUMINANCE, // format
453  GL_UNSIGNED_BYTE, // type
454  buf2); // data
455 }
456 
457 void GLPostProcessor::drawNoise()
458 {
459  if (renderSettings.getNoise() == 0.0f) return;
460 
461  // Rotate and mirror noise texture in consecutive frames to avoid
462  // seeing 'patterns' in the noise.
463  static const vec2 pos[8][4] = {
464  { { -1, -1 }, { 1, -1 }, { 1, 1 }, { -1, 1 } },
465  { { -1, 1 }, { 1, 1 }, { 1, -1 }, { -1, -1 } },
466  { { -1, 1 }, { -1, -1 }, { 1, -1 }, { 1, 1 } },
467  { { 1, 1 }, { 1, -1 }, { -1, -1 }, { -1, 1 } },
468  { { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 } },
469  { { 1, -1 }, { -1, -1 }, { -1, 1 }, { 1, 1 } },
470  { { 1, -1 }, { 1, 1 }, { -1, 1 }, { -1, -1 } },
471  { { -1, -1 }, { -1, 1 }, { 1, 1 }, { 1, -1 } }
472  };
473  vec2 noise(noiseX, noiseY);
474  const vec2 tex[4] = {
475  noise + vec2(0.0f, 1.875f),
476  noise + vec2(2.0f, 1.875f),
477  noise + vec2(2.0f, 0.0f ),
478  noise + vec2(0.0f, 0.0f )
479  };
480 
481  gl::context->progTex.activate();
482 
483  glEnable(GL_BLEND);
484  glBlendFunc(GL_ONE, GL_ONE);
485  glUniform4f(gl::context->unifTexColor, 1.0f, 1.0f, 1.0f, 1.0f);
486  mat4 I;
487  glUniformMatrix4fv(gl::context->unifTexMvp, 1, GL_FALSE, &I[0][0]);
488 
489  unsigned seq = frameCounter & 7;
490  glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, pos[seq]);
491  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, tex);
492  glEnableVertexAttribArray(0);
493  glEnableVertexAttribArray(1);
494 
495  noiseTextureA.bind();
496  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
497  glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
498  noiseTextureB.bind();
499  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
500 
501  glDisableVertexAttribArray(1);
502  glDisableVertexAttribArray(0);
503  glBlendEquation(GL_FUNC_ADD); // restore default
504  glDisable(GL_BLEND);
505 }
506 
507 static const int GRID_SIZE = 16;
508 static const int GRID_SIZE1 = GRID_SIZE + 1;
509 static const int NUM_INDICES = (GRID_SIZE1 * 2 + 2) * GRID_SIZE - 2;
510 struct Vertex {
514 };
515 
516 void GLPostProcessor::preCalcMonitor3D(float width)
517 {
518  // precalculate vertex-positions, -normals and -texture-coordinates
519  Vertex vertices[GRID_SIZE1][GRID_SIZE1];
520 
521  static const float GRID_SIZE2 = float(GRID_SIZE) / 2.0f;
522  float s = width / 320.0f;
523  float b = (320.0f - width) / (2.0f * 320.0f);
524 
525  for (int sx = 0; sx < GRID_SIZE1; ++sx) {
526  for (int sy = 0; sy < GRID_SIZE1; ++sy) {
527  Vertex& v = vertices[sx][sy];
528  float x = (sx - GRID_SIZE2) / GRID_SIZE2;
529  float y = (sy - GRID_SIZE2) / GRID_SIZE2;
530 
531  v.position = vec3(x, y, (x * x + y * y) / -12.0f);
532  v.normal = normalize(vec3(x / 6.0f, y / 6.0f, 1.0f)) * 1.2f;
533  v.tex = vec2((float(sx) / GRID_SIZE) * s + b,
534  float(sy) / GRID_SIZE);
535  }
536  }
537 
538  // calculate indices
539  uint16_t indices[NUM_INDICES];
540 
541  uint16_t* ind = indices;
542  for (int y = 0; y < GRID_SIZE; ++y) {
543  for (int x = 0; x < GRID_SIZE1; ++x) {
544  *ind++ = (y + 0) * GRID_SIZE1 + x;
545  *ind++ = (y + 1) * GRID_SIZE1 + x;
546  }
547  // skip 2, filled in later
548  ind += 2;
549  }
550  assert((ind - indices) == NUM_INDICES + 2);
551  ind = indices;
552  for (int y = 0; y < (GRID_SIZE - 1); ++y) {
553  ind += 2 * GRID_SIZE1;
554  // repeat prev and next index to restart strip
555  ind[0] = ind[-1];
556  ind[1] = ind[ 2];
557  ind += 2;
558  }
559 
560  // upload calculated values to buffers
561  glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer.get());
562  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
563  GL_STATIC_DRAW);
564  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer.get());
565  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
566  GL_STATIC_DRAW);
567  glBindBuffer(GL_ARRAY_BUFFER, 0);
568  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
569 
570  // calculate transformation matrices
571  mat4 proj = frustum(-1, 1, -1, 1, 1, 10);
572  mat4 tran = translate(vec3(0.0f, 0.4f, -2.0f));
573  mat4 rotx = rotateX(radians(-10.0f));
574  mat4 scal = scale(vec3(2.2f, 2.2f, 2.2f));
575 
576  mat3 normal = mat3(rotx);
577  mat4 mvp = proj * tran * rotx * scal;
578 
579  // set uniforms
580  monitor3DProg.activate();
581  glUniform1i(monitor3DProg.getUniformLocation("u_tex"), 0);
582  glUniformMatrix4fv(monitor3DProg.getUniformLocation("u_mvpMatrix"),
583  1, GL_FALSE, &mvp[0][0]);
584  glUniformMatrix3fv(monitor3DProg.getUniformLocation("u_normalMatrix"),
585  1, GL_FALSE, &normal[0][0]);
586 }
587 
588 void GLPostProcessor::drawMonitor3D()
589 {
590  monitor3DProg.activate();
591 
592  char* base = nullptr;
593  glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer.get());
594  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer.get());
595  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
596  base);
597  glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
598  base + sizeof(vec3));
599  glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
600  base + sizeof(vec3) + sizeof(vec3));
601  glEnableVertexAttribArray(0);
602  glEnableVertexAttribArray(1);
603  glEnableVertexAttribArray(2);
604 
605  glDrawElements(GL_TRIANGLE_STRIP, NUM_INDICES, GL_UNSIGNED_SHORT, nullptr);
606  glDisableVertexAttribArray(2);
607  glDisableVertexAttribArray(1);
608  glDisableVertexAttribArray(0);
609 
610  glBindBuffer(GL_ARRAY_BUFFER, 0);
611  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
612 }
613 
614 } // namespace openmsx
matMxN< 3, 3, float > mat3
Definition: gl_mat.hh:158
mat4 frustum(float left, float right, float bottom, float top, float nearVal, float farVal)
GLsizei getHeight() const
Definition: GLUtil.hh:107
unique_ptr< GLScaler > createScaler(RenderSettings &renderSettings)
Instantiates a Scaler.
Wrapper around an OpenGL fragment shader: a program executed on the GPU that computes the colors of p...
Definition: GLUtil.hh:380
Represents the output window/screen of openMSX.
Definition: Display.hh:31
static unsigned getLineWidth(FrameSource *frame, unsigned y, unsigned step)
Returns the maximum width for lines [y..y+step).
std::unique_ptr< RawFrame > rotateFrames(std::unique_ptr< RawFrame > finishedFrame, EmuTime::param time) override
Sets up the "abcdFrame" variables for a new frame.
int clip(int x)
Clips x to the range [LO,HI].
Definition: Math.hh:101
void update(const Setting &setting) override
vecN< N, T > normalize(const vecN< N, T > &x)
Definition: gl_vec.hh:350
int getGlow() const
The amount of glow [0..100].
Wrapper around an OpenGL vertex shader: a program executed on the GPU that computes per-vertex stuff...
Definition: GLUtil.hh:367
DisplayDeform getDisplayDeform()
Display deformation (normal, 3d) ATM this only works when using the SDLGL-PP renderer.
unsigned getWidth() const
Get the width of (all) lines in this frame.
Definition: FrameSource.hh:59
A frame buffer where pixels can be written to.
T radians(T d)
Definition: gl_vec.hh:160
const Pixel * getMultiLinePtr(int line, unsigned numLines, unsigned &actualLines, unsigned width, Pixel *buf) const
Similar to the above getLinePtr() method, but now tries to get multiple lines at once.
Definition: FrameSource.hh:113
gl::ivec2 getViewSize() const
mat4 scale(const vec3 &xyz)
Definition: gl_transform.hh:19
void bind()
Makes this texture the active GL texture.
Definition: GLUtil.hh:77
void attach(const Shader &shader)
Adds a given shader to this program.
Definition: GLUtil.cc:267
auto find_if(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:113
void paint(OutputSurface &output) override
Paint this layer.
void attach(Observer< T > &observer)
Definition: Subject.hh:50
constexpr auto data(C &c) -> decltype(c.data())
Definition: span.hh:69
RenderSettings & renderSettings
Render settings.
std::unique_ptr< Context > context
Definition: GLContext.cc:9
bool isViewScaled() const
void update(const Setting &setting) override
Definition: VideoLayer.cc:49
FloatSetting & getNoiseSetting()
The amount of noise to add to the frame.
GLsizei getWidth() const
Definition: GLUtil.hh:106
ScaleAlgorithm getScaleAlgorithm() const
The current scaling algorithm.
unsigned gcd(unsigned a, unsigned b)
Calculate greatest common divider of two strictly positive integers.
Definition: Math.hh:134
float random_float(float from, float upto)
Return a random float in the range [from, upto) (note: half-open interval).
Definition: random.hh:50
gl::ivec2 getViewOffset() const
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void resize(GLsizei width, GLsizei height)
Definition: GLUtil.cc:78
void setInterpolation(bool interpolation)
Enable/disable bilinear interpolation for this texture.
Definition: GLUtil.cc:54
vecN< 2, int > ivec2
Definition: gl_vec.hh:142
OutputSurface & screen
The surface which is visible to the user.
void bindAttribLocation(unsigned index, const char *name)
Bind the given name for a vertex shader attribute to the given location.
Definition: GLUtil.cc:299
int g
Abstract base class for post processors.
Thrown when a subsystem initialisation fails.
FrameSource * paintFrame
Represents a frame as it should be displayed.
void link()
Links all attached shaders together into one program.
Definition: GLUtil.cc:279
float getHorizontalStretch() const
vecN< 3, float > vec3
Definition: gl_vec.hh:140
const RawFrame * superImposeVideoFrame
Video frame on which to superimpose the (VDP) output.
FloatSetting & getHorizontalStretchSetting()
Amount of horizontal stretch.
void detach(Observer< T > &observer)
Definition: Subject.hh:56
bool getInterleaveBlackFrame() const
Is black frame interleaving enabled?
const Pixel * getLinePtr(int line, unsigned width, Pixel *buf) const
Gets a pointer to the pixels of the given line number.
Definition: FrameSource.hh:91
ITER find_if_unguarded(ITER first, ITER last, PRED pred)
Faster alternative to &#39;find_if&#39; when it&#39;s guaranteed that the predicate will be true for at least one...
Definition: stl.hh:145
mat4 translate(const vec3 &xyz)
Definition: gl_transform.hh:36
vecN< 2, float > vec2
Definition: gl_vec.hh:139
mat4 rotateX(float angle)
Definition: gl_transform.hh:93
virtual unsigned getRowLength() const
Returns the distance (in pixels) between two consecutive lines.
Definition: FrameSource.hh:186
Definition: gl_mat.hh:24
auto & global_urng()
Return reference to a (shared) global random number generator.
Definition: random.hh:8
auto end(const string_view &x)
Definition: string_view.hh:152
unsigned getHeight() const
Gets the number of lines in this frame.
Definition: FrameSource.hh:44
virtual std::unique_ptr< RawFrame > rotateFrames(std::unique_ptr< RawFrame > finishedFrame, EmuTime::param time)
Sets up the "abcdFrame" variables for a new frame.
ScaleAlgorithm
Scaler algorithm.
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
Definition: vla.hh:44