21 GLPostProcessor::GLPostProcessor(
24 unsigned maxWidth_,
unsigned height_,
bool canDoInterlace_)
26 videoSource, maxWidth_, height_, canDoInterlace_)
27 , noiseTextureA(true, true)
28 , noiseTextureB(true, true)
43 glTexImage2D(GL_TEXTURE_2D,
57 monitor3DProg.
attach(vertexShader);
58 monitor3DProg.
attach(fragmentShader);
75 void GLPostProcessor::initBuffers()
78 static constexpr
vec2 pos_tex[4 + 4] = {
82 glBindBuffer(GL_ARRAY_BUFFER, vbo.
get());
83 glBufferData(GL_ARRAY_BUFFER,
sizeof(pos_tex), pos_tex, GL_STATIC_DRAW);
84 glBindBuffer(GL_ARRAY_BUFFER, 0);
87 void GLPostProcessor::createRegions()
94 unsigned g = std::gcd(srcHeight, dstHeight);
95 unsigned srcStep = srcHeight /
g;
96 unsigned dstStep = dstHeight /
g;
100 unsigned srcStartY = 0;
101 unsigned dstStartY = 0;
102 while (dstStartY < dstHeight) {
105 assert(srcStartY < srcHeight);
109 unsigned srcEndY = srcStartY + srcStep;
110 unsigned dstEndY = dstStartY + dstStep;
111 while ((srcEndY < srcHeight) && (dstEndY < dstHeight) &&
117 regions.emplace_back(srcStartY, srcEndY,
132 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
133 glClear(GL_COLOR_BUFFER_BIT);
142 (horStretch != 320.0f) ||
148 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
149 glClear(GL_COLOR_BUFFER_BIT);
157 if (scaleAlgorithm != algo) {
158 scaleAlgorithm = algo;
174 if (renderToTexture) {
175 glViewport(0, 0, scrnWidth, scrnHeight);
176 glBindTexture(GL_TEXTURE_2D, 0);
177 fbo[frameCounter & 1].
push();
180 for (
auto& r : regions) {
183 auto it =
find_unguarded(textures, r.lineWidth, &TextureData::width);
185 ? &superImposeTex :
nullptr;
186 currScaler->scaleImage(
187 it->tex, superImpose,
188 r.srcStartY, r.srcEndY, r.lineWidth,
189 r.dstStartY, r.dstEndY, scrnWidth,
196 if (renderToTexture) {
197 fbo[frameCounter & 1].
pop();
198 colorTex[frameCounter & 1].
bind();
201 glViewport(
x, y, w, h);
206 float x1 = (320.0f - float(horStretch)) / (2.0f * 320.0f);
207 float x2 = 1.0f - x1;
214 1.0f, 1.0f, 1.0f, 1.0f);
217 1, GL_FALSE, &I[0][0]);
219 glBindBuffer(GL_ARRAY_BUFFER, vbo.
get());
220 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0,
nullptr);
221 glEnableVertexAttribArray(0);
223 glBindBuffer(GL_ARRAY_BUFFER, stretchVBO.
get());
224 glBufferData(GL_ARRAY_BUFFER,
sizeof(tex), tex, GL_STREAM_DRAW);
225 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0,
nullptr);
226 glEnableVertexAttribArray(1);
228 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
230 glDisableVertexAttribArray(1);
231 glDisableVertexAttribArray(0);
232 glBindBuffer(GL_ARRAY_BUFFER, 0);
242 std::unique_ptr<RawFrame> finishedFrame, EmuTime::param time)
244 std::unique_ptr<RawFrame> reuseFrame =
256 auto& noiseSetting = renderSettings.getNoiseSetting();
257 auto& horizontalStretch = renderSettings.getHorizontalStretchSetting();
258 if (&
setting == &noiseSetting) {
259 preCalcNoise(noiseSetting.getDouble());
260 }
else if (&
setting == &horizontalStretch) {
261 preCalcMonitor3D(horizontalStretch.getDouble());
265 void GLPostProcessor::uploadFrame()
270 for (
auto& r : regions) {
275 uploadBlock(std::max<int>(0, r.srcStartY - before),
276 std::min<int>(srcHeight, r.srcEndY + after),
283 if (superImposeTex.
getWidth() != w ||
285 superImposeTex.
resize(w, h);
288 superImposeTex.
bind();
302 void GLPostProcessor::uploadBlock(
303 unsigned srcStartY,
unsigned srcEndY,
unsigned lineWidth)
306 auto it =
ranges::find(textures, lineWidth, &TextureData::width);
307 if (it ==
end(textures)) {
308 TextureData textureData;
310 textureData.tex.resize(lineWidth, height * 2);
311 textureData.pbo.setImage(lineWidth, height * 2);
312 textures.push_back(std::move(textureData));
313 it =
end(textures) - 1;
323 uint32_t* mapped = pbo.mapWrite();
324 for (
auto y :
xrange(srcStartY, srcEndY)) {
325 auto* dest = mapped + y * lineWidth;
328 memcpy(dest, data, lineWidth *
sizeof(uint32_t));
332 #if defined(__APPLE__)
336 if (lineWidth == 1 && srcStartY != 0 && srcStartY % 16 == 0) {
349 pbo.getOffset(0, srcStartY));
354 currScaler->uploadBlock(srcStartY, srcEndY, lineWidth, *
paintFrame);
358 void GLPostProcessor::drawGlow(
int glow)
360 if ((glow == 0) || !storedFrame)
return;
364 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
365 colorTex[(frameCounter & 1) ^ 1].bind();
367 1.0f, 1.0f, 1.0f, glow * 31 / 3200.0f);
369 glUniformMatrix4fv(
gl::context->unifTexMvp, 1, GL_FALSE, &I[0][0]);
371 glBindBuffer(GL_ARRAY_BUFFER, vbo.
get());
373 const vec2* offset =
nullptr;
374 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, offset);
376 glEnableVertexAttribArray(0);
378 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, offset);
379 glEnableVertexAttribArray(1);
381 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
383 glDisableVertexAttribArray(1);
384 glDisableVertexAttribArray(0);
385 glBindBuffer(GL_ARRAY_BUFFER, 0);
389 void GLPostProcessor::preCalcNoise(
float factor)
391 GLbyte buf1[256 * 256];
392 GLbyte buf2[256 * 256];
394 std::normal_distribution<float> distribution(0.0f, 1.0f);
395 for (
auto i :
xrange(256 * 256)) {
396 float r = distribution(generator);
397 int s =
std::clamp(
int(roundf(r * factor)), -255, 255);
398 buf1[i] = (s > 0) ? s : 0;
399 buf2[i] = (s < 0) ? -s : 0;
404 noiseTextureA.
bind();
415 #if OPENGL_VERSION >= OPENGL_3_3
416 GLint swizzleMask1[] = {GL_RED, GL_RED, GL_RED, GL_ONE};
417 glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask1);
420 noiseTextureB.
bind();
431 #if OPENGL_VERSION >= OPENGL_3_3
432 GLint swizzleMask2[] = {GL_RED, GL_RED, GL_RED, GL_ONE};
433 glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask2);
437 void GLPostProcessor::drawNoise()
443 static constexpr
vec2 pos[8][4] = {
444 { { -1, -1 }, { 1, -1 }, { 1, 1 }, { -1, 1 } },
445 { { -1, 1 }, { 1, 1 }, { 1, -1 }, { -1, -1 } },
446 { { -1, 1 }, { -1, -1 }, { 1, -1 }, { 1, 1 } },
447 { { 1, 1 }, { 1, -1 }, { -1, -1 }, { -1, 1 } },
448 { { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 } },
449 { { 1, -1 }, { -1, -1 }, { -1, 1 }, { 1, 1 } },
450 { { 1, -1 }, { 1, 1 }, { -1, 1 }, { -1, -1 } },
451 { { -1, -1 }, { -1, 1 }, { 1, 1 }, { 1, -1 } }
453 vec2 noise(noiseX, noiseY);
454 const vec2 tex[4] = {
455 noise +
vec2(0.0f, 1.875f),
456 noise +
vec2(2.0f, 1.875f),
457 noise +
vec2(2.0f, 0.0f ),
458 noise +
vec2(0.0f, 0.0f )
464 glBlendFunc(GL_ONE, GL_ONE);
465 glUniform4f(
gl::context->unifTexColor, 1.0f, 1.0f, 1.0f, 1.0f);
467 glUniformMatrix4fv(
gl::context->unifTexMvp, 1, GL_FALSE, &I[0][0]);
469 unsigned seq = frameCounter & 7;
470 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, pos[seq]);
471 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, tex);
472 glEnableVertexAttribArray(0);
473 glEnableVertexAttribArray(1);
475 noiseTextureA.
bind();
476 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
477 glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
478 noiseTextureB.
bind();
479 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
481 glDisableVertexAttribArray(1);
482 glDisableVertexAttribArray(0);
483 glBlendEquation(GL_FUNC_ADD);
496 void GLPostProcessor::preCalcMonitor3D(
float width)
501 constexpr
float GRID_SIZE2 = float(
GRID_SIZE) / 2.0f;
502 float s = width / 320.0f;
503 float b = (320.0f - width) / (2.0f * 320.0f);
507 Vertex& v = vertices[sx][sy];
508 float x = (sx - GRID_SIZE2) / GRID_SIZE2;
509 float y = (sy - GRID_SIZE2) / GRID_SIZE2;
521 uint16_t* ind = indices;
541 glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer.
get());
542 glBufferData(GL_ARRAY_BUFFER,
sizeof(vertices), vertices,
544 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer.
get());
545 glBufferData(GL_ELEMENT_ARRAY_BUFFER,
sizeof(indices), indices,
547 glBindBuffer(GL_ARRAY_BUFFER, 0);
548 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
557 mat4 mvp = proj * tran * rotX * scal;
563 1, GL_FALSE, &mvp[0][0]);
565 1, GL_FALSE, &normal[0][0]);
568 void GLPostProcessor::drawMonitor3D()
572 char* base =
nullptr;
573 glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer.
get());
574 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer.
get());
575 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex),
577 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex),
578 base +
sizeof(
vec3));
579 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE,
sizeof(Vertex),
580 base +
sizeof(
vec3) +
sizeof(
vec3));
581 glEnableVertexAttribArray(0);
582 glEnableVertexAttribArray(1);
583 glEnableVertexAttribArray(2);
585 glDrawElements(GL_TRIANGLE_STRIP,
NUM_INDICES, GL_UNSIGNED_SHORT,
nullptr);
586 glDisableVertexAttribArray(2);
587 glDisableVertexAttribArray(1);
588 glDisableVertexAttribArray(0);
590 glBindBuffer(GL_ARRAY_BUFFER, 0);
591 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.
unsigned getWidth() const
Get the width of (all) lines in this frame.
unsigned getHeight() const
Gets the number of lines in this frame.
const Pixel * getLinePtr(int line, unsigned width, Pixel *buf) const
Gets a pointer to the pixels of the given line number.
std::unique_ptr< RawFrame > rotateFrames(std::unique_ptr< RawFrame > finishedFrame, EmuTime::param time) override
Sets up the "abcdFrame" variables for a new frame.
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.
Class containing all settings for renderers.
int getGlow() const
The amount of glow [0..100].
bool getInterleaveBlackFrame() const
Is black frame interleaving enabled?
float getHorizontalStretch() const
FloatSetting & getNoiseSetting()
The amount of noise to add to the frame.
FloatSetting & getHorizontalStretchSetting()
Amount of horizontal stretch.
DisplayDeform getDisplayDeform()
Display deformation (normal, 3d) ATM this only works when using the SDLGL-PP renderer.
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
vecN< N, T > normalize(const vecN< N, T > &x)
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
std::optional< Context > context
constexpr mat4 frustum(float left, float right, float bottom, float top, float nearVal, float farVal)
constexpr mat4 translate(const vec3 &xyz)
constexpr mat4 scale(const vec3 &xyz)
void format(SectorAccessibleDisk &disk, bool dos1)
Format the given disk (= a single partition).
std::unique_ptr< GLScaler > createScaler(RenderSettings &renderSettings)
Instantiates a Scaler.
This file implemented 3 utility functions:
constexpr KeyMatrixPosition x
Keyboard bindings.
constexpr int NUM_INDICES
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)