Commit 1eb3ee74 by Balázs Ludmány

Commit Arnoldnak!

parent cb68f729
......@@ -18,11 +18,10 @@ QML_IMPORT_PATH =
# Default rules for deployment.
include(deployment.pri)
LIBS += -L$$PWD/../libvnc/libvncclient/.libs/ -lvncclient -lEGL -ljpeg -L$$PWD/../build-texload-Desktop_Qt_5_9_0_GCC_64bit-Release -ltexload
unix|win32: LIBS += -L$$PWD/../libvnc/libvncclient/.libs/ -lvncclient -lEGL -ljpeg
INCLUDEPATH += $$PWD/../libvnc
DEPENDPATH += $$PWD/../libvnc
INCLUDEPATH += $$PWD/../libvnc $$PWD/../texload
DEPENDPATH += $$PWD/../libvnc $$PWD/../texload
HEADERS += \
vncrenderer.h \
......@@ -33,11 +32,12 @@ HEADERS += \
concurrentqueue.h
DISTFILES += \
draw_shader.fsh \
draw_shader.vsh \
copy_shader.vsh \
copy_shader.fsh \
fill_shader.vsh \
fill_shader.fsh \
bitmap_shader.vsh \
bitmap_shader.fsh
bitmap_shader.fsh \
ycbcr_shader.fsh
#QMAKE_CXXFLAGS_RELEASE -= -O2
uniform sampler2D texture;
varying highp vec4 vartexcoord;
varying highp vec2 vartexcoord;
void main(void)
{
gl_FragColor = texture2D(texture, vartexcoord.st);
//gl_FragColor = vec4(vartexcoord.st, 0.0, 1.0);
gl_FragColor = vec4(texture2D(texture, vartexcoord).rgb, 1.0);
}
uniform highp mat4 ortho;
uniform highp mat4 texortho;
uniform highp mat4 ortho; // transforms screen coordinates to [-1, 1]
uniform highp mat4 texortho; // transforms screen coordinates to [0, 1]
attribute highp vec2 position;
attribute highp vec2 texcoord;
varying highp vec4 vartexcoord;
varying highp vec2 vartexcoord;
void main(void)
{
gl_Position = ortho * vec4(position, 0.0, 1.0);
vartexcoord = texortho * vec4(texcoord, 0.0, 1.0);
vartexcoord = vec4(texortho * vec4(position, 0.0, 1.0)).xy;
}
......@@ -3,6 +3,7 @@
#include <QSemaphore>
#include <QMutex>
#include <QTime>
template <class T, const unsigned short S>
class ConcurrentQueue
......@@ -14,6 +15,7 @@ public:
bool isEmpty() const;
private:
T m_buffer[S];
QTime m_time[S];
unsigned short m_next;
unsigned short m_first;
QSemaphore m_free;
......
uniform sampler2D texture;
varying mediump vec2 vartexcoord;
varying highp vec2 vartarget;
void main(void)
{
gl_FragColor = texture2D(texture, vartexcoord);
gl_FragColor = texture2D(texture, vartarget);
}
uniform mediump mat4 ortho;
uniform mediump mat4 texortho;
attribute mediump vec2 position;
attribute mediump vec2 texcoord;
varying mediump vec2 vartexcoord;
uniform highp mat4 srcortho;
uniform highp mat4 trgortho;
attribute highp vec2 source;
attribute highp vec2 target;
varying highp vec2 vartarget;
void main(void)
{
gl_Position = vec4((position.x - 1024.0) / 1024.0, (position.y - 1024.0) / 1024.0, 0.0, 1.0);
vartexcoord = vec2(texcoord.x / 1920.0, texcoord.y / 1080.0);
gl_Position = trgortho * vec4(target, 0.0, 1.0);
vartarget = vec4(srcortho * vec4(source, 0.0, 1.0)).xy;
}
#include "dispatcher.h"
#include "uploader.h"
Dispatcher::Dispatcher(Uploader *uploader, QObject *parent) :
QObject(parent), m_client(NULL), m_uploader(uploader)
QObject(parent)
{
m_client = rfbGetClient(8, 3, 4);
m_client->canHandleNewFBSize = FALSE;
m_client->MallocFrameBuffer = Dispatcher::MallocFrameBuffer;
m_client->GotCopyRect = Dispatcher::GotCopyRect;
m_client->GotFillRect = Dispatcher::GotFillRect;
m_client->GotBitmap = Dispatcher::GotBitmap;
m_client->GotJpeg = Dispatcher::GotJpeg;
m_client->FinishedFrameBufferUpdate = Dispatcher::FinishedFrameBufferUpdate;
m_client->GetPassword = Dispatcher::GetPassword;
rfbClientSetClientData(m_client, const_cast<char*>("dispatcher"), this);
// the pointer to the Uploader is stored in the rfbClient instead of a member of the class to avoid duplication
rfbClientSetClientData(m_client, const_cast<char*>("uploader"), uploader);
}
Dispatcher::~Dispatcher()
{
rfbClientCleanup(m_client);
}
rfbBool Dispatcher::MallocFrameBuffer(rfbClient *client)
{
Dispatcher *dispatcher = (Dispatcher *) rfbClientGetClientData(client, 0);
auto dispatcher = static_cast<Dispatcher *>(rfbClientGetClientData(client, const_cast<char*>("dispatcher")));
emit dispatcher->resizeFramebuffer(QSize(client->width, client->height));
return TRUE;
}
void Dispatcher::GotCopyRect(rfbClient *client, const int src_x, const int src_y, const int w, const int h, const int dest_x, const int dest_y)
{
Dispatcher *dispatcher = (Dispatcher *) rfbClientGetClientData(client, 0);
dispatcher->uploader()->gotCopy(src_x, src_y, w, h, dest_x, dest_y);
auto uploader = static_cast<Uploader *>(rfbClientGetClientData(client, const_cast<char*>("uploader")));
uploader->gotCopy(src_x, src_y, w, h, dest_x, dest_y);
}
void Dispatcher::GotFillRect(rfbClient *client, int x, int y, int w, int h, uint32_t colour)
{
Dispatcher *dispatcher = (Dispatcher *) rfbClientGetClientData(client, 0);
auto uploader = static_cast<Uploader *>(rfbClientGetClientData(client, const_cast<char*>("uploader")));
int red = (colour >> client->format.redShift) & client->format.redMax;
int green = (colour >> client->format.greenShift) & client->format.greenMax;
int blue = (colour >> client->format.blueShift) & client->format.blueMax;
dispatcher->uploader()->gotFill(x, y, w, h, red, green, blue);
uploader->gotFill(x, y, w, h, red, green, blue);
}
void Dispatcher::GotBitmap(rfbClient *client, const uint8_t *buffer, int x, int y, int w, int h)
{
Dispatcher *dispatcher = (Dispatcher *) rfbClientGetClientData(client, 0);
// We have to make a deep copy because libvnc might free the buffer as soon as this function returns
dispatcher->uploader()->gotBitmap(buffer, x, y, w, h);
auto uploader = static_cast<Uploader *>(rfbClientGetClientData(client, const_cast<char*>("uploader")));
uploader->gotBitmap(buffer, x, y, w, h);
}
rfbBool Dispatcher::GotJpeg(rfbClient *client, const uint8_t *buffer, int length, int x, int y, int w, int h)
{
Dispatcher *dispatcher = static_cast<Dispatcher *>(rfbClientGetClientData(client, 0));
dispatcher->uploader()->gotJpeg();
Jpeg *jpeg = new Jpeg({(unsigned char*)buffer, length, x, y, w, h});
dispatcher->queue()->enqueue(jpeg);
auto uploader = static_cast<Uploader *>(rfbClientGetClientData(client, const_cast<char*>("uploader")));
uploader->gotJpeg(buffer, length, x, y, w, h);
return TRUE;
}
void Dispatcher::FinishedFrameBufferUpdate(rfbClient *client)
{
Dispatcher *dispatcher = (Dispatcher *) rfbClientGetClientData(client, 0);
dispatcher->uploader()->finishedUpdate();
auto uploader = static_cast<Uploader *>(rfbClientGetClientData(client, const_cast<char*>("uploader")));
uploader->finishedUpdate();
}
char *Dispatcher::GetPassword(rfbClient *client)
{
char* password = new char[9];
char* password = (char*) std::malloc(sizeof(char) * 9);
strcpy(password, "asdfasdf");
return password;
}
......@@ -78,39 +96,18 @@ int Dispatcher::bitsPerPixel()
return m_client->format.bitsPerPixel;
}
ConcurrentQueue<Jpeg *, 256u> *Dispatcher::queue()
{
return &m_queue;
}
Uploader *Dispatcher::uploader() const
{
return m_uploader;
}
void Dispatcher::open(const QString host, const int port, const int width, const int height)
{
QByteArray tmp = host.toLocal8Bit();
m_client = rfbGetClient(8, 3, 4);
m_client->canHandleNewFBSize = FALSE;
m_client->serverHost = new char[tmp.size() + 1];
strcpy(m_client->serverHost, tmp.constData());
m_client->serverPort = port;
m_client->MallocFrameBuffer = Dispatcher::MallocFrameBuffer;
m_client->GotCopyRect = Dispatcher::GotCopyRect;
m_client->GotFillRect = Dispatcher::GotFillRect;
m_client->GotBitmap = Dispatcher::GotBitmap;
m_client->GotJpeg = Dispatcher::GotJpeg;
m_client->FinishedFrameBufferUpdate = Dispatcher::FinishedFrameBufferUpdate;
m_client->GetPassword = Dispatcher::GetPassword;
// Set the format requested by the user
m_client->appData.useRemoteCursor = TRUE;
//m_client->appData.useRemoteCursor = TRUE;
m_client->width = width;
m_client->height = height;
m_client->appData.compressLevel = 9;
m_client->appData.qualityLevel = 0;
//m_client->appData.compressLevel = 0;
//m_client->appData.qualityLevel = 9;
#ifdef __BIG_ENDIAN__
m_client->format.bigEndian = TRUE;
#else
......@@ -120,30 +117,11 @@ void Dispatcher::open(const QString host, const int port, const int width, const
m_client->format.trueColour = TRUE;
m_client->format.bitsPerPixel = 32;
rfbClientSetClientData(m_client, 0, this);
if(!rfbInitClient(m_client, NULL, NULL)) {
emit error();
return;
}
// Stick to the format sent by the server if we can render it easily, set a new one otherwise
//
// Formats OpenGL ES 2.0 can (should be able to) handle:
// Internal Format External Format Type Bytes per Pixel
// --------------- --------------- ---- ---------------
// RGBA RGBA UNSIGNED_BYTE 4
// RGB RGB UNSIGNED_BYTE 3
// RGBA RGBA UNSIGNED_SHORT_4_4_4_4 2
// RGBA RGBA UNSIGNED_SHORT_5_5_5_1 2
// RGB RGB UNSIGNED_SHORT_5_6_5 2
// The Raspberry Pi supports BGRA too but only through extensions
//
// JPEG is RGB UNSIGNED_BYTE
// Supported raw formats:
// 32 bit: RGB888
// 16 bit: RGB444, RGB555, RGB565
refresh();
decode();
}
......@@ -166,10 +144,9 @@ void Dispatcher::refresh()
}
void Dispatcher::decode()
{
if(m_client == NULL)
return;
m_uploader->startDecoding();
auto uploader = static_cast<Uploader *>(rfbClientGetClientData(m_client, const_cast<char*>("uploader")));
uploader->startDecoding();
if(!HandleRFBServerMessage(m_client)) {
terminate();
......
#ifndef RFBDISPATCH_H
#define RFBDISPATCH_H
#include <QOpenGLContext>
#include <QCoreApplication>
#include <QImage>
#include <QThreadPool>
#include <QObject>
#include <QPoint>
#include <QSize>
#include <QColor>
#include <QMouseEvent>
#include <QVector3D>
#include <QTime>
#include <rfb/rfbclient.h>
#include "uploader.h"
#include "jpegdecoder.h"
extern QTime elapsed;
class Uploader;
class Dispatcher : public QObject
{
Q_OBJECT
public:
Dispatcher(Uploader *uploader, QObject *parent = Q_NULLPTR);
explicit Dispatcher(Uploader *uploader, QObject *parent = Q_NULLPTR);
~Dispatcher();
// libvnc hooks
static rfbBool MallocFrameBuffer(rfbClient* client);
......@@ -40,14 +28,8 @@ public:
// parameters of the VNC connection
int bitsPerPixel();
// getters
ConcurrentQueue<Jpeg *, 256u>* queue();
Uploader *uploader() const;
private:
rfbClient *m_client;
Uploader *m_uploader;
ConcurrentQueue<Jpeg*, 256u> m_queue;
signals:
// libvnc events
void gotCopy(const int src_x, const int src_y, const int width, const int height, const int dest_x, const int dest_y);
......
uniform sampler2D texture;
varying mediump vec2 vartexcoord;
varying lowp vec4 varcolor;
void main(void)
{
gl_FragColor = vec4(texture2D(texture, vartexcoord).rgb + varcolor.rgb, 1.0);
}
uniform mediump mat4 ortho;
attribute mediump vec2 position;
attribute lowp vec4 color;
attribute mediump vec2 texcoord;
varying lowp vec4 varcolor;
varying mediump vec2 vartexcoord;
void main(void)
{
gl_Position = ortho * vec4(position, 0.0, 1.0);
varcolor = color;
vartexcoord = vec2(texcoord.x / 2048.0, texcoord.y / 2048.0);
}
......@@ -2,5 +2,5 @@ varying highp vec4 varcolor;
void main(void)
{
gl_FragColor = varcolor;
gl_FragColor = vec4(varcolor.rgb, 1.0);
}
#include "jpegdecoder.h"
int Jpeg::y_width = 0;
int Jpeg::cb_width = 0;
int Jpeg::cr_width = 0;
unsigned char *Jpeg::y_pointer = 0;
unsigned char *Jpeg::cb_pointer = 0;
unsigned char *Jpeg::cr_pointer = 0;
struct error_mgr
{
struct jpeg_error_mgr mgr;
jmp_buf setjmp_buffer;
};
typedef struct error_mgr* error_ptr;
METHODDEF(void)
error_exit (j_common_ptr cinfo)
{
error_ptr myerr = (error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
JpegDecoder::JpegDecoder(ConcurrentQueue<Jpeg *, 256u> *queue, QObject *parent) :
JpegDecoder::JpegDecoder(ConcurrentQueue<Jpeg *, 1024u> *queue, QObject *parent) :
QObject(parent), m_queue(queue)
{
}
void JpegDecoder::operate()
{
//QTime time;
//time.start();
struct jpeg_decompress_struct cinfo;
struct error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr.mgr);
jerr.mgr.error_exit = error_exit;
jerr.mgr.error_exit = &error_exit;
if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo);
}
jpeg_create_decompress(&cinfo);
Jpeg *jpeg;
JSAMPLE *samples;
JSAMPROW *rows;
//int dequeue, decompress;
forever {
//time.restart();
jpeg = m_queue->dequeue();
//dequeue = time.elapsed();
jpeg_mem_src(&cinfo, jpeg->data, jpeg->length);
(void) jpeg_read_header(&cinfo, TRUE);
cinfo.dct_method = JDCT_FASTEST;
cinfo.do_fancy_upsampling = FALSE;
cinfo.two_pass_quantize = FALSE;
cinfo.dither_mode = JDITHER_ORDERED;
//cinfo.scale_num = 1;
//cinfo.scale_denom = 8;
cinfo.out_color_space = JCS_EXT_RGBX;
(void) jpeg_start_decompress(&cinfo);
samples = new JSAMPLE[cinfo.output_width * cinfo.output_height * cinfo.output_components];
rows = new JSAMPROW[cinfo.output_height * cinfo.output_components];
for(size_t i = 0; i < cinfo.output_height; i++) {
rows[i] = samples + (i * cinfo.output_width * cinfo.output_components);
auto jpeg = m_queue->dequeue();
jpeg->output = new JSAMPARRAY[3];
int paddingHeight = (16 - (jpeg->height % 16));
int paddingWidth = jpeg->width + (16 - jpeg->width % 16);
jpeg->padding = new JSAMPLE[3 * paddingHeight * paddingWidth];
jpeg->rows = new JSAMPROW[3 * (jpeg->height + paddingHeight)];
for (unsigned char component = 0; component < 3; ++component)
jpeg->output[component] = &jpeg->rows[component * (jpeg->height + paddingHeight)];
for (int row = 0; row < jpeg->height; ++row)
{
jpeg->output[0][row] = &Jpeg::y_pointer[jpeg->x + (jpeg->y + row) * Jpeg::y_width];
jpeg->output[1][row] = &Jpeg::cb_pointer[jpeg->x + (jpeg->y + row) * Jpeg::cb_width];
jpeg->output[2][row] = &Jpeg::cr_pointer[jpeg->x + (jpeg->y + row) * Jpeg::cr_width];
}
for (int row = 0; row < paddingHeight; ++row)
{
jpeg->output[0][jpeg->height + row] = &jpeg->padding[row * paddingWidth];
jpeg->output[1][jpeg->height + row] = &jpeg->padding[(paddingHeight + row) * paddingWidth];
jpeg->output[2][jpeg->height + row] = &jpeg->padding[(2 * paddingHeight + row) * paddingWidth];
}
jpeg_mem_src(&cinfo, jpeg->input, jpeg->length);
jpeg_read_header(&cinfo, TRUE);
cinfo.dct_method = JDCT_FASTEST;
cinfo.raw_data_out = TRUE;
jpeg_start_decompress(&cinfo);
JSAMPARRAY rowzero[3];
for(int component = 0; component < cinfo.output_components; component++)
rowzero[component] = &jpeg->output[component][0];
while (cinfo.output_scanline < cinfo.output_height) {
jpeg_read_scanlines(&cinfo, rows + cinfo.output_scanline, 1);
for(int component = 0; component < cinfo.output_components; component++)
jpeg->output[component] = &rowzero[component][cinfo.output_scanline / (cinfo.max_v_samp_factor * cinfo.comp_info[component].v_samp_factor)];
jpeg_read_raw_data(&cinfo, jpeg->output, qMax(static_cast<int>(cinfo.output_height - cinfo.output_scanline), cinfo.max_v_samp_factor * DCTSIZE));
}
(void) jpeg_finish_decompress(&cinfo);
//decompress = time.elapsed();
//qDebug() << "dequeue" << dequeue << "decompress" << decompress;
emit finished(samples, jpeg->x, jpeg->y, jpeg->width, jpeg->height);
delete[] jpeg->data;
jpeg_finish_decompress(&cinfo);
emit finishedJpeg();
delete jpeg;
delete[] rows;
}
jpeg_destroy_decompress(&cinfo);
}
......@@ -15,12 +15,39 @@
struct Jpeg
{
uint8_t *data;
int length;
int x;
int y;
uint8_t *input;
std::size_t length;
JSAMPIMAGE output;
JSAMPROW *rows;
JSAMPLE *padding;
int width;
int height;
int x;
int y;
static int y_width;
static int cb_width;
static int cr_width;
static unsigned char *y_pointer;
static unsigned char *cb_pointer;
static unsigned char *cr_pointer;
Jpeg(uint8_t *input,
const std::size_t length,
const int width,
const int height,
const int x,
const int y) : input(input), length(length), width(width), height(height), x(x), y(y)
{
}
~Jpeg()
{
std::free(input);
delete[] output;
delete[] rows;
delete[] padding;
}
};
/*!
......@@ -32,28 +59,12 @@ class JpegDecoder : public QObject
{
Q_OBJECT
public:
JpegDecoder(ConcurrentQueue<Jpeg*, 256u> *queue, QObject *parent = 0);
ConcurrentQueue<Jpeg*, 256u> *m_queue;
JpegDecoder(ConcurrentQueue<Jpeg*, 1024u> *queue, QObject *parent = 0);
ConcurrentQueue<Jpeg*, 1024u> *m_queue;
signals:
void finished(const unsigned char *image, const quint32 x, const quint32 y, const quint32 width, const quint32 height);
void finishedJpeg();
public slots:
void operate();
};
struct error_mgr
{
struct jpeg_error_mgr mgr;
jmp_buf setjmp_buffer;
};
typedef struct error_mgr* error_ptr;
METHODDEF(void)
error_exit (j_common_ptr cinfo)
{
error_ptr myerr = (error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
#endif // JPEGDECODER_H
......@@ -2,8 +2,6 @@
#include <QtQml>
#include <QQmlApplicationEngine>
QTime elapsed;
#include "qvnc.h"
int main(int argc, char *argv[])
......@@ -18,8 +16,8 @@ int main(int argc, char *argv[])
format.setMajorVersion(2);
format.setMinorVersion(0);
format.setRenderableType(QSurfaceFormat::OpenGLES);
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
format.setSwapInterval(1);
format.setSwapBehavior(QSurfaceFormat::SingleBuffer);
format.setSwapInterval(0);
#ifdef OGL_DEBUG
format.setOption(QSurfaceFormat::DebugContext, true);
#endif
......@@ -27,7 +25,7 @@ int main(int argc, char *argv[])
QGuiApplication::setAttribute(Qt::AA_UseOpenGLES, true);
QGuiApplication app(argc, argv);
qmlRegisterType<QVnc>("thinclient", 1, 3, "QVnc");
qmlRegisterType<QVnc>("thinclient", 1, 4, "QVnc");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
......
import QtQuick 2.6
import QtQuick.Controls 2.0
import thinclient 1.3
import QtQuick 2.5
import QtQuick.Controls 2.1
import thinclient 1.4
ApplicationWindow {
visible: true
......@@ -27,9 +27,9 @@ ApplicationWindow {
QVnc {
id: vnc
host: "vm.ik.bme.hu"
port: 10495
port: 13070
width: 1920
height: 1080
transform: Scale {xScale: window.width / vnc.width; yScale: (window.height - tab.height) / vnc.height}
//transform: Scale {xScale: window.width / vnc.width; yScale: (window.height - tab.height) / vnc.height}
}
}
#include "qvnc.h"
QVnc::QVnc(QQuickItem *parent) :
QQuickFramebufferObject(parent), m_decoders(DECODER_COUNT), m_threads(DECODER_COUNT + 1)
QQuickFramebufferObject(parent), m_host(""), m_port(0)
{
setAcceptedMouseButtons(Qt::AllButtons);
setAcceptHoverEvents(true);
// this makes sure the rendering is called only when we explicitly ask for it
setTextureFollowsItemSize(false);
for(int i = 0; i < m_threads.length(); i++)
m_threads[i] = new QThread(this);
connect(this, &QVnc::windowChanged, this, &QVnc::winChanged);
m_uploader = new Uploader;
m_uploader->moveToThread(m_threads[0]);
connect(m_threads[0], &QThread::finished, m_uploader, &QObject::deleteLater);
connect(m_uploader, &Uploader::uploadFinished, this, &QVnc::update);
connect(&m_worker, &QThread::finished, m_uploader, &QObject::deleteLater);
m_dispatcher = new Dispatcher(m_uploader);
m_dispatcher->moveToThread(m_threads[0]);
connect(m_threads[0], &QThread::finished, m_dispatcher, &QObject::deleteLater);
connect(this, &QVnc::open_connection, m_dispatcher, &Dispatcher::open);
connect(this, &QVnc::terminate_connection, m_dispatcher, &Dispatcher::terminate);
connect(m_dispatcher, &Dispatcher::error, this, &QVnc::dispatch_error);
connect(&m_worker, &QThread::finished, m_dispatcher, &QObject::deleteLater);
connect(m_uploader, &Uploader::uploadFinished, m_dispatcher, &Dispatcher::refresh);
// connect(m_uploader, &Uploader::uploadFinished, this, &QVnc::update);
connect(m_uploader, &Uploader::buffersSwapped, m_dispatcher, &Dispatcher::decode);
int i = 1;
foreach(JpegDecoder* d, m_decoders) {
d = new JpegDecoder(m_dispatcher->queue());
d->moveToThread(m_threads[i]);
connect(m_threads[i], &QThread::finished, d, &QObject::deleteLater);
connect(d, &JpegDecoder::finished, m_uploader, &Uploader::finishedJpeg);
connect(this, &QVnc::operate, d, &JpegDecoder::operate);
i++;
}
m_uploader->moveToThread(&m_worker);
m_dispatcher->moveToThread(&m_worker);
for(int i = 0; i < m_threads.length(); i++)
m_threads.at(i)->start();
m_worker.start();
emit operate();
connect(this, &QVnc::open_connection, m_dispatcher, &Dispatcher::open);
connect(this, &QVnc::terminate_connection, m_dispatcher, &Dispatcher::terminate);
connect(m_dispatcher, &Dispatcher::error, this, &QVnc::dispatch_error);
}
QVnc::~QVnc()
{
foreach(QThread* t, m_threads) {
t->quit();
t->wait();
}
}
void QVnc::open()
......@@ -88,25 +67,19 @@ void QVnc::dispatch_error()
void QVnc::winChanged(QQuickWindow *window)
{
if(window != NULL) {
connect(window, &QQuickWindow::openglContextCreated, m_uploader, &Uploader::createContext, Qt::BlockingQueuedConnection);
connect(window, &QQuickWindow::frameSwapped, m_uploader, &Uploader::swapBuffers, Qt::BlockingQueuedConnection);
}
if(window != NULL)
connect(window, &QQuickWindow::openglContextCreated, m_uploader, &Uploader::createContext);
}
QQuickFramebufferObject::Renderer *QVnc::createRenderer() const
{
return new VncRenderer(window(),
m_uploader->fillBuffer(),
m_uploader->copyBuffer(),
m_uploader->bitmapBuffer(),
m_uploader->texture(),
NULL,
NULL,
NULL,
m_uploader->fillCount(),
m_uploader->copyCount(),
m_uploader->bitmapCount());
auto renderer = new VncRenderer(window(),
m_uploader->fillBuffer(),
m_uploader->copyBuffer(),
m_uploader->bitmapBuffer(),
m_uploader->ycbcrBuffer());
connect(renderer, &VncRenderer::rendered, m_dispatcher, &Dispatcher::decode);
return renderer;
}
QString QVnc::host() const
......@@ -170,9 +143,9 @@ void QVnc::wheelEvent(QWheelEvent *event)
void QVnc::mouseMoveEvent(QMouseEvent *event)
{
// Filter mouse move events, we don't need 100+ of them in a second
if((event->timestamp() - m_lastMouseEvent) > 33) {
//if((event->timestamp() - m_lastMouseEvent) > 33) {
Qt::MouseButtons buttons = event->buttons();
m_lastMouseEvent = event->timestamp();
// m_lastMouseEvent = event->timestamp();
if(m_dispatcher->mouseEvent(event->x(),
event->y(),
buttons & Qt::LeftButton,
......@@ -183,13 +156,13 @@ void QVnc::mouseMoveEvent(QMouseEvent *event)
event->accept();
else
event->ignore();
}
//}
}
void QVnc::hoverMoveEvent(QHoverEvent *event)
{
// Qt bug: timestamp is zero for QHoverEvent
// if((event->timestamp() - m_lastMouseEvent) > 33) {
//if((event->timestamp() - m_lastMouseEvent) > 33) {
// m_lastMouseEvent = event->timestamp();
QPoint position = event->pos();
if(m_dispatcher->mouseEvent(position.x(),
......@@ -202,7 +175,7 @@ void QVnc::hoverMoveEvent(QHoverEvent *event)
event->accept();
else
event->ignore();
// }
//}
}
Uploader *QVnc::uploader() const
......
#ifndef VNC_H
#define VNC_H
#define DECODER_COUNT 5
// #define OGL_DEBUG
#define TEXTURE_WIDTH 1920
#define TEXTURE_HEIGHT 1080
#define VERTEX_BUFFER 16384
//#define OGL_DEBUG
#include <QQuickWindow>
#include <QObject>
......@@ -32,7 +33,7 @@ public:
explicit QVnc(QQuickItem *parent = Q_NULLPTR);
~QVnc();
Renderer *createRenderer() const;
Renderer *createRenderer() const override;
QString host() const;
int port() const;
Uploader *uploader() const;
......@@ -47,9 +48,7 @@ protected:
private:
Dispatcher *m_dispatcher;
Uploader *m_uploader;
QVector<JpegDecoder*> m_decoders;
VncRenderer *m_renderer;
QVector<QThread*> m_threads;
QThread m_worker;
QString m_host;
int m_port;
......@@ -64,10 +63,6 @@ signals:
void hostChanged(QString host);
void portChanged(int port);
void operate();
void refresh();
public slots:
void open();
void terminate();
......
......@@ -16,12 +16,13 @@
#ifndef UPLOADER_H
#define UPLOADER_H
#define TEXTURE_WIDTH 4096
#define TEXTURE_HEIGHT 4096
#define VERTEX_BUFFER 1024
// #define OGL_DEBUG
#define TEXTURE_WIDTH 1920
#define TEXTURE_HEIGHT 1080
#define VERTEX_BUFFER 16384
//#define OGL_DEBUG
#include <utility>
#include <memory>
#include <QObject>
#include <QDebug>
......@@ -33,15 +34,26 @@
#include <QtMath>
#include <QTime>
#include <QOpenGLDebugLogger>
#include <QThread>
extern QTime elapsed;
#include <texture.h>
#include "concurrentqueue.h"
#define DECODER_COUNT 5
struct Bitmap
{
GLushort x;
GLushort y;
GLushort texX;
GLushort texY;
};
struct Copy
{
GLushort src_x;
GLushort src_y;
GLushort dest_x;
GLushort dest_y;
};
struct Fill
......@@ -54,6 +66,9 @@ struct Fill
GLubyte alpha;
};
class JpegDecoder;
class Jpeg;
/*!
* \brief The Uploader class
*
......@@ -63,17 +78,22 @@ class Uploader : public QObject
{
Q_OBJECT
public:
Uploader(QObject *parent = Q_NULLPTR);
explicit Uploader(QObject *parent = Q_NULLPTR);
~Uploader();
GLuint **texture() const;
QOpenGLBuffer **fillBuffer() const;
QOpenGLBuffer **copyBuffer() const;
QOpenGLBuffer **bitmapBuffer() const;
unsigned int *fillCount() const;
unsigned int *copyCount() const;
unsigned int *bitmapCount() const;
void swapBuffers();
// might as well make these public, they are shared with the renderer
std::shared_ptr<Texture> rgbTexture() const {return m_rgbTexture;}
std::shared_ptr<Texture> yTexture() const {return m_yTexture;}
std::shared_ptr<Texture> cbTexture() const {return m_cbTexture;}
std::shared_ptr<Texture> crTexture() const {return m_crTexture;}
QOpenGLBuffer fillBuffer() const {return m_fillBuffer;}
QOpenGLBuffer copyBuffer() const {return m_copyBuffer;}
QOpenGLBuffer bitmapBuffer() const {return m_bitmapBuffer;}
QOpenGLBuffer ycbcrBuffer() const {return m_ycbcrBuffer;}
std::size_t fillCount() const {return m_fillIndex;}
std::size_t copyCount() const {return m_copyIndex;}
std::size_t bitmapCount() const {return m_bitmapIndex;}
std::size_t ycbcrCount() const {return m_ycbcrIndex;}
// VNC events
void gotFill(const GLushort x, const GLushort y,
......@@ -85,56 +105,55 @@ public:
void gotBitmap(const unsigned char *image,
const GLushort x, const GLushort y,
const GLushort width, const GLushort height);
void gotJpeg();
void gotJpeg(const uint8_t *buffer, int length, int x, int y, int width, int height);
void finishedUpdate();
void startDecoding();
private:
QOffscreenSurface m_surface;
QOpenGLContext m_context;
QOpenGLFunctions *m_gl;
QOpenGLBuffer **m_uploadFill;
QOpenGLBuffer **m_renderFill;
QOpenGLBuffer m_fillBuffer;
Fill *m_fillPointer;
size_t m_fillIndex;
unsigned int *m_fillCount;
std::size_t m_fillIndex;
QOpenGLBuffer **m_uploadCopy;
QOpenGLBuffer **m_renderCopy;
Bitmap *m_copyPointer;
size_t m_copyIndex;
unsigned int *m_copyCount;
QOpenGLBuffer m_copyBuffer;
Copy *m_copyPointer;
std::size_t m_copyIndex;
QOpenGLBuffer **m_uploadBitmap;
QOpenGLBuffer **m_renderBitmap;
QOpenGLBuffer m_bitmapBuffer;
Bitmap *m_bitmapPointer;
size_t m_bitmapIndex;
unsigned int *m_bitmapCount;
std::size_t m_bitmapIndex;
std::shared_ptr<Texture> m_rgbTexture;
unsigned char *m_rgbPointer;
QOpenGLBuffer m_ycbcrBuffer;
Bitmap *m_ycbcrPointer;
std::size_t m_ycbcrIndex;
GLuint m_texId[2];
GLuint **m_uploadTexture;
GLuint **m_renderTexture;
std::shared_ptr<Texture> m_yTexture;
std::shared_ptr<Texture> m_cbTexture;
std::shared_ptr<Texture> m_crTexture;
std::array<QThread, DECODER_COUNT> m_workers;
std::array<JpegDecoder*, DECODER_COUNT> m_decoders;
ConcurrentQueue<Jpeg*, 1024u> m_queue;
int m_jpegs;
bool m_finished;
bool m_swap;
unsigned short m_atlasX;
unsigned short m_atlasY;
unsigned short m_atlasRowHeight;
unsigned short m_atlasLastWidth;
bool refreshAtlas(const int width, const int height);
bool m_first;
void startRendering();
signals:
void uploadFinished();
void buffersSwapped();
void operate();
public slots:
void createContext(QOpenGLContext *context);
void finishedJpeg(const unsigned char *image,
const GLushort x, const GLushort y,
const GLushort width, const GLushort height);
void finishedJpeg();
private slots:
void handleDebugMessage(const QOpenGLDebugMessage &debugMessage);
};
......
#ifndef VNCRENDERER_H
#define VNCRENDERER_H
#include <memory>
#include <QQuickWindow>
#include <QQuickFramebufferObject>
#include <QOpenGLFramebufferObject>
......@@ -11,6 +12,8 @@
#include <QOpenGLBuffer>
#include <QTime>
#include <texture.h>
#include "qvnc.h"
struct Indices
......@@ -35,54 +38,56 @@ class VncRenderer : public QObject, public QQuickFramebufferObject::Renderer
Q_OBJECT
public:
VncRenderer(QQuickWindow *window,
QOpenGLBuffer **fillBuffer,
QOpenGLBuffer **copyBuffer,
QOpenGLBuffer **bitmapBuffer,
GLuint **rgbTexture,
GLuint **yTexture,
GLuint **cbTexture,
GLuint **crTexture,
unsigned int *fillCount,
unsigned int *copyCount,
unsigned int *bitmapCount,
QOpenGLBuffer fillBuffer,
QOpenGLBuffer copyBuffer,
QOpenGLBuffer bitmapBuffer,
QOpenGLBuffer ycbcrBuffer,
QObject *parent = nullptr);
protected:
void render() override;
void synchronize(QQuickFramebufferObject *fbo) override;
QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) override;
private:
QQuickWindow *m_window;
QMatrix4x4 m_copyOrtho;
QMatrix4x4 m_copyTexortho;
QMatrix4x4 m_bitmapOrtho;
QMatrix4x4 m_bitmapTexortho;
QMatrix4x4 m_fromTexture;
QMatrix4x4 m_toFramebuffer;
QMatrix4x4 m_fromFramebuffer;
QMatrix4x4 m_toTexture;
QOpenGLBuffer **m_fillBuffer;
QOpenGLBuffer **m_copyBuffer;
QOpenGLBuffer **m_bitmapBuffer;
QOpenGLBuffer m_fillBuffer;
QOpenGLBuffer m_copyBuffer;
QOpenGLBuffer m_bitmapBuffer;
QOpenGLBuffer m_ycbcrBuffer;
GLuint **m_rgbTexture;
GLuint **m_yTexture;
GLuint **m_cbTexture;
GLuint **m_crTexture;
std::shared_ptr<Texture> m_rgbTexture;
std::shared_ptr<Texture> m_yTexture;
std::shared_ptr<Texture> m_cbTexture;
std::shared_ptr<Texture> m_crTexture;
unsigned int *m_fillCount;
unsigned int *m_copyCount;
unsigned int *m_bitmapCount;
unsigned int m_fillCount;
unsigned int m_copyCount;
unsigned int m_bitmapCount;
unsigned int m_ycbcrCount;
QOpenGLBuffer m_indices;
QOpenGLShaderProgram m_fillProgram;
QOpenGLShaderProgram m_bitmapProgram;
QOpenGLShaderProgram m_ycbcrProgram;
QOpenGLShaderProgram m_copyProgram;
GLuint m_textureTarget;
QOpenGLFunctions *m_gl;
QOpenGLFramebufferObject *m_framebufferObject;
bool m_clear;
int frameCount;
QTime frameTime;
private slots:
void handleDebugMessage(const QOpenGLDebugMessage &debugMessage);
signals:
void rendered();
};
#endif // VNCRENDERER_H
uniform sampler2D y_texture;
uniform sampler2D cb_texture;
uniform sampler2D cr_texture;
varying highp vec2 vartexcoord;
varying highp float vartexindex;
void main(void)
{
highp float Y;
highp float Cb;
highp float Cr;
highp float pos = mod((gl_FragCoord.x - 0.5), 4.0);
if(pos < 1.0) {
Y = texture2D(y_texture, vartexcoord).r;
Cb = texture2D(cb_texture, vartexcoord).r;
Cr = texture2D(cr_texture, vartexcoord).r;
} else if(pos < 2.0) {
Y = texture2D(y_texture, vartexcoord).g;
Cb = texture2D(cb_texture, vartexcoord).g;
Cr = texture2D(cr_texture, vartexcoord).g;
} else if(pos < 3.0) {
Y = texture2D(y_texture, vartexcoord).b;
Cb = texture2D(cb_texture, vartexcoord).b;
Cr = texture2D(cr_texture, vartexcoord).b;
} else {
Y = texture2D(y_texture, vartexcoord).a;
Cb = texture2D(cb_texture, vartexcoord).a;
Cr = texture2D(cr_texture, vartexcoord).a;
}
highp float R = Y + 1.402 * (Cr - 0.5);
highp float G = Y - 0.34414 * (Cb - 0.5) - 0.71414 * (Cr - 0.5);
highp float B = Y + 1.772 * (Cb - 0.5);
gl_FragColor = vec4(B, G, R, 1.0);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment