Hello,
i thought my quesition was a mostly theoretical one, so somehow not really depending on details of my code. Also, i'm not sure, which parts of my code migth be relevant for the question. Also, it is not easy to describe what i do....
Concerning srtucture of my application:
My application generates video-mappings using opengl. It has a control-window and one or more output-windows. For the window-management i use wxWidgets 3.1.2 and all of this is controlled by my own script-interpreter.
Next to some helper-classes there are mainly two huge classes: clsParser (the interpreter) and clsControl (repesenting any type of window). On starting the program one clsParser-object is instanced that interprets a singel script instruction:
include "Config/loader.txt";
.
Now, for each object like menus, controls etc. one clsParser is instanced and some of then create clsControl-instances. Like this, the whole UI of my program is created.
If the user i.e. clicks a button, the clsContol-instance sends an small script to the associated clsParser-instance:
eval(OnClick);
, where "OnClick" might be a string-hashtable key, created during the initialisation-process. If it exists, it is interpreted.
The main-loop:
The rendering-loop uses the same technic using a timer event, that sends
eval(OnTimer);
to the root-clsParser-instance. There is only one "OnTimer"-script that is processed sequencially and that controls the rendering of one videoframe at a fixed framerate.
The interpreter knows a pair of instructions: "LinkTimer" and "EvaluateTimer". "LinkTimer" is called with an index (and a char*) while instancing an object i.e. at startup and causes a this-pointer to be enterd into one of several lists selected by the given index.
"EvaluateTimer" is also called with an index and is used in the OnTimer-script. It causes the interpreter to loop through the list with the given index, sending an event-instuction to each of the linked clsParser-instances (still sequentially). Like this, the execution is grouped in the following ordered steps:
1. Update of basic parameters
2. OpenGL-vertex-array-calculations (only for vertx-based 3d-rendering)
3. OpenGL-in-memory-rendering (to frambuffer)
4. Render frame to output-windows
5. Render frames to previews in the control-window
Step 4 & 5 of this sequence can be run in a parallel thread. What i expect to happen here is the following:
wxWidgets will refreshes the UI after the root-clsParser-instance returns from processing the OnTimer-instruction, which is a non-opengl-process that also don't need my interpreter. Therefore i want to use my interpreter in a parallel thread, to render the OpenGL-content to the output-vieports.
Concerning code:
The following c++-codelines are part of clsParser:
1 2 3 4
|
else if (!strcmp (nam, "run")) {
if (useThreads) std::async(std::launch::async, &clsParser::Exec, this, (char*)list[0] ) ;
else Exec((char*)list[0]);
}
| |
...where "(char*)list[0]" contains the string
eval(Thread_REFRESH);
refering to the following script-code-string:
1 2 3 4 5 6
|
var Thread_REFRESH = '
EvaluateTread (_T_THREAD_IDX_OUTPUT );
if (uiCNT<=0) { uiCNT = 1 + (8 * CPU * CPU);
EvaluateTread (_T_THREAD_IDX_REFRESH );
};
';
| |
The c++-code of "EvaluateThread" is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
else if (!strcmp (nam, "EvaluateTread")) {
wxArrayPtrVoid items = TimerThreads[(int)(list[0][0])]; clsVar *tStr;
for (int i = 0; i < items.Count(); i++) {
if (items[i] && (tStr = ((sTTI*)items[i])->p->strHash[
(char*)((sTTI*)items[i])->name
]) && (char*)*tStr) {
((sTTI*)items[i])->p->Exec((char*)*(tStr));
}
}
}
| |
... with "list" as a parameter-list (simelar argc, argv) containig "clsVar"-instances that is a typeindependent container-class.
(...)->name
contains the mentioned char*, set with the "LinkTimer"-call, so that
(...)->p->Exec((char*)*(tStr));
will start a local interpreatation of a locally stored script-fragment accesed by "->name".
In case of refreshing the OpenGL-outputs, this locally intepreted script-code may look like this:
...where "Refresh" is assiciated with the following c++-code in clsParser:
1 2 3 4 5 6
|
else if (!strcmp (nam, "Refresh")) {
if (Control->CTRL && Control->CTRL->IsShownOnScreen()) {
Control->CTRL->Refresh(false);
Control->CTRL->Update();
}
}
| |
Here Control is the clsConrol-instanse, CTRL is a pointer to wxGLCanvas, Refresh() envokes the wxPaint-event and Update() causes immediate execution of the paint-event-function. I tried it with and without the Update()-call.
Remember that the rendering is already done at this point, but all images are still in GPU-memory. So inside of the paint-event, OpenGL only renders a scaled version of the framebuffer to the wxGLCanvas-window. So the vertex-array has only four points.
So, if this is not done by the main-thread, the main-thread returns after calling the
if (useThreads) std::async(std::launch::async, &clsParser::Exec, this, (char*)list[0] ) ;
. So it runs idle and has time to refresh all the other UI-elements.
Sorry, that it is so much text, but i had no idea hwo i choud have described it in shorter words. The problem is, that because of interpreter, the executed code is splitted into little parts, that are located all over the source-code of my program.
---
Okay, so far the theory. But i'm not sure if it really does what i think.
In between i found out, that with "useThreads = true", the measured time between the
eval(OnTimer);
-instruction and root-parsers return is visibly shorter. So obviously the execution really runs parallel, and it does not crash the program.
But - as said in the first post - the CPU-diagram in the resurce-monitor grows in this case.
As i have no real experience with mulithreading, i was interested in some more information, but in internet i mostly find tutorials how to program complex multithreading-applications, but not really much concerning what is happening in a running parallel thread and how it interacts with i.e. the window-management of the system.
So i would be happy, if someone could tell me more concerning this, or where to find more information.
Thank you very much,
Frank