From 5cd3c5b5068328089252e897328d18d78938f2ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Sun, 29 Apr 2012 15:14:37 +0000 Subject: [PATCH] paysages: Threaded previews (WIP). git-svn-id: https://subversion.assembla.com/svn/thunderk/paysages@310 b1fd45b6-86a6-48da-8261-f70d1f35bdcc --- TODO | 2 +- gui_qt/baseform.cpp | 1 - gui_qt/basepreview.cpp | 380 +++++++++++++++++++++++++++-------------- gui_qt/basepreview.h | 67 ++++++-- gui_qt/dialognoise.cpp | 2 - gui_qt/mainwindow.cpp | 3 + i18n/paysages_fr.ts | 96 +++++------ lib_paysages/array.c | 8 +- 8 files changed, 363 insertions(+), 196 deletions(-) diff --git a/TODO b/TODO index a588799..567232b 100644 --- a/TODO +++ b/TODO @@ -10,7 +10,7 @@ Technology Preview 2 : - Add a noise filler (and maybe noise intervals ?). - Improve curve editor. - Water and terrain LOD moves with the camera, fix it like in the wanderer. -- More threading in previews and layered previews (with a less detailed layer in the background). +- There should not have to be more preview threads than core. Technology Preview 3 : - Restore render progress. diff --git a/gui_qt/baseform.cpp b/gui_qt/baseform.cpp index 030f12e..1dfa18f 100644 --- a/gui_qt/baseform.cpp +++ b/gui_qt/baseform.cpp @@ -170,7 +170,6 @@ void BaseForm::addPreview(BasePreview* preview, QString label) previews->layout()->addWidget(label_widget); previews->layout()->addWidget(preview); - preview->start(); preview->setObjectName("_form_preview_"); } diff --git a/gui_qt/basepreview.cpp b/gui_qt/basepreview.cpp index c25ee8c..a122621 100644 --- a/gui_qt/basepreview.cpp +++ b/gui_qt/basepreview.cpp @@ -6,40 +6,208 @@ #include #include -class PreviewDrawer:public QThread +class PreviewChunk { public: - PreviewDrawer(BasePreview* preview): - QThread(), - _preview(preview) + + PreviewChunk(BasePreview* preview, QImage* pixbuf, QMutex* lock, int xstart, int ystart, int xsize, int ysize) { - _running = false; + _preview = preview; + _pixbuf = pixbuf; + _lock = lock; + _xstart = xstart; + _ystart = ystart; + _xsize = xsize; + _ysize = ysize; + + _need_render = true; } - void askStop() + + bool isFrom(BasePreview* preview) { - _running = false; + return _preview == preview; } - static inline void usleep(int us) + + void update() { - QThread::usleep(us); + _need_render = true; } -protected: - void run() + + bool render() { - _running = true; - while (_running) + bool changed = false; + + if (_need_render) { - _preview->doRender(); - QThread::usleep(50000); + _need_render = false; + + _lock->lock(); + QImage tempbuffer = _pixbuf->copy(_xstart, _ystart, _xsize, _ysize); + _lock->unlock(); + + for (int x = 0; x < _xsize; x++) + { + for (int y = 0; y < _ysize; y++) + { + QRgb col = tempbuffer.pixel(x, y); + if (qAlpha(col) < 255) + { + QColor newcol = _preview->getPixelColor(_xstart + x, _ystart + y); + newcol.setAlpha(255); + + tempbuffer.setPixel(x, y, newcol.rgb()); + + changed = true; + } + } + } + + if (changed) + { + _lock->lock(); + for (int x = 0; x < _xsize; x++) + { + for (int y = 0; y < _ysize; y++) + { + _pixbuf->setPixel(_xstart + x, _ystart + y, tempbuffer.pixel(x, y)); + } + } + _lock->unlock(); + _preview->tellContentChange(); + } } + + return changed; } private: BasePreview* _preview; - bool _running; + QImage* _pixbuf; + QMutex* _lock; + bool _need_render; + int _xstart; + int _ystart; + int _xsize; + int _ysize; }; +static PreviewDrawingManager* _drawing_manager = NULL; + +/*************** PreviewDrawingThread ***************/ +PreviewDrawingThread::PreviewDrawingThread() : +QThread() +{ + _running = false; +} + +void PreviewDrawingThread::askStop() +{ + _running = false; +} + +void PreviewDrawingThread::run() +{ + _running = true; + while (_running) + { + _drawing_manager->performOneThreadJob(); + QThread::usleep(50000); + } +} + +/*************** PreviewDrawingManager ***************/ +PreviewDrawingManager::PreviewDrawingManager() +{ + _thread_count = QThread::idealThreadCount(); + if (_thread_count < 1) + { + _thread_count = 1; + } +} + +void PreviewDrawingManager::startThreads() +{ + for (int i = 0; i < _thread_count * 3; i++) + { + PreviewDrawingThread* thread = new PreviewDrawingThread(); + _threads.append(thread); + thread->start(); + } +} + +void PreviewDrawingManager::addChunk(PreviewChunk* chunk) +{ + _lock.lock(); + _chunks.append(chunk); + _updateQueue.append(chunk); + _lock.unlock(); +} + +void PreviewDrawingManager::removeChunks(BasePreview* preview) +{ + for (int i = 0; i < _chunks.size(); i++) + { + PreviewChunk* chunk; + chunk = _chunks.at(i); + if (chunk->isFrom(preview)) + { + _lock.lock(); + _chunks.remove(i); + _updateQueue.removeAll(chunk); + _lock.unlock(); + + i--; + } + } +} + +void PreviewDrawingManager::updateChunks(BasePreview* preview) +{ + for (int i = 0; i < _chunks.size(); i++) + { + PreviewChunk* chunk; + chunk = _chunks.at(i); + if (chunk->isFrom(preview)) + { + chunk->update(); + _lock.lock(); + if (!_updateQueue.contains(chunk)) + { + _updateQueue.prepend(chunk); + } + _lock.unlock(); + } + } +} + +void PreviewDrawingManager::performOneThreadJob() +{ + PreviewChunk* chunk; + do + { + chunk = NULL; + _lock.lock(); + if (!_updateQueue.isEmpty()) + { + chunk = _updateQueue.takeFirst(); + } + _lock.unlock(); + + if (chunk) + { + chunk->render(); + PreviewDrawingThread::usleep(50000); + } + else + { + PreviewDrawingThread::usleep(50000); + } + + } while (true); +} + +/*************** BasePreview ***************/ BasePreview::BasePreview(QWidget* parent) : - QWidget(parent) +QWidget(parent) { this->lock_drawing = new QMutex(); @@ -60,10 +228,10 @@ BasePreview::BasePreview(QWidget* parent) : this->yoffset = 0.0; this->pixbuf = new QImage(this->size(), QImage::Format_ARGB32); this->pixbuf->fill(0x00000000); + _width = width(); + _height = height(); this->alive = true; - this->need_restart = false; - this->need_render = true; QObject::connect(this, SIGNAL(contentChange()), this, SLOT(update())); QObject::connect(this, SIGNAL(redrawRequested()), this, SLOT(handleRedraw())); @@ -71,22 +239,29 @@ BasePreview::BasePreview(QWidget* parent) : this->setMinimumSize(256, 256); this->setMaximumSize(256, 256); this->resize(256, 256); - - this->updater = new PreviewDrawer(this); } BasePreview::~BasePreview() { alive = false; - ((PreviewDrawer*)updater)->askStop(); - updater->wait(); + _drawing_manager->removeChunks(this); - delete updater; delete pixbuf; delete lock_drawing; } +void BasePreview::initDrawers() +{ + _drawing_manager = new PreviewDrawingManager(); + _drawing_manager->startThreads(); +} + +void BasePreview::stopDrawers() +{ + //delete _drawing_manager; +} + void BasePreview::updateData() { } @@ -98,8 +273,8 @@ QColor BasePreview::getColor(double x, double y) void BasePreview::configScaling(double min, double max, double step, double init, bool logarithmic) { - double size = (double)width(); - + double size = (double) width(); + if (size >= 1.0) { conf_scale_min = min / size; @@ -131,44 +306,30 @@ void BasePreview::configScrolling(double xmin, double xmax, double xinit, double redraw(); } -void BasePreview::start() -{ - this->updater->start(); -} - -void BasePreview::doRender() -{ - if (this->alive) - { - if (this->need_render) - { - this->need_render = false; - this->renderPixbuf(); - emit contentChange(); - } - } -} - void BasePreview::redraw() { emit(redrawRequested()); } +QColor BasePreview::getPixelColor(int x, int y) +{ + return getColor((double) (x - _width / 2) * scaling + xoffset, (double) (y - _height / 2) * scaling + yoffset); +} + void BasePreview::handleRedraw() { lock_drawing->lock(); - + updateData(); - + QImage part = pixbuf->copy(); pixbuf->fill(0x00000000); QPainter painter(pixbuf); painter.setOpacity(0.99); painter.drawImage(0, 0, part); - need_render = true; - need_restart = true; - + _drawing_manager->updateChunks(this); + lock_drawing->unlock(); } @@ -180,11 +341,21 @@ void BasePreview::resizeEvent(QResizeEvent* event) image = this->pixbuf; + _width = event->size().width(); + _height = event->size().height(); + this->pixbuf = new QImage(this->size(), QImage::Format_ARGB32); this->pixbuf->fill(0x00000000); - this->need_render = true; - this->need_restart = true; + + _drawing_manager->removeChunks(this); + for (int x = 0; x < _width; x += 32) + { + for (int y = 0; y < _height; y += 32) + { + _drawing_manager->addChunk(new PreviewChunk(this, pixbuf, lock_drawing, x, y, x + 32 > _width ? _width - x : 32, y + 32 > _height ? _height - y : 32)); + } + } delete image; @@ -197,53 +368,6 @@ void BasePreview::paintEvent(QPaintEvent* event) painter.drawImage(0, 0, *this->pixbuf); } -void BasePreview::renderPixbuf() -{ - QColor col; - bool done; - int x, y, w, h; - - w = this->pixbuf->width(); - h = this->pixbuf->height(); - - this->need_restart = false; - - for (x = 0; x < w; x++) - { - this->lock_drawing->lock(); - - if (this->need_restart || !this->alive) - { - this->lock_drawing->unlock(); - return; - } - - done = false; - for (y = 0; y < h; y++) - { - if (this->need_restart || !this->alive) - { - this->lock_drawing->unlock(); - return; - } - - if (qAlpha(this->pixbuf->pixel(x, y)) < 255) - { - col = this->getColor((double)(x - w / 2) * this->scaling + this->xoffset, (double)(y - h / 2) * this->scaling + this->yoffset); - col.setAlpha(255); - this->pixbuf->setPixel(x, y, col.rgb()); - done = true; - } - } - if (done && (x == w - 1 || x % 10 == 0)) - { - emit contentChange(); - } - this->lock_drawing->unlock(); - PreviewDrawer::usleep(100); - } -} - void BasePreview::updateScaling() { if (conf_scroll_logarithmic && conf_scale_max - conf_scale_min > 0.0000001) @@ -270,7 +394,7 @@ void BasePreview::mouseMoveEvent(QMouseEvent* event) int dx, dy; int ndx, ndy; int width, height; - + if (event->buttons() & Qt::LeftButton) { dx = event->x() - mousex; @@ -280,44 +404,44 @@ void BasePreview::mouseMoveEvent(QMouseEvent* event) ndy = dy; if (xoffset - dx * scaling > conf_scroll_xmax) { - ndx = (int)floor((conf_scroll_xmax - xoffset) / scaling); + ndx = (int) floor((conf_scroll_xmax - xoffset) / scaling); } if (xoffset - dx * scaling < conf_scroll_xmin) { - ndx = (int)floor((conf_scroll_xmin - xoffset) / scaling); + ndx = (int) floor((conf_scroll_xmin - xoffset) / scaling); } if (yoffset - dy * scaling > conf_scroll_ymax) { - ndy = (int)floor((conf_scroll_ymax - yoffset) / scaling); + ndy = (int) floor((conf_scroll_ymax - yoffset) / scaling); } if (yoffset - dy * scaling < conf_scroll_ymin) { - ndy = (int)floor((conf_scroll_ymin - yoffset) / scaling); + ndy = (int) floor((conf_scroll_ymin - yoffset) / scaling); } if (ndx != 0 || ndy != 0) { width = this->width(); height = this->height(); - + if (ndx <= -width || ndx >= width || ndy <= -height || ndy >= height) { - xoffset -= (double)ndx * scaling; - yoffset -= (double)ndy * scaling; - + xoffset -= (double) ndx * scaling; + yoffset -= (double) ndy * scaling; + lock_drawing->lock(); pixbuf->fill(0x00000000); - need_render = true; + _drawing_manager->updateChunks(this); lock_drawing->unlock(); } else { int xstart, xsize, ystart, ysize; - + lock_drawing->lock(); - xoffset -= (double)ndx * scaling; - yoffset -= (double)ndy * scaling; - + xoffset -= (double) ndx * scaling; + yoffset -= (double) ndy * scaling; + if (ndx < 0) { xstart = -ndx; @@ -338,15 +462,15 @@ void BasePreview::mouseMoveEvent(QMouseEvent* event) ystart = 0; ysize = height - ndy; } - + QImage part = pixbuf->copy(xstart, ystart, xsize, ysize); QPainter painter(pixbuf); pixbuf->fill(0x00000000); painter.drawImage(xstart + ndx, ystart + ndy, part); - - need_render = true; + + _drawing_manager->updateChunks(this); lock_drawing->unlock(); - + emit contentChange(); } } @@ -362,7 +486,7 @@ void BasePreview::wheelEvent(QWheelEvent* event) double old_scaling; int width, height; int new_width, new_height; - + if (event->modifiers() & Qt::ShiftModifier) { factor = 5.0; @@ -375,10 +499,10 @@ void BasePreview::wheelEvent(QWheelEvent* event) { factor = 1.0; } - + if (event->orientation() == Qt::Vertical) { - if (event->delta() > 0 && scalingbase > conf_scale_min) + if (event->delta() > 0 && scalingbase > conf_scale_min) { scalingbase -= factor * conf_scale_step; if (scalingbase < conf_scale_min) @@ -386,7 +510,7 @@ void BasePreview::wheelEvent(QWheelEvent* event) scalingbase = conf_scale_min; } } - else if (event->delta() < 0 && scalingbase < conf_scale_max) + else if (event->delta() < 0 && scalingbase < conf_scale_max) { scalingbase += factor * conf_scale_step; if (scalingbase > conf_scale_max) @@ -408,21 +532,21 @@ void BasePreview::wheelEvent(QWheelEvent* event) old_scaling = scaling; updateScaling(); - + width = pixbuf->width(); height = pixbuf->height(); - + if (scaling < old_scaling) { lock_drawing->lock(); - new_width = (int)floor(((double)width) * scaling / old_scaling); - new_height = (int)floor(((double)height) * scaling / old_scaling); + new_width = (int) floor(((double) width) * scaling / old_scaling); + new_height = (int) floor(((double) height) * scaling / old_scaling); QImage part = pixbuf->copy((width - new_width) / 2, (height - new_height) / 2, new_width, new_height).scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QPainter painter(pixbuf); pixbuf->fill(0x00000000); painter.setOpacity(0.99); painter.drawImage(0, 0, part); - need_render = true; + _drawing_manager->updateChunks(this); lock_drawing->unlock(); emit contentChange(); @@ -430,16 +554,16 @@ void BasePreview::wheelEvent(QWheelEvent* event) else if (scaling > old_scaling) { lock_drawing->lock(); - QImage part = pixbuf->scaled((int)floor(((double)width) * old_scaling / scaling), (int)floor(((double)height) * old_scaling / scaling), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + QImage part = pixbuf->scaled((int) floor(((double) width) * old_scaling / scaling), (int) floor(((double) height) * old_scaling / scaling), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QPainter painter(pixbuf); pixbuf->fill(0x00000000); painter.setOpacity(0.99); painter.drawImage((width - part.width()) / 2, (height - part.height()) / 2, part); - need_render = true; + _drawing_manager->updateChunks(this); lock_drawing->unlock(); emit contentChange(); } - + event->accept(); } diff --git a/gui_qt/basepreview.h b/gui_qt/basepreview.h index bc049b8..a74260c 100644 --- a/gui_qt/basepreview.h +++ b/gui_qt/basepreview.h @@ -5,24 +5,30 @@ #include #include #include +#include +#include -class BasePreview:public QWidget -{ +class BasePreview : public QWidget { Q_OBJECT public: BasePreview(QWidget* parent); ~BasePreview(); - void start(); - void doRender(); + static void initDrawers(); + static void stopDrawers(); + void redraw(); + + inline void tellContentChange() {emit contentChange();} + + QColor getPixelColor(int x, int y); protected: virtual void updateData(); virtual QColor getColor(double x, double y); - - void configScaling(double min, double max, double step, double init, bool logarithmic=true); + + void configScaling(double min, double max, double step, double init, bool logarithmic = true); void configScrolling(double xmin, double xmax, double xinit, double ymin, double ymax, double yinit); double xoffset; @@ -30,29 +36,27 @@ protected: double scaling; private: - void renderPixbuf(); void updateScaling(); void resizeEvent(QResizeEvent* event); void paintEvent(QPaintEvent* event); - + void mousePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); void wheelEvent(QWheelEvent* event); - QThread* updater; - QMutex* lock_drawing; QImage* pixbuf; + int _width; + int _height; + int mousex; int mousey; double scalingbase; bool alive; - bool need_restart; - bool need_render; double conf_scroll_xmin; double conf_scroll_xmax; @@ -75,4 +79,43 @@ private slots: void handleRedraw(); }; + + +/*** Private section ***/ +class PreviewChunk; + +class PreviewDrawingThread : public QThread { +public: + PreviewDrawingThread(); + void askStop(); + + static inline void usleep(int us) { + QThread::usleep(us); + } + +protected: + void run(); + +private: + bool _running; +}; + +class PreviewDrawingManager { +public: + PreviewDrawingManager(); + void startThreads(); + void addChunk(PreviewChunk* chunk); + void removeChunks(BasePreview* preview); + void updateChunks(BasePreview* preview); + void performOneThreadJob(); + +private: + int _thread_count; + QVector _threads; + QVector _chunks; + QList _updateQueue; + QMutex _lock; +}; + + #endif diff --git a/gui_qt/dialognoise.cpp b/gui_qt/dialognoise.cpp index d035ac1..4aa17ed 100644 --- a/gui_qt/dialognoise.cpp +++ b/gui_qt/dialognoise.cpp @@ -105,14 +105,12 @@ DialogNoise::DialogNoise(QWidget *parent, NoiseGenerator* value): label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); previews->layout()->addWidget(label); previews->layout()->addWidget(previewLevel); - previewLevel->start(); previewTotal = new PreviewTotal(previews, _current); label = new QLabel(tr("Total preview")); label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); previews->layout()->addWidget(label); previews->layout()->addWidget(previewTotal); - previewTotal->start(); form = new QWidget(this); form->setLayout(new QVBoxLayout()); diff --git a/gui_qt/mainwindow.cpp b/gui_qt/mainwindow.cpp index ed7e497..ef82e23 100644 --- a/gui_qt/mainwindow.cpp +++ b/gui_qt/mainwindow.cpp @@ -11,6 +11,7 @@ #include #include +#include "basepreview.h" #include "formatmosphere.h" #include "formclouds.h" #include "formlighting.h" @@ -52,6 +53,8 @@ int main(int argc, char** argv) app.installTranslator(&qtTranslator); } + BasePreview::initDrawers(); + window = new MainWindow(); window->show(); diff --git a/i18n/paysages_fr.ts b/i18n/paysages_fr.ts index 5fa3377..df8a85e 100644 --- a/i18n/paysages_fr.ts +++ b/i18n/paysages_fr.ts @@ -29,7 +29,7 @@ Annuler les modifications - + Layer %1 Niveau %1 @@ -98,57 +98,57 @@ Cliquez avec le bouton droit sur un point pour le supprimer. Aperçu du composant - + Total preview Aperçu du total - + Noise components Composants du bruit - + Add component Ajouter un composant - + Remove component Supprimer un composant - + Component height Hauteur du composant - + Component scaling Echelle du composant - + Validate Valider - + Reset Recommencer - + Cancel Annuler - + Paysages 3D - Noise editor Paysages 3D - Editeur de bruit - + Component %1 Composant %1 @@ -704,139 +704,139 @@ Maintenir Ctrl : Plus rapide MainWindow - + Terrain Terrain - + Textures Textures - + Water Eau - + Atmosphere Atmosphère - + &Load &Ouvrir - + Crtl+L Ctrl+O - + &Explore (F2) &Explorer (F2) - + F2 F2 - + &Quick render (F5) &Rendu rapide (F5) - + F5 F5 - + Do you want to start a new scenery ? Any unsaved changes will be lost. Voulez-vous commencer un nouveau paysage ? Les modifications non sauvegardées seront perdues. - + Paysages 3D - New scenery Paysages 3D - Nouvelle scène - + Paysages 3D - Choose a file to save the scenery Paysages 3D - Choisissez un fichier pour enregistrer la scène - - + + Paysages 3D Scenery (*.p3d) Scène Paysages 3D (*.p3d) - + Paysages 3D - File saving error - + Can't write specified file : %1 - + Unexpected error while saving file : %1 - + Do you want to load a scenery from file ? Any unsaved changes will be lost. Voulez-vous charger une scène ? Les modifications nons sauvegardées seront perdues. - + Paysages 3D - Load scenery Paysages 3D - Charger une scène - + Paysages 3D - Choose a scenery file to load Paysages 3D - Choisissez un fichier de scène à charger - + Paysages 3D - File loading error - + Can't read specified file : %1 - + This file doesn't look like a Paysages 3D file : %1 - + This file was created with an incompatible Paysages 3D version : %1 - + Unexpected error while loading file : %1 - + A 3D landscape editing and rendering software. Authors : @@ -849,12 +849,12 @@ GLib - http://www.gtk.org/ - + Sky Ciel - + Clouds Nuages @@ -863,7 +863,7 @@ GLib - http://www.gtk.org/ Eclairage - + Render Rendu @@ -872,22 +872,22 @@ GLib - http://www.gtk.org/ &Scène - + &New &Nouveau - + Crtl+N Ctrl+N - + &Save &Sauvegarder - + Crtl+S Ctrl+S @@ -924,7 +924,7 @@ GLib - http://www.gtk.org/ Ai&de - + &About &A propos @@ -945,7 +945,7 @@ GLib - http://www.gtk.org/ Voulez-vous charger un paysage ? Les modifications nons sauvegardées seront perdues. - + Paysages 3D Paysages 3D diff --git a/lib_paysages/array.c b/lib_paysages/array.c index 1c14a66..a5087d6 100644 --- a/lib_paysages/array.c +++ b/lib_paysages/array.c @@ -6,7 +6,7 @@ void arrayCreate(Array* array, int item_size) { array->length = 0; - array->alloc_length = 3; + array->alloc_length = 1; array->item_size = item_size; array->dirty = 1; array->data = malloc((size_t)item_size * array->alloc_length); @@ -25,7 +25,7 @@ void* arrayAppend(Array* array, void* item) if (array->length >= array->alloc_length) { - array->alloc_length += 10; + array->alloc_length += 1; array->data = realloc(array->data, item_size * array->alloc_length); } @@ -100,7 +100,7 @@ void arrayClear(Array* array) { free(array->data); array->length = 0; - array->alloc_length = 3; - array->data = malloc((size_t)array->item_size * 3); + array->alloc_length = 1; + array->data = malloc((size_t)array->item_size * array->alloc_length); array->dirty = 1; }