PDA

View Full Version : Separating load of GUI and worker thread



avanindra
12th March 2012, 05:45
Hi everyone,

I need some advice . I have a gui application written in Qt. The application calls some computationally intensive threads , they are computer vision or computational geometry implementations.

I have used QThread API for creating the threads.

The problem I am facing is even though algorithms are being executed in separate worker threads , they still affect the GUI and make it pretty unresponsive , in fact they even affect
all other applications running in the OS. I am using windows 7 64 bit on dual core processor. In windows resource manager I see the CPU usage , the worker thread never takes more than
50% of CPU and there is plenty of main memory free , so the resources are available in plenty , but still if click on GUI while the worker thread is running , the GUI becomes pretty
unresponsive for 15 to 20 seconds , then it responds in pretty flickery way.

One more important thing to notice is that , the application runs perfectly in unix environment.

Here is one of the slot

..................

textureGenerator is the thread here , I start it from one of the slots of the main GUI app.



generateTextureAct->setDisabled( true );

textureGenerator->load_calibration( hybridNccReconstructor->get_calibration() );

std::cout<<" project name : "<<projectName<<std::endl;

textureGenerator->set_project_name( projectName );

vtkPolyData *_meshed_data = vtkPolyData::New();

model.get_output( _meshed_data );

textureGenerator->set_working_directory( working_directory );

textureGenerator->load_data( _meshed_data );

textureGenerator->start();

...............

Any help would be greatly appreciated.

Thanks a lot.

Avanindra Singh

wysota
12th March 2012, 10:26
What does the implementation of the thread itself look like?

avanindra
12th March 2012, 12:13
This is the texture generator thread implementation texturegeneratorthread.cpp file. Please bear with me if code snippet is big.

create_texture is the method where actual computation is done , I call it in the run() method.

texturegeneratorthread.cpp .....


#include "texturegeneratorthread.h"
// #include "half_edge/halfedge.h"
// #include "texture_parameterization/textureparameterization.h"

#include <vtkPointData.h>
#include "utility-3d/objwriter.h"
#include "atlas_generation/halfedgedatabuilder.h"
#include "vtkTriangle.h"

namespace tr
{

TextureGeneratorThread::TextureGeneratorThread()
{

#ifdef TR_WINDOWS
directory_separator = "\\";
#endif

#ifdef TR_UNIX
directory_separator = "/";
#endif

texture_mapper = new TextureMapper();

atlas_generator = new AtlasGenerator();
mesh = vtkPolyData::New();

texture_image_name = "texture.jpg";

#ifdef TR_GUI_MODE
connect( atlas_generator , SIGNAL( percentageCompleted( int ) ) , this , SIGNAL( percentageCompleted( int ) ) );
connect( texture_mapper , SIGNAL( percentageCompleted( int ) ) , this , SIGNAL( percentageCompleted( int ) ) );
#endif

}


void TextureGeneratorThread::load_calibration(Calibrati on_Info* calib_data)
{
calibration = calib_data;
texture_mapper->load_calibration( calib_data );
}


void TextureGeneratorThread::load_data(vtkPolyData* data)
{

mesh->ShallowCopy( data );

texture_mapper->load_data( mesh );

}

void TextureGeneratorThread::set_working_directory(stri ng directory)
{
working_directory = directory;
}

string TextureGeneratorThread::get_texture_image_name()
{
return texture_image_name;
}

string TextureGeneratorThread::get_texture_image_path()
{
return texture_image_path;
}

void TextureGeneratorThread::set_project_name(string name)
{
project_name = name;
}



void TextureGeneratorThread::get_output(vtkPolyData* output)
{
output->ShallowCopy( mesh );

cout<<mesh->GetNumberOfCells()<<" "<<mesh->GetNumberOfPoints()<<endl;
}



void TextureGeneratorThread::create_texture()
{

// texture_mapper->load_data( mesh );
//
// texture_mapper->remove_invisible_facets();
//
// texture_mapper->get_output( mesh );

HalfEdgeData *_half_edge_mesh = new tr::HalfEdgeData();

/*_half_edge_mesh->load_mesh( mesh );

_half_edge_mesh->build();*/

HalfEdgeData half_edge_data;

HalfEdgeDataBuilder builder( *_half_edge_mesh );

vtkPolyData *data = mesh;

int _num_points = data->GetNumberOfPoints();

int nb_vertices = _num_points ;
int nb_facets = data->GetNumberOfCells() ;

builder.begin_surface() ;

for(int i = 0; i<nb_vertices; i++)
{
double x[ 3 ] ;
// input >> x >> y >> z ;
data->GetPoint( i , x );

builder.add_vertex( Point3d( x[ 0 ] , x[ 1 ] , x[ 2 ] ) ) ;
}

vtkIdList *cell = vtkIdList::New();

for( int i = 0; i < nb_facets; i++)
{
int nb ;

builder.begin_facet() ;

data->GetCellPoints( i , cell );

nb = cell->GetNumberOfIds();

if ( nb == 3 )
{
int v1 = cell->GetId( 0 );
int v2 = cell->GetId( 1 );
int v3 = cell->GetId( 2 );

if ( v1 == v2 || v2 == v3 || v3 == v1 )
{
continue;
}

double pt1[ 3 ] , pt2[ 3 ] , pt3[ 3 ];

data->GetPoint( v1 , pt1 );
data->GetPoint( v2 , pt2 );
data->GetPoint( v3 , pt3 );

double area = vtkTriangle::TriangleArea( pt1 , pt2 , pt3 );

if ( area == 0 )
{
continue;
}

}
else
{
continue;
}


for(int j = 0; j < nb; j++)
{
int p = cell->GetId( j ) ;

builder.add_vertex_to_facet(p) ;
}

builder.end_facet() ;

}

builder.end_surface() ;

emit percentageCompleted(5);

//atlas_generator->set_half_edge_mesh( _half_edge_mesh );

atlas_generator->set_initial_percentage( 0.05 );

// TextureParameterization *_parameterizer = new TextureParameterization();

// _parameterizer->load_data( _half_edge_mesh );

vtkPolyData *_parameterized_mesh = vtkPolyData::New();

// _parameterizer->get_output( _parameterized_mesh );

atlas_generator->set_half_edge_mesh( _half_edge_mesh );

atlas_generator->apply();

std::vector< bool > _is_border_facet;
std::vector< char > _opposite_vertex;

atlas_generator->get_output( _parameterized_mesh , _is_border_facet , _opposite_vertex );

texture_mapper->set_initial_percentage( 0.55 );

texture_mapper->load_data( _parameterized_mesh , _is_border_facet , _opposite_vertex);

// std::string _texture_image_path = working_directory + directory_separator + texture_image_name;

texture_image_path = working_directory + directory_separator + project_name + ".jpg";

std::string obj_file_path = working_directory + directory_separator + project_name + ".obj";

texture_mapper->create_texture_image2( texture_image_path );

texture_mapper->get_output( mesh );

ObjWriter *writer = new ObjWriter();

writer->set_file_name( obj_file_path );

writer->set_input( mesh );

writer->write();

delete writer;

std::cout<<" texture generation completed "<<std::endl;

emit percentageCompleted( 100 );

}

void TextureGeneratorThread::run()
{
emit started();
create_texture();
emit completed();
}



}

#include "texturegeneratorthread.moc"


Here is the header texturegeneratorthread.h ....


#ifndef TEXTUREGENERATORTHREAD_H
#define TEXTUREGENERATORTHREAD_H

#include "texturemapper.h"
#include "QThread"
#include "calibration_info.h"
#include "vtkPolyData.h"
#include "atlas_generation/atlasgenerator.h"
#include "string"

namespace tr{

class TextureGeneratorThread : public QThread
{
Q_OBJECT;

TextureMapper *texture_mapper;

AtlasGenerator *atlas_generator;

Calibration_Info *calibration;

vtkPolyData *mesh;

std::string directory_separator;

std::string working_directory;

std::string texture_image_name;

std::string project_name;

std::string texture_image_path;


protected:

void create_texture();


public:

TextureGeneratorThread();

void load_calibration( Calibration_Info *calib_data );

void load_data( vtkPolyData *data );

void set_working_directory( std::string directory );

std::string get_texture_image_name();

std::string get_texture_image_path();

void set_project_name( std::string name );

void get_output( vtkPolyData *output );



void run();

signals:

void started();
void percentageCompleted(int);
void completed();

};

}

#endif // TEXTUREGENERATORTHREAD_H





Thanks

Avanindra Singh

Spitfire
13th March 2012, 16:14
Hmmm no wonder you're app is unresponsive.
You're creating the thread but never start the event loop in it.

call exec() at the bottom of the TextureGeneratorThread::run() and remove both emits from there.
Thread will emit started() signal automatically and instead of emitting finished() yourself, call quit() when you're done.

Also you're loading meshes before starting the thread. My gut feeling is that loading and processing meshes is quite intensive so I would made the thread to that as well.

wysota
13th March 2012, 19:34
Hmmm no wonder you're app is unresponsive.
You're creating the thread but never start the event loop in it.
I don't think that's it. You don't have to have an event loop if you're not processing events. And the OP is not processing events. Maybe the architecture is not the best one in the world but since the thread IS started and no slots are being called from within the thread, the GUI thread should remain responsive. Of course provided that mesh loading is not the part making the GUI thread unresponsive.

@avanindra: By the way -- this is usually a useful lecture in such situations: Keeping the GUI Responsive

avanindra
16th March 2012, 09:20
Thanks for the replies. The problem was occurring due to setting the priorities of the threads. I had set some of the worker threads priorities to QThread::TimeCriticalPriority and this was hampering the responsiveness of the GUI.

@spitfire


Thread will emit started() signal automatically and instead of emitting finished() yourself, call quit() when you're done.

I wrote this code at time when I started learning about Qt Thread API , I emitted these unnecessary signals. nevertheless , these were not the responsiveness hampering codes.


@wysota
Thanks for the link . It was really helpful.



Regards

Avanindra Singh.

MarekR22
16th March 2012, 09:48
I'm giving this link constantly: http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/
Another thing is that you problem is so common that there is ready solution for it, you should use Qt Concurrent API see: http://qt-project.org/doc/qt-4.8/threads-qtconcurrent.html , this is quite easy to use and application scales it self without problems.

wysota
16th March 2012, 09:58
I'm giving this link constantly: http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/
Ok Marek, but in this exact case the OP's construction is valid and correct one. Not the simplest one as one could use QRunnable or just QtConcurrent::run() but having an event loop in this particular situation would not have changed anything as the only thing the thread does is that it performs an intensive calculation and then quits immediately.