All centralized frameworks offer a way to graphically display partial results of the ongoing computation. Also to debug, it is important to have some graphics. However, a much higher computational speed could be achieved, probably at least one order of magnitude, if graphics were disabled. For this reason, Seti@Home [10] provides one graphical, and one non-graphical version of the client. In this section, we will see how GPU tries to minimize the problem.
We explored several possibilities of adding graphics to the ongoing computation. Some constraints of Delphi/Kylix limited our choice: in particular, graphical output cannot be displayed at any time, but must be synchronized with VCL, the Visual Component Library assigned to manage graphics and control behavior in Delphi.
A simple example, the pi plugin extended with graphics output, can be found in /dllbuilding/pi.
To provide graphics output, a plugin implements an additional method in the dynamic link library:
function update(var stk : TStack):Boolean; stdcall;
For each plugin, there can be only one update method. Update receives the same stk parameter described before. However, the struct stk contains now additional fields that were not present at the time [3] was written.
TStack, the struct definition is defined by:
type TStack = record Stack : Array [1..MAXSTACK] of Extended; StIdx : LongInt; { Index on Stack where Operations take place. StIdx cannot be more than MAXSTACK } {new fields} Progress : Real; { indicates plugin progress from 0 to 100 } My : Pointer; { used to store data passed between update and function itself } {$IFDEF MSWINDOWS} hw : HWND; { handle to the window where graphics are displayed } {$ENDIF} Update : Boolean; { function desires an update } {$IFDEF STRINGS} { future extension } StrStack : Array [1..MAXSTRINGS] of Strings; StrIdx : LongInt; {$ENDIF} end;
Besides the earlier described Stack and StIdx [3] we have new fields.
If a plugin wants graphical output, it needs a sort of cooperative multithreading. A plugin should release control by exiting the function from time to time while slowly increasing the field Progress. In between, the update function is called. Once Progress reaches 100, the virtual machine [3] considers the plugin finished and is ready to continue the stack evaluation. For backward compatibility reasons, if Progress remains 0 after the first call, the plugin is considered finished. Additionally, a graphics plugin will set stk.Update to true, to signal that the function wants an update.
We take a closer look at the internals, to understand better what happens; if GPU is idle and gets a new incoming job, it creates a ComputationThread (in ComputationThread.pas) that is executed via the following main loop (simplified):
Stk.Update := False; Stk.Progress := 0; repeat Found := PlugMan.ExecFuncInPlugins(FunctionName,stk,...); if (UpdateNeeded and Stk.Update) then Synchronize(Update); until ((Stk.Progress=0) or (Stk.Progress=100) or Terminated);
PlugMan (in PluginManager.pas) deals with
plugins. Method
ExecFuncInPlugins searches through all available plugins for a
method called FunctionName. Once found, it calls FunctionName and passes
stk as parameter.
UpdateNeeded is true only if the user is in the window, where plugins display graphics output. If GPU is minimized, or the volunteer is using other functionality of GPU (like the chat system), UpdateNeeded stays false and there is no need to synchronize with the VCL; in particular, we do not waste computer cycles to display graphics on a hidden component. In this way, we do not need to provide two separate plugins, one graphical and one non-graphical.
Terminated is set if the thread is forced to exit, if the user closes GPU, or if he wants to reset the virtual machine.