| 1 |
#include "GLHQScaler.hh" |
| 2 |
|
| 3 |
#include "File.hh" |
| 4 |
#include "FileContext.hh" |
| 5 |
#include "FrameSource.hh" |
| 6 |
#include "GLUtil.hh" |
| 7 |
#include "HQCommon.hh" |
| 8 |
|
| 9 |
#include "inplace_buffer.hh" |
| 10 |
#include "narrow.hh" |
| 11 |
|
| 12 |
#include <algorithm> |
| 13 |
#include <array> |
| 14 |
#include <cstring> |
| 15 |
#include <utility> |
| 16 |
|
| 17 |
namespace openmsx { |
| 18 |
|
| 19 |
GLHQScaler::GLHQScaler(GLScaler& fallback_, unsigned maxWidth_, unsigned maxHeight_) |
| 20 |
: GLScaler("hq") |
| 21 |
, fallback(fallback_) |
| 22 |
, maxWidth(maxWidth_), maxHeight(maxHeight_) |
| 23 |
{ |
| 24 |
for (auto i : xrange(2)) { |
| 25 |
auto& p = program[i]; |
| 26 |
p.activate(); |
| 27 |
glUniform1i(p.getUniformLocation("edgeTex"), 2); |
| 28 |
glUniform1i(p.getUniformLocation("offsetTex"), 3); |
| 29 |
glUniform1i(p.getUniformLocation("weightTex"), 4); |
| 30 |
edgePosScaleUnif[i] = p.getUniformLocation("edgePosScale"); |
| 31 |
} |
| 32 |
|
| 33 |
|
| 34 |
auto format = (OPENGL_VERSION >= OPENGL_3_3) ? GL_RG : GL_LUMINANCE_ALPHA; |
| 35 |
edgeTexture.bind(); |
| 36 |
glTexImage2D(GL_TEXTURE_2D, |
| 37 |
0, |
| 38 |
format, |
| 39 |
maxWidth, |
| 40 |
maxHeight, |
| 41 |
0, |
| 42 |
format, |
| 43 |
GL_UNSIGNED_BYTE, |
| 44 |
nullptr); |
| 45 |
#if OPENGL_VERSION >= OPENGL_3_3 |
| 46 |
GLint swizzleMask[] = {GL_RED, GL_RED, GL_RED, GL_ONE}; |
| 47 |
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); |
| 48 |
#endif |
| 49 |
edgeBuffer.allocate(maxWidth * maxHeight); |
| 50 |
|
| 51 |
const auto& context = systemFileContext(); |
| 52 |
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |
| 53 |
std::string offsetsName = "shaders/HQ_xOffsets.dat"; |
| 54 |
std::string weightsName = "shaders/HQ_xWeights.dat"; |
| 55 |
for (auto i : xrange(3)) { |
| 56 |
int n = i + 2; |
| 57 |
offsetsName[10] = narrow<char>('0' + n); |
| 58 |
auto offsets = File(context.resolve(offsetsName)).mmap<const uint8_t>(); |
| 59 |
offsetTexture[i].bind(); |
| 60 |
glTexImage2D(GL_TEXTURE_2D, |
| 61 |
0, |
| 62 |
GL_RGBA, |
| 63 |
n * 64, |
| 64 |
n * 64, |
| 65 |
0, |
| 66 |
GL_RGBA, |
| 67 |
GL_UNSIGNED_BYTE, |
| 68 |
offsets.data()); |
| 69 |
|
| 70 |
weightsName[10] = narrow<char>('0' + n); |
| 71 |
auto weights = File(context.resolve(weightsName)).mmap<const uint8_t>(); |
| 72 |
weightTexture[i].bind(); |
| 73 |
glTexImage2D(GL_TEXTURE_2D, |
| 74 |
0, |
| 75 |
GL_RGB, |
| 76 |
n * 64, |
| 77 |
n * 64, |
| 78 |
0, |
| 79 |
GL_RGB, |
| 80 |
GL_UNSIGNED_BYTE, |
| 81 |
weights.data()); |
| 82 |
} |
| 83 |
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); |
| 84 |
} |
| 85 |
|
| 86 |
void GLHQScaler::scaleImage( |
| 87 |
gl::ColorTexture& src, gl::ColorTexture* superImpose, |
| 88 |
unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, |
| 89 |
unsigned dstStartY, unsigned dstEndY, unsigned dstWidth, |
| 90 |
unsigned logSrcHeight) |
| 91 |
{ |
| 92 |
unsigned factorY = (dstEndY - dstStartY) / (srcEndY - srcStartY); |
| 93 |
|
| 94 |
if ((factorY >= 2) && ((srcWidth % 320) == 0)) { |
| 95 |
assert(src.getHeight() == 2 * 240); |
| 96 |
setup(superImpose != nullptr); |
| 97 |
glActiveTexture(GL_TEXTURE4); |
| 98 |
weightTexture[factorY - 2].bind(); |
| 99 |
glActiveTexture(GL_TEXTURE3); |
| 100 |
offsetTexture[factorY - 2].bind(); |
| 101 |
glActiveTexture(GL_TEXTURE2); |
| 102 |
edgeTexture.bind(); |
| 103 |
glActiveTexture(GL_TEXTURE0); |
| 104 |
|
| 105 |
int i = superImpose ? 1 : 0; |
| 106 |
glUniform2f(edgePosScaleUnif[i], |
| 107 |
float(src.getWidth()) / float(maxWidth), |
| 108 |
float(src.getHeight()) / float(maxHeight)); |
| 109 |
|
| 110 |
execute(src, superImpose, |
| 111 |
srcStartY, srcEndY, srcWidth, |
| 112 |
dstStartY, dstEndY, dstWidth, |
| 113 |
logSrcHeight); |
| 114 |
} else { |
| 115 |
fallback.scaleImage(src, superImpose, |
| 116 |
srcStartY, srcEndY, srcWidth, |
| 117 |
dstStartY, dstEndY, dstWidth, |
| 118 |
logSrcHeight); |
| 119 |
} |
| 120 |
} |
| 121 |
|
| 122 |
using Pixel = uint32_t; |
| 123 |
void GLHQScaler::uploadBlock( |
| 124 |
unsigned srcStartY, unsigned srcEndY, unsigned lineWidth, |
| 125 |
FrameSource& paintFrame) |
| 126 |
{ |
| 127 |
if ((lineWidth % 320) != 0) return; |
| 128 |
|
| 129 |
assert(maxWidth <= 1280); |
| 130 |
inplace_buffer<Endian::L32, 1280 / 2> tmpBuf2(uninitialized_tag{}, lineWidth / 2); |
| 131 |
#ifndef NDEBUG |
| 132 |
|
| 133 |
std::ranges::fill(tmpBuf2, 0); |
| 134 |
#endif |
| 135 |
|
| 136 |
inplace_buffer<Pixel, 1280> buf1(uninitialized_tag{}, lineWidth); |
| 137 |
inplace_buffer<Pixel, 1280> buf2(uninitialized_tag{}, lineWidth); |
| 138 |
auto curr = paintFrame.getLine(narrow<int>(srcStartY) - 1, buf1); |
| 139 |
auto next = paintFrame.getLine(narrow<int>(srcStartY) + 0, buf2); |
| 140 |
EdgeHQ edgeOp; |
| 141 |
calcEdgesGL(curr, next, tmpBuf2, edgeOp); |
| 142 |
|
| 143 |
edgeBuffer.bind(); |
| 144 |
auto mapped = edgeBuffer.mapWrite(); |
| 145 |
auto numLines = srcEndY - srcStartY; |
| 146 |
for (auto yy : xrange(numLines)) { |
| 147 |
curr = next; |
| 148 |
std::swap(buf1, buf2); |
| 149 |
next = paintFrame.getLine(narrow<int>(yy + srcStartY + 1), buf2); |
| 150 |
calcEdgesGL(curr, next, tmpBuf2, edgeOp); |
| 151 |
auto dest = mapped.subspan(yy * size_t(lineWidth), lineWidth); |
| 152 |
assert(dest.size_bytes() == std::span{tmpBuf2}.size_bytes()); |
| 153 |
memcpy(dest.data(), tmpBuf2.data(), std::span{tmpBuf2}.size_bytes()); |
| 154 |
} |
| 155 |
edgeBuffer.unmap(); |
| 156 |
|
| 157 |
auto format = (OPENGL_VERSION >= OPENGL_3_3) ? GL_RG : GL_LUMINANCE_ALPHA; |
| 158 |
edgeTexture.bind(); |
| 159 |
glTexSubImage2D(GL_TEXTURE_2D, |
| 160 |
0, |
| 161 |
0, |
| 162 |
narrow<GLint>(srcStartY), |
| 163 |
narrow<GLint>(lineWidth), |
| 164 |
narrow<GLint>(numLines), |
| 165 |
format, |
| 166 |
GL_UNSIGNED_BYTE, |
| 167 |
mapped.data()); |
| 168 |
edgeBuffer.unbind(); |
| 169 |
} |
| 170 |
|
| 171 |
} |