25 unsigned maxWidth_,
unsigned height_,
bool canDoInterlace_)
27 videoSource, maxWidth_, height_, canDoInterlace_)
35 colorTex[i].setInterpolation(
true);
37 glTexImage2D(GL_TEXTURE_2D,
51 monitor3DProg.
attach(vertexShader);
52 monitor3DProg.
attach(fragmentShader);
69void GLPostProcessor::initBuffers()
72 static constexpr std::array pos_tex = {
76 glBindBuffer(GL_ARRAY_BUFFER, vbo.
get());
77 glBufferData(GL_ARRAY_BUFFER,
sizeof(pos_tex), pos_tex.data(), GL_STATIC_DRAW);
78 glBindBuffer(GL_ARRAY_BUFFER, 0);
81void GLPostProcessor::createRegions()
88 unsigned g = std::gcd(srcHeight, dstHeight);
89 unsigned srcStep = srcHeight /
g;
90 unsigned dstStep = dstHeight /
g;
94 unsigned srcStartY = 0;
95 unsigned dstStartY = 0;
96 while (dstStartY < dstHeight) {
99 assert(srcStartY < srcHeight);
103 unsigned srcEndY = srcStartY + srcStep;
104 unsigned dstEndY = dstStartY + dstStep;
105 while ((srcEndY < srcHeight) && (dstEndY < dstHeight) &&
111 regions.emplace_back(srcStartY, srcEndY,
126 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
127 glClear(GL_COLOR_BUFFER_BIT);
136 (horStretch != 320.0f) ||
142 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
143 glClear(GL_COLOR_BUFFER_BIT);
151 if (scaleAlgorithm != algo) {
152 scaleAlgorithm = algo;
168 if (renderToTexture) {
169 glViewport(0, 0, scrnWidth, scrnHeight);
170 glBindTexture(GL_TEXTURE_2D, 0);
171 fbo[frameCounter & 1].push();
174 for (
auto& r : regions) {
177 auto it =
find_unguarded(textures, r.lineWidth, &TextureData::width);
179 ? &superImposeTex :
nullptr;
180 currScaler->scaleImage(
181 it->tex, superImpose,
182 r.srcStartY, r.srcEndY, r.lineWidth,
183 r.dstStartY, r.dstEndY, scrnWidth,
190 if (renderToTexture) {
191 fbo[frameCounter & 1].pop();
192 colorTex[frameCounter & 1].bind();
195 glViewport(x, y, w, h);
200 float x1 = (320.0f - float(horStretch)) / (2.0f * 320.0f);
201 float x2 = 1.0f - x1;
207 glContext.progTex.activate();
208 glUniform4f(glContext.unifTexColor,
209 1.0f, 1.0f, 1.0f, 1.0f);
211 glUniformMatrix4fv(glContext.unifTexMvp,
212 1, GL_FALSE, &I[0][0]);
214 glBindBuffer(GL_ARRAY_BUFFER, vbo.
get());
215 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0,
nullptr);
216 glEnableVertexAttribArray(0);
218 glBindBuffer(GL_ARRAY_BUFFER, stretchVBO.
get());
219 glBufferData(GL_ARRAY_BUFFER,
sizeof(tex), tex.data(), GL_STREAM_DRAW);
220 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0,
nullptr);
221 glEnableVertexAttribArray(1);
223 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
225 glDisableVertexAttribArray(1);
226 glDisableVertexAttribArray(0);
227 glBindBuffer(GL_ARRAY_BUFFER, 0);
237 std::unique_ptr<RawFrame> finishedFrame, EmuTime::param time)
239 std::unique_ptr<RawFrame> reuseFrame =
251 auto& noiseSetting = renderSettings.getNoiseSetting();
252 auto& horizontalStretch = renderSettings.getHorizontalStretchSetting();
253 if (&
setting == &noiseSetting) {
254 preCalcNoise(noiseSetting.getFloat());
255 }
else if (&
setting == &horizontalStretch) {
256 preCalcMonitor3D(horizontalStretch.getFloat());
260void GLPostProcessor::uploadFrame()
265 for (
auto& r : regions) {
270 uploadBlock(narrow<unsigned>(
std::max(0, narrow<int>(r.srcStartY) - before)),
271 std::min(srcHeight, r.srcEndY + after),
278 if (superImposeTex.
getWidth() != w ||
280 superImposeTex.
resize(w, h);
283 superImposeTex.
bind();
297void GLPostProcessor::uploadBlock(
298 unsigned srcStartY,
unsigned srcEndY,
unsigned lineWidth)
301 auto it =
ranges::find(textures, lineWidth, &TextureData::width);
302 if (it ==
end(textures)) {
303 TextureData textureData;
305 textureData.tex.resize(narrow<GLsizei>(lineWidth),
306 narrow<GLsizei>(height * 2));
307 textureData.pbo.setImage(lineWidth, height * 2);
308 textures.push_back(std::move(textureData));
309 it =
end(textures) - 1;
319 uint32_t* mapped = pbo.mapWrite();
320 for (
auto y :
xrange(srcStartY, srcEndY)) {
321 auto* dest = mapped + y * size_t(lineWidth);
323 if (line.data() != dest) {
328#if defined(__APPLE__)
332 if (lineWidth == 1 && srcStartY != 0 && srcStartY % 16 == 0) {
340 narrow<GLint>(srcStartY),
341 narrow<GLint>(lineWidth),
342 narrow<GLint>(srcEndY - srcStartY),
345 pbo.getOffset(0, srcStartY));
350 currScaler->uploadBlock(srcStartY, srcEndY, lineWidth, *
paintFrame);
354void GLPostProcessor::drawGlow(
int glow)
356 if ((glow == 0) || !storedFrame)
return;
359 glContext.progTex.activate();
361 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
362 colorTex[(frameCounter & 1) ^ 1].bind();
363 glUniform4f(glContext.unifTexColor,
364 1.0f, 1.0f, 1.0f, narrow<float>(glow) * 31.0f / 3200.0f);
366 glUniformMatrix4fv(glContext.unifTexMvp, 1, GL_FALSE, &I[0][0]);
368 glBindBuffer(GL_ARRAY_BUFFER, vbo.
get());
370 const vec2* offset =
nullptr;
371 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, offset);
373 glEnableVertexAttribArray(0);
375 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, offset);
376 glEnableVertexAttribArray(1);
378 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
380 glDisableVertexAttribArray(1);
381 glDisableVertexAttribArray(0);
382 glBindBuffer(GL_ARRAY_BUFFER, 0);
386void GLPostProcessor::preCalcNoise(
float factor)
388 std::array<uint8_t, 256 * 256> buf1;
389 std::array<uint8_t, 256 * 256> buf2;
391 std::normal_distribution<float> distribution(0.0f, 1.0f);
392 for (
auto i :
xrange(256 * 256)) {
393 float r = distribution(generator);
394 int s =
std::clamp(
int(roundf(r * factor)), -255, 255);
395 buf1[i] = narrow<uint8_t>((s > 0) ? s : 0);
396 buf2[i] = narrow<uint8_t>((s < 0) ? -s : 0);
401 noiseTextureA.
bind();
412#if OPENGL_VERSION >= OPENGL_3_3
413 GLint swizzleMask1[] = {GL_RED, GL_RED, GL_RED, GL_ONE};
414 glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask1);
417 noiseTextureB.
bind();
428#if OPENGL_VERSION >= OPENGL_3_3
429 GLint swizzleMask2[] = {GL_RED, GL_RED, GL_RED, GL_ONE};
430 glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask2);
434void GLPostProcessor::drawNoise()
440 static constexpr std::array pos = {
450 vec2 noise(noiseX, noiseY);
451 const std::array tex = {
452 noise +
vec2(0.0f, 1.875f),
453 noise +
vec2(2.0f, 1.875f),
454 noise +
vec2(2.0f, 0.0f ),
455 noise +
vec2(0.0f, 0.0f ),
459 glContext.progTex.activate();
462 glBlendFunc(GL_ONE, GL_ONE);
463 glUniform4f(glContext.unifTexColor, 1.0f, 1.0f, 1.0f, 1.0f);
465 glUniformMatrix4fv(glContext.unifTexMvp, 1, GL_FALSE, &I[0][0]);
467 unsigned seq = frameCounter & 7;
468 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, pos[seq].data());
469 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, tex.data());
470 glEnableVertexAttribArray(0);
471 glEnableVertexAttribArray(1);
473 noiseTextureA.
bind();
474 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
475 glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
476 noiseTextureB.
bind();
477 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
479 glDisableVertexAttribArray(1);
480 glDisableVertexAttribArray(0);
481 glBlendEquation(GL_FUNC_ADD);
485static constexpr int GRID_SIZE = 16;
486static constexpr int GRID_SIZE1 = GRID_SIZE + 1;
487static constexpr int NUM_INDICES = (GRID_SIZE1 * 2 + 2) * GRID_SIZE - 2;
494void GLPostProcessor::preCalcMonitor3D(
float width)
497 std::array<std::array<Vertex, GRID_SIZE1>, GRID_SIZE1> vertices;
499 constexpr float GRID_SIZE2 = float(GRID_SIZE) / 2.0f;
500 float s = width / 320.0f;
501 float b = (320.0f - width) / (2.0f * 320.0f);
503 for (
auto sx :
xrange(GRID_SIZE1)) {
504 for (
auto sy :
xrange(GRID_SIZE1)) {
505 Vertex& v = vertices[sx][sy];
506 float x = (narrow<float>(sx) - GRID_SIZE2) / GRID_SIZE2;
507 float y = (narrow<float>(sy) - GRID_SIZE2) / GRID_SIZE2;
511 v.
tex =
vec2((
float(sx) / GRID_SIZE) * s + b,
512 float(sy) / GRID_SIZE);
517 std::array<uint16_t, NUM_INDICES> indices;
519 uint16_t* ind = indices.data();
520 for (
auto y :
xrange(GRID_SIZE)) {
521 for (
auto x :
xrange(GRID_SIZE1)) {
522 *ind++ = narrow<uint16_t>((y + 0) * GRID_SIZE1 + x);
523 *ind++ = narrow<uint16_t>((y + 1) * GRID_SIZE1 + x);
528 assert((ind - indices.data()) == NUM_INDICES + 2);
529 ind = indices.data();
530 repeat(GRID_SIZE - 1, [&] {
531 ind += 2 * GRID_SIZE1;
539 glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer.
get());
540 glBufferData(GL_ARRAY_BUFFER,
sizeof(vertices), vertices.data(),
542 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer.
get());
543 glBufferData(GL_ELEMENT_ARRAY_BUFFER,
sizeof(indices), indices.data(),
545 glBindBuffer(GL_ARRAY_BUFFER, 0);
546 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
555 mat4 mvp = proj * tran * rotX * scal;
561 1, GL_FALSE, &mvp[0][0]);
563 1, GL_FALSE, &normal[0][0]);
566void GLPostProcessor::drawMonitor3D()
570 char* base =
nullptr;
571 glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer.
get());
572 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer.
get());
573 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex),
575 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex),
576 base +
sizeof(
vec3));
577 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE,
sizeof(Vertex),
578 base +
sizeof(
vec3) +
sizeof(
vec3));
579 glEnableVertexAttribArray(0);
580 glEnableVertexAttribArray(1);
581 glEnableVertexAttribArray(2);
583 glDrawElements(GL_TRIANGLE_STRIP, NUM_INDICES, GL_UNSIGNED_SHORT,
nullptr);
584 glDisableVertexAttribArray(2);
585 glDisableVertexAttribArray(1);
586 glDisableVertexAttribArray(0);
588 glBindBuffer(GL_ARRAY_BUFFER, 0);
589 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
GLsizei getHeight() const
void resize(GLsizei width, GLsizei height)
Wrapper around an OpenGL fragment shader: a program executed on the GPU that computes the colors of p...
void activate() const
Makes this program the active shader program.
void attach(const Shader &shader)
Adds a given shader to this program.
void link()
Links all attached shaders together into one program.
void bindAttribLocation(unsigned index, const char *name)
Bind the given name for a vertex shader attribute to the given location.
GLint getUniformLocation(const char *name) const
Gets a reference to a uniform variable declared in the shader source.
void setInterpolation(bool interpolation)
Enable/disable bilinear interpolation for this texture.
void bind()
Makes this texture the active GL texture.
Wrapper around an OpenGL vertex shader: a program executed on the GPU that computes per-vertex stuff.
Represents the output window/screen of openMSX.
std::span< const Pixel > getLine(int line, std::span< Pixel > buf) const
Gets a pointer to the pixels of the given line number.
unsigned getWidth() const
Get the width of (all) lines in this frame.
unsigned getHeight() const
Gets the number of lines in this frame.
std::unique_ptr< RawFrame > rotateFrames(std::unique_ptr< RawFrame > finishedFrame, EmuTime::param time) override
Sets up the "abcdFrame" variables for a new frame.
GLPostProcessor(MSXMotherBoard &motherBoard, Display &display, OutputSurface &screen, const std::string &videoSource, unsigned maxWidth, unsigned height, bool canDoInterlace)
void paint(OutputSurface &output) override
Paint this layer.
~GLPostProcessor() override
void update(const Setting &setting) noexcept override
A frame buffer where pixels can be written to.
gl::ivec2 getLogicalSize() const
bool isViewScaled() const
gl::ivec2 getViewOffset() const
gl::ivec2 getViewSize() const
int getLogicalHeight() const
Abstract base class for post processors.
virtual std::unique_ptr< RawFrame > rotateFrames(std::unique_ptr< RawFrame > finishedFrame, EmuTime::param time)
Sets up the "abcdFrame" variables for a new frame.
FrameSource * paintFrame
Represents a frame as it should be displayed.
const RawFrame * superImposeVideoFrame
Video frame on which to superimpose the (VDP) output.
static unsigned getLineWidth(FrameSource *frame, unsigned y, unsigned step)
Returns the maximum width for lines [y..y+step).
RenderSettings & renderSettings
Render settings.
OutputSurface & screen
The surface which is visible to the user.
int getGlow() const
The amount of glow [0..100].
bool getInterleaveBlackFrame() const
Is black frame interleaving enabled?
FloatSetting & getNoiseSetting()
The amount of noise to add to the frame.
float getHorizontalStretch() const
DisplayDeform getDisplayDeform()
Display deformation (normal, 3d) ATM this only works when using the SDLGL-PP renderer.
FloatSetting & getHorizontalStretchSetting()
Amount of horizontal stretch.
ScaleAlgorithm getScaleAlgorithm() const
The current scaling algorithm.
void detach(Observer< T > &observer)
void attach(Observer< T > &observer)
void update(const Setting &setting) noexcept override
mat4 rotateX(float angle)
matMxN< 3, 3, float > mat3
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
std::optional< Context > context
constexpr mat4 frustum(float left, float right, float bottom, float top, float nearVal, float farVal)
vecN< N, T > normalize(const vecN< N, T > &x)
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
constexpr mat4 translate(const vec3 &xyz)
constexpr mat4 scale(const vec3 &xyz)
void format(SectorAccessibleDisk &disk, MSXBootSectorType bootType)
Format the given disk (= a single partition).
std::unique_ptr< GLScaler > createScaler(RenderSettings &renderSettings)
Instantiates a Scaler.
This file implemented 3 utility functions:
auto copy(InputRange &&range, OutputIter out)
auto find(InputRange &&range, const T &value)
auto & global_urng()
Return reference to a (shared) global random number generator.
float random_float(float from, float upto)
Return a random float in the range [from, upto) (note: half-open interval).
ITER find_unguarded(ITER first, ITER last, const VAL &val, Proj proj={})
Faster alternative to 'find' when it's guaranteed that the value will be found (if not the behavior i...
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
constexpr auto xrange(T e)
constexpr auto end(const zstring_view &x)