2022-02-27 14:56:51 +02:00
|
|
|
/*
|
|
|
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
|
|
|
* running old operating systems and software designed for IBM
|
|
|
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
|
|
|
* system designs based on the PCI bus.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of the 86Box distribution.
|
|
|
|
|
*
|
|
|
|
|
* OpenGL renderer for Qt
|
|
|
|
|
*
|
|
|
|
|
* Authors:
|
|
|
|
|
* Teemu Korhonen
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2022 Teemu Korhonen
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <QCoreApplication>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QOpenGLShaderProgram>
|
2022-03-01 22:21:43 +02:00
|
|
|
#include <QOpenGLTexture>
|
2022-03-05 02:57:57 +02:00
|
|
|
#include <QStringBuilder>
|
2022-02-27 14:56:51 +02:00
|
|
|
#include <QSurfaceFormat>
|
|
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
#include "qt_opengloptionsdialog.hpp"
|
|
|
|
|
#include "qt_openglrenderer.hpp"
|
|
|
|
|
|
2022-08-01 12:45:38 +06:00
|
|
|
#ifndef GL_MAP_PERSISTENT_BIT
|
|
|
|
|
#define GL_MAP_PERSISTENT_BIT 0x0040
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef GL_MAP_COHERENT_BIT
|
|
|
|
|
#define GL_MAP_COHERENT_BIT 0x0080
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-02-27 14:56:51 +02:00
|
|
|
OpenGLRenderer::OpenGLRenderer(QWidget *parent)
|
|
|
|
|
: QWindow(parent->windowHandle())
|
|
|
|
|
, renderTimer(new QTimer(this))
|
2022-07-07 14:34:59 +06:00
|
|
|
, options(nullptr)
|
2022-02-27 14:56:51 +02:00
|
|
|
{
|
|
|
|
|
renderTimer->setTimerType(Qt::PreciseTimer);
|
|
|
|
|
/* TODO: need's more accuracy, maybe target 1ms earlier and spin yield */
|
|
|
|
|
connect(renderTimer, &QTimer::timeout, this, &OpenGLRenderer::render);
|
|
|
|
|
|
|
|
|
|
buf_usage = std::vector<std::atomic_flag>(BUFFERCOUNT);
|
|
|
|
|
for (auto &flag : buf_usage)
|
|
|
|
|
flag.clear();
|
|
|
|
|
|
|
|
|
|
setSurfaceType(QWindow::OpenGLSurface);
|
|
|
|
|
|
|
|
|
|
QSurfaceFormat format;
|
|
|
|
|
|
2022-03-06 01:06:47 +02:00
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
|
format.setVersion(4, 1);
|
|
|
|
|
#else
|
|
|
|
|
format.setVersion(3, 2);
|
|
|
|
|
#endif
|
2022-02-27 14:56:51 +02:00
|
|
|
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
|
2022-03-05 02:57:57 +02:00
|
|
|
|
2022-03-01 22:21:43 +02:00
|
|
|
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES)
|
|
|
|
|
format.setRenderableType(QSurfaceFormat::OpenGLES);
|
2022-02-27 14:56:51 +02:00
|
|
|
|
|
|
|
|
setFormat(format);
|
|
|
|
|
|
|
|
|
|
parentWidget = parent;
|
|
|
|
|
|
|
|
|
|
source.setRect(0, 0, INIT_WIDTH, INIT_HEIGHT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OpenGLRenderer::~OpenGLRenderer()
|
|
|
|
|
{
|
|
|
|
|
finalize();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRenderer::exposeEvent(QExposeEvent *event)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(event);
|
|
|
|
|
|
|
|
|
|
if (!isInitialized)
|
|
|
|
|
initialize();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRenderer::resizeEvent(QResizeEvent *event)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(event);
|
|
|
|
|
|
|
|
|
|
onResize(event->size().width(), event->size().height());
|
|
|
|
|
|
|
|
|
|
if (notReady())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
context->makeCurrent(this);
|
|
|
|
|
|
|
|
|
|
glViewport(
|
|
|
|
|
destination.x(),
|
|
|
|
|
destination.y(),
|
2022-03-02 15:08:14 +06:00
|
|
|
destination.width() * devicePixelRatio(),
|
|
|
|
|
destination.height() * devicePixelRatio());
|
2022-02-27 14:56:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
OpenGLRenderer::event(QEvent *event)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(event);
|
|
|
|
|
|
|
|
|
|
bool res = false;
|
|
|
|
|
if (!eventDelegate(event, res))
|
|
|
|
|
return QWindow::event(event);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRenderer::initialize()
|
|
|
|
|
{
|
2022-03-05 02:57:57 +02:00
|
|
|
try {
|
|
|
|
|
context = new QOpenGLContext(this);
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
context->setFormat(format());
|
2022-02-28 22:13:11 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
if (!context->create())
|
|
|
|
|
throw opengl_init_error(tr("Couldn't create OpenGL context."));
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
if (!context->makeCurrent(this))
|
|
|
|
|
throw opengl_init_error(tr("Couldn't switch to OpenGL context."));
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-06 01:06:47 +02:00
|
|
|
auto version = context->format().version();
|
|
|
|
|
|
|
|
|
|
if (version.first < 3)
|
|
|
|
|
throw opengl_init_error(tr("OpenGL version 3.0 or greater is required. Current version is %1.%2").arg(version.first).arg(version.second));
|
|
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
initializeOpenGLFunctions();
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-06 01:06:47 +02:00
|
|
|
/* Prepare the shader version string */
|
|
|
|
|
glslVersion = reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION));
|
|
|
|
|
glslVersion.truncate(4);
|
|
|
|
|
glslVersion.remove('.');
|
|
|
|
|
glslVersion.prepend("#version ");
|
|
|
|
|
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES)
|
|
|
|
|
glslVersion.append(" es");
|
|
|
|
|
else if (context->format().profile() == QSurfaceFormat::CoreProfile)
|
|
|
|
|
glslVersion.append(" core");
|
|
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
initializeExtensions();
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
initializeBuffers();
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
/* Vertex, texture 2d coordinates and color (white) making a quad as triangle strip */
|
|
|
|
|
const GLfloat surface[] = {
|
|
|
|
|
-1.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f,
|
|
|
|
|
1.f, 1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 1.f,
|
|
|
|
|
-1.f, -1.f, 0.f, 1.f, 1.f, 1.f, 1.f, 1.f,
|
|
|
|
|
1.f, -1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f
|
|
|
|
|
};
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
glGenVertexArrays(1, &vertexArrayID);
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
glBindVertexArray(vertexArrayID);
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
glGenBuffers(1, &vertexBufferID);
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
|
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(surface), surface, GL_STATIC_DRAW);
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
glGenTextures(1, &textureID);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureID);
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
const GLfloat border_color[] = { 0.f, 0.f, 0.f, 1.f };
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, QOpenGLTexture::RGBA8_UNorm, INIT_WIDTH, INIT_HEIGHT, 0, QOpenGLTexture::BGRA, QOpenGLTexture::UInt32_RGBA8_Rev, NULL);
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-07-07 14:34:59 +06:00
|
|
|
reloadOptions();
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
glClearColor(0.f, 0.f, 0.f, 1.f);
|
|
|
|
|
|
|
|
|
|
glViewport(
|
|
|
|
|
destination.x(),
|
|
|
|
|
destination.y(),
|
|
|
|
|
destination.width() * devicePixelRatio(),
|
|
|
|
|
destination.height() * devicePixelRatio());
|
|
|
|
|
|
|
|
|
|
GLenum error = glGetError();
|
|
|
|
|
if (error != GL_NO_ERROR)
|
|
|
|
|
throw opengl_init_error(tr("OpenGL initialization failed. Error %1.").arg(error));
|
|
|
|
|
|
|
|
|
|
isInitialized = true;
|
|
|
|
|
|
|
|
|
|
emit initialized();
|
|
|
|
|
|
|
|
|
|
} catch (const opengl_init_error &e) {
|
|
|
|
|
/* Mark all buffers as in use */
|
|
|
|
|
for (auto &flag : buf_usage)
|
|
|
|
|
flag.test_and_set();
|
|
|
|
|
|
|
|
|
|
QMessageBox::critical((QWidget *) qApp->findChild<QWindow *>(), tr("Error initializing OpenGL"), e.what() % tr("\nFalling back to software rendering."));
|
|
|
|
|
|
|
|
|
|
context->doneCurrent();
|
|
|
|
|
isFinalized = true;
|
|
|
|
|
isInitialized = true;
|
|
|
|
|
|
|
|
|
|
emit errorInitializing();
|
|
|
|
|
}
|
2022-02-27 14:56:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRenderer::finalize()
|
|
|
|
|
{
|
|
|
|
|
if (isFinalized)
|
|
|
|
|
return;
|
|
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
renderTimer->stop();
|
|
|
|
|
|
2022-02-27 14:56:51 +02:00
|
|
|
context->makeCurrent(this);
|
|
|
|
|
|
|
|
|
|
if (hasBufferStorage)
|
|
|
|
|
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
|
|
|
|
|
|
|
|
|
glDeleteBuffers(1, &unpackBufferID);
|
|
|
|
|
glDeleteTextures(1, &textureID);
|
|
|
|
|
glDeleteBuffers(1, &vertexBufferID);
|
|
|
|
|
glDeleteVertexArrays(1, &vertexArrayID);
|
|
|
|
|
|
|
|
|
|
if (!hasBufferStorage && unpackBuffer)
|
|
|
|
|
free(unpackBuffer);
|
|
|
|
|
|
|
|
|
|
context->doneCurrent();
|
|
|
|
|
|
|
|
|
|
isFinalized = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDialog *
|
|
|
|
|
OpenGLRenderer::getOptions(QWidget *parent)
|
|
|
|
|
{
|
2022-03-06 01:06:47 +02:00
|
|
|
auto dialog = new OpenGLOptionsDialog(parent, *options, [this]() { return new OpenGLOptions(this, false, glslVersion); });
|
2022-02-27 14:56:51 +02:00
|
|
|
|
|
|
|
|
connect(dialog, &OpenGLOptionsDialog::optionsChanged, this, &OpenGLRenderer::updateOptions);
|
|
|
|
|
|
|
|
|
|
return dialog;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-03-05 02:57:57 +02:00
|
|
|
OpenGLRenderer::initializeExtensions()
|
2022-02-27 14:56:51 +02:00
|
|
|
{
|
2022-03-01 19:15:12 +02:00
|
|
|
#ifndef NO_BUFFER_STORAGE
|
2022-08-01 12:45:38 +06:00
|
|
|
if (context->hasExtension("GL_ARB_buffer_storage") || context->hasExtension("GL_EXT_buffer_storage")) {
|
2022-02-27 14:56:51 +02:00
|
|
|
hasBufferStorage = true;
|
|
|
|
|
|
2022-08-01 12:45:38 +06:00
|
|
|
glBufferStorage = (PFNGLBUFFERSTORAGEEXTPROC_LOCAL) context->getProcAddress(context->hasExtension("GL_EXT_buffer_storage") ? "glBufferStorageEXT" : "glBufferStorage");
|
|
|
|
|
if (!glBufferStorage)
|
|
|
|
|
glBufferStorage = glBufferStorage = (PFNGLBUFFERSTORAGEEXTPROC_LOCAL) context->getProcAddress("glBufferStorage");
|
2022-02-27 14:56:51 +02:00
|
|
|
}
|
2022-02-28 22:13:11 +02:00
|
|
|
#endif
|
2022-02-27 14:56:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2022-03-05 02:57:57 +02:00
|
|
|
OpenGLRenderer::initializeBuffers()
|
2022-02-27 14:56:51 +02:00
|
|
|
{
|
|
|
|
|
glGenBuffers(1, &unpackBufferID);
|
|
|
|
|
|
|
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBufferID);
|
|
|
|
|
|
|
|
|
|
if (hasBufferStorage) {
|
2022-03-01 19:15:12 +02:00
|
|
|
#ifndef NO_BUFFER_STORAGE
|
2022-02-27 14:56:51 +02:00
|
|
|
/* Create persistent buffer for pixel transfer. */
|
|
|
|
|
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, BUFFERBYTES * BUFFERCOUNT, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
|
|
|
|
|
|
|
|
|
|
unpackBuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, BUFFERBYTES * BUFFERCOUNT, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
|
2022-02-28 22:13:11 +02:00
|
|
|
#endif
|
2022-02-27 14:56:51 +02:00
|
|
|
} else {
|
|
|
|
|
/* Fallback; create our own buffer. */
|
|
|
|
|
unpackBuffer = malloc(BUFFERBYTES * BUFFERCOUNT);
|
|
|
|
|
|
2022-03-05 02:57:57 +02:00
|
|
|
if (unpackBuffer == nullptr)
|
|
|
|
|
throw opengl_init_error(tr("Allocating memory for unpack buffer failed."));
|
|
|
|
|
|
2022-02-27 14:56:51 +02:00
|
|
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, BUFFERBYTES * BUFFERCOUNT, NULL, GL_STREAM_DRAW);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRenderer::applyOptions()
|
|
|
|
|
{
|
2022-02-27 16:45:57 +02:00
|
|
|
/* TODO: change detection in options */
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-02-27 16:45:57 +02:00
|
|
|
if (options->framerate() > 0) {
|
2022-02-27 14:56:51 +02:00
|
|
|
int interval = (int) ceilf(1000.f / (float) options->framerate());
|
|
|
|
|
renderTimer->setInterval(std::chrono::milliseconds(interval));
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-27 16:45:57 +02:00
|
|
|
if (options->renderBehavior() == OpenGLOptions::TargetFramerate)
|
|
|
|
|
renderTimer->start();
|
|
|
|
|
else
|
|
|
|
|
renderTimer->stop();
|
|
|
|
|
|
|
|
|
|
auto format = this->format();
|
|
|
|
|
int interval = options->vSync() ? 1 : 0;
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-02-27 16:45:57 +02:00
|
|
|
if (format.swapInterval() != interval) {
|
|
|
|
|
format.setSwapInterval(interval);
|
2022-02-27 14:56:51 +02:00
|
|
|
setFormat(format);
|
|
|
|
|
context->setFormat(format);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-27 16:45:57 +02:00
|
|
|
GLint filter = options->filter() == OpenGLOptions::Linear ? GL_LINEAR : GL_NEAREST;
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
|
|
|
|
|
|
|
|
|
currentFilter = options->filter();
|
2022-02-27 14:56:51 +02:00
|
|
|
}
|
|
|
|
|
|
2022-07-07 14:34:59 +06:00
|
|
|
void
|
|
|
|
|
OpenGLRenderer::reloadOptions()
|
|
|
|
|
{
|
|
|
|
|
if (options) { delete options; options = nullptr; }
|
|
|
|
|
options = new OpenGLOptions(this, true, glslVersion);
|
|
|
|
|
|
|
|
|
|
applyOptions();
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-27 14:56:51 +02:00
|
|
|
void
|
|
|
|
|
OpenGLRenderer::applyShader(const OpenGLShaderPass &shader)
|
|
|
|
|
{
|
2022-02-27 20:22:28 +02:00
|
|
|
if (!shader.bind())
|
2022-02-27 14:56:51 +02:00
|
|
|
return;
|
|
|
|
|
|
2022-02-27 20:22:28 +02:00
|
|
|
if (shader.vertex_coord() != -1) {
|
|
|
|
|
glEnableVertexAttribArray(shader.vertex_coord());
|
|
|
|
|
glVertexAttribPointer(shader.vertex_coord(), 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0);
|
2022-02-27 14:56:51 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-27 20:22:28 +02:00
|
|
|
if (shader.tex_coord() != -1) {
|
|
|
|
|
glEnableVertexAttribArray(shader.tex_coord());
|
|
|
|
|
glVertexAttribPointer(shader.tex_coord(), 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void *) (2 * sizeof(GLfloat)));
|
2022-02-27 14:56:51 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-27 20:22:28 +02:00
|
|
|
if (shader.color() != -1) {
|
|
|
|
|
glEnableVertexAttribArray(shader.color());
|
|
|
|
|
glVertexAttribPointer(shader.color(), 4, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void *) (4 * sizeof(GLfloat)));
|
2022-02-27 14:56:51 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-27 20:22:28 +02:00
|
|
|
if (shader.mvp_matrix() != -1) {
|
2022-02-27 14:56:51 +02:00
|
|
|
static const GLfloat mvp[] = {
|
|
|
|
|
1.f, 0.f, 0.f, 0.f,
|
|
|
|
|
0.f, 1.f, 0.f, 0.f,
|
|
|
|
|
0.f, 0.f, 1.f, 0.f,
|
|
|
|
|
0.f, 0.f, 0.f, 1.f
|
|
|
|
|
};
|
2022-02-27 20:22:28 +02:00
|
|
|
glUniformMatrix4fv(shader.mvp_matrix(), 1, GL_FALSE, mvp);
|
2022-02-27 14:56:51 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-27 20:22:28 +02:00
|
|
|
if (shader.output_size() != -1)
|
|
|
|
|
glUniform2f(shader.output_size(), destination.width(), destination.height());
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-02-27 20:22:28 +02:00
|
|
|
if (shader.input_size() != -1)
|
|
|
|
|
glUniform2f(shader.input_size(), source.width(), source.height());
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-02-27 20:22:28 +02:00
|
|
|
if (shader.texture_size() != -1)
|
|
|
|
|
glUniform2f(shader.texture_size(), source.width(), source.height());
|
2022-02-27 14:56:51 +02:00
|
|
|
|
2022-02-27 20:22:28 +02:00
|
|
|
if (shader.frame_count() != -1)
|
|
|
|
|
glUniform1i(shader.frame_count(), frameCounter);
|
2022-02-27 14:56:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRenderer::render()
|
|
|
|
|
{
|
|
|
|
|
context->makeCurrent(this);
|
|
|
|
|
|
2022-02-27 16:45:57 +02:00
|
|
|
if (options->filter() != currentFilter)
|
2022-02-27 14:56:51 +02:00
|
|
|
applyOptions();
|
|
|
|
|
|
|
|
|
|
/* TODO: multiple shader passes */
|
|
|
|
|
applyShader(options->shaders().first());
|
|
|
|
|
|
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
|
|
|
|
|
|
context->swapBuffers(this);
|
|
|
|
|
|
|
|
|
|
frameCounter = (frameCounter + 1) & 1023;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRenderer::updateOptions(OpenGLOptions *newOptions)
|
|
|
|
|
{
|
|
|
|
|
context->makeCurrent(this);
|
|
|
|
|
|
|
|
|
|
glUseProgram(0);
|
|
|
|
|
|
|
|
|
|
delete options;
|
|
|
|
|
|
|
|
|
|
options = newOptions;
|
|
|
|
|
|
|
|
|
|
options->setParent(this);
|
|
|
|
|
|
|
|
|
|
applyOptions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::tuple<uint8_t *, std::atomic_flag *>>
|
|
|
|
|
OpenGLRenderer::getBuffers()
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::tuple<uint8_t *, std::atomic_flag *>> buffers;
|
|
|
|
|
|
|
|
|
|
if (notReady() || !unpackBuffer)
|
|
|
|
|
return buffers;
|
|
|
|
|
|
|
|
|
|
/* Split the buffer area */
|
|
|
|
|
for (int i = 0; i < BUFFERCOUNT; i++) {
|
|
|
|
|
buffers.push_back(std::make_tuple((uint8_t *) unpackBuffer + BUFFERBYTES * i, &buf_usage[i]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return buffers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OpenGLRenderer::onBlit(int buf_idx, int x, int y, int w, int h)
|
|
|
|
|
{
|
|
|
|
|
if (notReady())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
context->makeCurrent(this);
|
|
|
|
|
|
|
|
|
|
if (source.width() != w || source.height() != h) {
|
|
|
|
|
source.setRect(0, 0, w, h);
|
|
|
|
|
|
|
|
|
|
/* Resize the texture */
|
|
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
2022-03-05 02:57:57 +02:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, (GLenum) QOpenGLTexture::RGBA8_UNorm, source.width(), source.height(), 0, (GLenum) QOpenGLTexture::BGRA, (GLenum) QOpenGLTexture::UInt32_RGBA8_Rev, NULL);
|
2022-02-27 14:56:51 +02:00
|
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBufferID);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-28 21:55:58 +02:00
|
|
|
if (!hasBufferStorage)
|
2022-07-11 15:32:07 +06:00
|
|
|
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, BUFFERBYTES * buf_idx, h * ROW_LENGTH * sizeof(uint32_t) + (y * ROW_LENGTH * sizeof(uint32_t)), (uint8_t *) unpackBuffer + BUFFERBYTES * buf_idx);
|
2022-02-28 21:55:58 +02:00
|
|
|
|
2022-02-27 14:56:51 +02:00
|
|
|
glPixelStorei(GL_UNPACK_SKIP_PIXELS, BUFFERPIXELS * buf_idx + y * ROW_LENGTH + x);
|
|
|
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, ROW_LENGTH);
|
2022-03-05 02:57:57 +02:00
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, (GLenum) QOpenGLTexture::BGRA, (GLenum) QOpenGLTexture::UInt32_RGBA8_Rev, NULL);
|
2022-02-27 14:56:51 +02:00
|
|
|
|
|
|
|
|
/* TODO: check if fence sync is implementable here and still has any benefit. */
|
|
|
|
|
glFinish();
|
|
|
|
|
|
|
|
|
|
buf_usage[buf_idx].clear();
|
|
|
|
|
|
|
|
|
|
if (options->renderBehavior() == OpenGLOptions::SyncWithVideo)
|
|
|
|
|
render();
|
2022-03-01 13:31:19 +06:00
|
|
|
}
|