Jump to content

Shrinavat

Members
  • Content Count

    42
  • Joined

  • Last visited

  • Days Won

    1

Posts posted by Shrinavat


  1. There is current code, in which I think the use of dictionaries is redundant:

    const
      N = $1;
      S = $2;
      E = $4;
      W = $8;
      
    var
      DX, DY, OPPOSITE: TDictionary<Integer, Integer>;
    
    // init dictionares
      DX := TDictionary<Integer, Integer>.Create;
      DX.Add(E, 1);
      DX.Add(W, -1);
      DX.Add(N, 0);
      DX.Add(S, 0);
      DY := TDictionary<Integer, Integer>.Create;
      DY.Add(E, 0);
      DY.Add(W, 0);
      DY.Add(N, -1);
      DY.Add(S, 1);
      OPPOSITE := TDictionary<Integer, Integer>.Create;
      OPPOSITE.Add(E, W);
      OPPOSITE.Add(W, E);
      OPPOSITE.Add(N, S);
      OPPOSITE.Add(S, N);
    
    // Usage of these dictionaries is shown below in the example code:
      var nx := x + DX[dir];  
      var ny := y + DY[dir];
      grid[ny, nx] := grid[ny, nx] or OPPOSITE[dir];

    To improve performance, I would like to avoid using dictionaries and use a simpler code instead... but I don't know which one?

    Any help in this arena would be greatly appreciated.


  2. On 2/26/2021 at 7:45 PM, Remy Lebeau said:

    I miss the old newsgroups 😢 None of the newer web-based forums have really matched up to them, in terms of usability, monitoring, organization.  It is just not the same.

    Alas, "Omnia orta cadunt" :classic_sad:

    2021-02-28_140856.png


  3. I have a pipeline for downloading files to a specific database.

    FFileDownloader := Parallel.Pipeline
      .Stage(Asy_URLBuilder)
      .Stage(Asy_URLRetriever)
      .Stage(Asy_DBInserter, Parallel.TaskConfig.OnMessage(Self))
      .Run;
    
    procedure Asy_URLBuilder(const input, output: IOmniBlockingCollection);
    var
      ovIN, ovOUT: TOmniValue;
    begin
      for ovIN in input do begin
        // ... compose url for downloading
    	output.TryAdd(ovOUT); // url is in ovOUT
      end;
    end;
    
    procedure Asy_URLRetriever(const input, output: IOmniBlockingCollection);
    var
      ovIN, ovOUT: TOmniValue;
    begin
      for ovIN in input do begin
        // ... downloading
    	output.TryAdd(ovOUT); // downloaded file is in ovOUT
      end;
    end;
    
    procedure Asy_DBInserter(const input, output: IOmniBlockingCollection; const task: IOmniTask);
    var
      ovIN: TOmniValue;
      DB: TxxxDatabase;
    begin
      DB := TxxxDatabase.Create(nil);
      DB.DatabaseName := ??? ;
      DB.Open;
      
      for ovIN in input do begin
    	// ... insert downloaded file in specific database
      end;
    
      DB.Commit;
      task.Comm.Send(WM_TASK_COMPLETED);
    end;

    I run a pipeline for various databases. I need to pass DatabaseName to the third stage of the pipeline. How can I do that?

    Can I use the SetParameter method of Task controller when creating a pipeline? And if so, how?

    Any help will be appreciated!


  4. I hope this is the right place to ask this... Is there a Delphi wrapper for libpng?

    I would very much like to use it, but I couldn't find it anywhere. Please share if you know anything. If someone got working wrapper for libpng and wants to share it at here would be cool.

    Thanks!


  5. On 1/31/2019 at 1:58 PM, Primož Gabrijelčič said:

    `workItem.Result` is a record property. Because of that, this code:

    
     workItem.Result.AsOwnedObject := TBitmap32.Create(256,256);

    is equivalent to running:

    
    var tmp: TOmniValue;
    
    tmp := workItem.Result;
    tmp.AsOwnedObject := TBitmap32.Create(256,256);

    And that fails. You should change the code to:

    
    var tmp: TOmniValue;
    
    tmp.AsOwnedObject := TBitmap32.Create(256,256);
    workItem.Result := tmp;

    I'll see if I can change the implementation of `IOmniWorkitem` to prevent such problems. 

    @Primož Gabrijelčič Do you have any progress for this issue fix? There are no new commits on github. Current workaround with using extra variable is not very elegant, although it works. 


  6. Here is the simplest code:



    unit Unit1;

    interface

    uses
      System.SysUtils,
      System.Classes,
      Vcl.Graphics,
      Vcl.Imaging.jpeg,
      Vcl.Controls,
      Vcl.Forms,
      Vcl.StdCtrls,
      OtlParallel,
      OtlTask;

    type
      TForm1 = class(TForm)
        btnReadInOTL: TButton;
        btnReadInGUI: TButton;
        procedure btnReadInGUIClick(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure btnReadInOTLClick(Sender: TObject);
      private
        FMS: TMemoryStream;
        FTestWorker: IOmniBackgroundWorker;
        procedure Asy_Test(const workItem: IOmniWorkItem);
        procedure HandleRequestDone(const Sender: IOmniBackgroundWorker; const
            workItem: IOmniWorkItem);
      end;

    var
      Form1: TForm1;

    implementation

    {$R *.dfm}

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FMS := TMemoryStream.Create;
      FMS.LoadFromFile('test.jpg');

      FTestWorker := Parallel.BackgroundWorker
        .Execute(Asy_Test)
        .OnRequestDone(HandleRequestDone);
    end;

    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      FMS.Free;
      FTestWorker.CancelAll;
      FTestWorker.Terminate(INFINITE);
      FTestWorker := nil;
    end;


    procedure TForm1.Asy_Test(const workItem: IOmniWorkItem);
    var
      wic: TWICImage;
      jpg: TJPEGImage;
      bs: TBytesStream;
    begin
      bs := TBytesStream.Create;
      try
        bs.Write(FMS.Memory^, FMS.Size);
        bs.Position := 0;
        wic := TWICImage.Create;
    //    jpg := TJPEGImage.Create;
        try
          wic.LoadFromStream(bs);
    //      jpg.LoadFromStream(bs);
    //      jpg.SaveToFile('TJPEGImage.jpg');
        finally
          FreeAndNil(wic);
    //      FreeAndNil(jpg);
        end;
      finally
        FreeAndNil(bs);
      end;
    end;

    procedure TForm1.HandleRequestDone(const Sender: IOmniBackgroundWorker; const
        workItem: IOmniWorkItem);
    begin
      if workItem.IsExceptional then
        Application.MessageBox(PChar(workItem.FatalException.Message), '')
    end;

    procedure TForm1.btnReadInGUIClick(Sender: TObject);
    var
      wic: TWICImage;
      bs: TBytesStream;
    begin
      bs := TBytesStream.Create;
      try
        bs.Write(FMS.Memory^, FMS.Size);
        bs.Position := 0;
        wic := TWICImage.Create;
        try
          wic.LoadFromStream(bs);
          Application.MessageBox('All is OK', '');
        finally
          FreeAndNil(wic);
        end;
      finally
        FreeAndNil(bs);
      end;
    end;

    procedure TForm1.btnReadInOTLClick(Sender: TObject);
    begin
      FTestWorker.Schedule(FTestWorker.CreateWorkItem(0));
    end;

    end.

    When I click on the button "Read Image in GUI Thread", then everything is fine. TWICImage loads the image from the stream.

    However, the same code does not work in the thread (click on the button "Read Image in OTL Thread"), I get AV "Access violation at address 0050316F in module 'Project1.exe'. Read of address 00000000".

    If you use TJPEGImage instead of TWICImage, then everything works fine in both cases.

    I don't get what the problem is. Can someone explain to me in simple terms what should I do in order for TWICImage to work in the thread? I want to use TWICImage because it allows you to download a wide variety of image formats, not just jpeg.

     

    Delphi 10.2.3, Win7SP1 x64. Project full source is in attachment

     

    test_OTL_read_from_memory.zip


  7. @Anders Melander I have latest Graphics32  with fixed TBitmap32 constructor access violation

    AV occurs when executing a line of code Image32.Bitmap.Assign(workItem.Result.AsObject as TBitmap32); - FBackend is nil:

     

     

    av.png

     

    This is in case when workItem.Result.Ownsobjects: = True

    If I comment out workItem.Result.OwnsObject := True line in Asy_Factory procedure and uncomment  workItem.Result.AsObject.Free; line in HandleRequestDone procedure, FBackend is not nil.

    It's a bug. But whose? Mine, GR32 or OTL?


  8. I carefully read the "3.9 Background worker" chapter from the "Parallel Programming with OmniThreadLibrary" book, and - hallelujah! :classic_smile:

    I was able to solve my problem with the help of Parallel.BackgroundWorker and Parallel.ParallelTask. Well, at least I think so... Again, project full source is in attachment, here is the main unit part:

    unit UBackgroundWorkerImageFactory;



    interface

    uses
      Winapi.Windows,
      System.SysUtils,
      System.Classes,
      Vcl.Controls,
      Vcl.Forms,
      Vcl.StdCtrls,
      GR32,
      GR32_Image,
      GR32_Resamplers,
      OtlCommon,
      OtlCollections,
      OtlParallel,
      OtlSync,
      OtlTask;

    type
      TfrmBackgroundWorkerImageFactory = class(TForm)
        Image32: TImage32;
        memLog: TMemo;
        btnStartTask1: TButton;
        btnCreateImageFactory: TButton;
        btnStartTask2: TButton;
        btnStartTask3: TButton;
        btnStartTask4: TButton;
        btnStartTask5: TButton;
        procedure FormDestroy(Sender: TObject);
        procedure btnCreateImageFactoryClick(Sender: TObject);
        procedure btnStartTask1Click(Sender: TObject);
        procedure btnStartTask2Click(Sender: TObject);
        procedure btnStartTask3Click(Sender: TObject);
        procedure btnStartTask4Click(Sender: TObject);
        procedure btnStartTask5Click(Sender: TObject);
      private
        FLogger: IOmniBackgroundWorker;
        FBackgroundWorkerImageFactory: IOmniBackgroundWorker;
        // asynchronous workers
        procedure Asy_Factory(const workItem: IOmniWorkItem);
        procedure HandleRequestDone(const Sender: IOmniBackgroundWorker;
          const workItem: IOmniWorkItem);
      end;

    var
      frmBackgroundWorkerImageFactory: TfrmBackgroundWorkerImageFactory;

    implementation

    {$R *.dfm}

    procedure TfrmBackgroundWorkerImageFactory.FormDestroy(Sender: TObject);
    begin
      if Assigned(FBackgroundWorkerImageFactory) then begin
        FBackgroundWorkerImageFactory.CancelAll;
        FBackgroundWorkerImageFactory.Terminate(INFINITE);
        FBackgroundWorkerImageFactory := nil;
      end;
      if Assigned(FLogger) then begin
        FLogger.Terminate(INFINITE);
        FLogger := nil;
      end;
    end;

    procedure TfrmBackgroundWorkerImageFactory.btnCreateImageFactoryClick(Sender: TObject);
    begin
      FLogger := Parallel.BackgroundWorker.NumTasks(1)
        .Execute(
          procedure(const workItem: IOmniWorkItem)
          begin
            memLog.Lines.Add(workItem.Data.AsString);
          end);

      FBackgroundWorkerImageFactory := Parallel.BackgroundWorker
        .Execute(Asy_Factory)
        .OnRequestDone(HandleRequestDone);

      btnCreateImageFactory.Enabled := False;
      btnStartTask1.Enabled := True;
      btnStartTask2.Enabled := True;
      btnStartTask3.Enabled := True;
      btnStartTask4.Enabled := True;
      btnStartTask5.Enabled := True;
      FLogger.Schedule(FLogger.CreateWorkItem('ImageFactory created!'));
    end;


    //******************************************************************************
    // ImageFactory
    //******************************************************************************
    procedure TfrmBackgroundWorkerImageFactory.Asy_Factory(const workItem: IOmniWorkItem);
    var
      Left, Top, Right, Bottom: integer;
      TileSource: TFileName;
      iTask: integer;
      numTasks: integer;
      RenderQueue: IOmniBlockingCollection;
      Renderer: IOmniParallelTask;
      TaskResults: array of TBitmap32;
      ResultBitmap: TBitmap32;
    begin
      RenderQueue := TOmniBlockingCollection.Create;
      numTasks := workItem.Data['LayersCount'].AsInteger + 1;
      Left := workItem.Data['Left'].AsInteger;
      Top := workItem.Data['Top'].AsInteger;
      Right := workItem.Data['Right'].AsInteger;
      Bottom := workItem.Data['Bottom'].AsInteger;

      // create multiple TBitmap32, one per child task
      SetLength(TaskResults, numTasks);
      for iTask := Low(TaskResults) to High(TaskResults) do
        TaskResults[iTask] := TBitmap32.Create(2*256, 2*256); // Large image is 2x2 tiles

      // start child tasks
      Renderer := Parallel.ParallelTask.NumTasks(numTasks).NoWait
        .Execute(
          procedure
          var
            workItem: TOmniValue;
            x,y, bx,by: integer;
            tile: TBitmap32;
          begin
            workItem := RenderQueue.Next;

            tile := TBitmap32.Create();
            try
              tile.Font.Size := 24;
              { loop of drawing tiles to a large bitmap }
              by := 0; // bx,by - coordinates on the bitmap where the tile is drawn (in tiles)
              for y := Top to Bottom do begin
                bx := 0;
                for x := Left to Right do begin
                  tile.LoadFromFile(workItem[1].AsString); // TileSource - for test only! In reality it will be like this: tile := GetTileFromDB(x, y)
                  tile.RenderText(10,10, Format('x=%d, y=%d', [x,y]), 0, clTrWhite32); // for info
                  TaskResults[workItem[0].AsInteger].Draw(bx*256, by*256, tile); // render tile to a large bitmap (output: index of Bitmap32 in taskResults array)
                  inc(bx);
                  FLogger.Schedule(FLogger.CreateWorkItem(
                      Format('processed %s: x=%d, y=%d (thread=%d)', [workItem[1].AsString,x,y,GetCurrentThreadID])));
                  //Sleep(500); // simulate workload
                end;
                inc(by);
              end;
            finally
              tile.Free;
            end;
          end
        );

      // provide input to child tasks
      for iTask := 0 to numTasks-1 do begin
        TileSource := workItem.Data['TileSource' + iTask.ToString];
        RenderQueue.Add(TOmniValue.Create([iTask, TileSource]));
      end;

      // process output
      Renderer.WaitFor(INFINITE);
      if not workItem.CancellationToken.IsSignalled then begin
        FLogger.Schedule(FLogger.CreateWorkItem(Format('Merging (thread=%d)', [GetCurrentThreadID])));

        ResultBitmap := TBitmap32.Create();
        ResultBitmap.Assign(TaskResults[0]); // base image only
        // if layers exists
        for iTask := 1 to High(TaskResults) do begin // merge all Layers over base image (with alpha channel)
          BlockTransfer(
            ResultBitmap,
            0, 0,
            ResultBitmap.ClipRect,
            TaskResults[iTask],
            TaskResults[iTask].ClipRect,
            dmBlend);
        end;
        workItem.Result := ResultBitmap;
    //    workItem.Result.OwnsObject := True;
      end;

      for iTask := Low(TaskResults) to High(TaskResults) do
        TaskResults[iTask].Free;
    end;

    procedure TfrmBackgroundWorkerImageFactory.HandleRequestDone(
      const Sender: IOmniBackgroundWorker; const workItem: IOmniWorkItem);
    begin
      Image32.Bitmap.Assign(workItem.Result.AsObject as TBitmap32);
      workItem.Result.AsObject.Free;
      FLogger.Schedule(FLogger.CreateWorkItem('ImageFactory task completed!'));
    end;
    //******************************************************************************


    //******************************************************************************
    procedure TfrmBackgroundWorkerImageFactory.btnStartTask1Click(Sender: TObject);
    var
      ov: TOmniValue;
    begin // create a single image in parallel
      ov := TOmniValue.CreateNamed(
        ['Left', 0,
        'Top', 0,
        'Right', 1,
        'Bottom', 1,
        'LayersCount', 0,
        'TileSource0', 'base.bmp'
        ]);

      FBackgroundWorkerImageFactory.Schedule(FBackgroundWorkerImageFactory.CreateWorkItem(ov));

      FLogger.Schedule(FLogger.CreateWorkItem('ImageFactory task #1 started - build one image, no merging'));
    end;

    procedure TfrmBackgroundWorkerImageFactory.btnStartTask2Click(Sender: TObject);
    var
      ov: TOmniValue;
    begin // create and merge TWO images in parallel
      ov := TOmniValue.CreateNamed(
        ['Left', 0,
        'Top', 0,
        'Right', 1,
        'Bottom', 1,
        'LayersCount', 1,
        'TileSource0', 'base.bmp',
        'TileSource1', 'overlay_1.bmp'
        ]);

      FBackgroundWorkerImageFactory.Schedule(FBackgroundWorkerImageFactory.CreateWorkItem(ov));

      FLogger.Schedule(FLogger.CreateWorkItem('ImageFactory task #2 started - build 2 images with merging'));
    end;

    procedure TfrmBackgroundWorkerImageFactory.btnStartTask3Click(Sender: TObject);
    var
      ov: TOmniValue;
    begin // create and merge THREE images in parallel
      ov := TOmniValue.CreateNamed(
        ['Left', 0,
        'Top', 0,
        'Right', 1,
        'Bottom', 1,
        'LayersCount', 2,
        'TileSource0', 'base.bmp',
        'TileSource1', 'overlay_1.bmp',
        'TileSource2', 'overlay_2.bmp'
        ]);

      FBackgroundWorkerImageFactory.Schedule(FBackgroundWorkerImageFactory.CreateWorkItem(ov));

      FLogger.Schedule(FLogger.CreateWorkItem('ImageFactory task #3 started - build 3 images with merging'));
    end;

    procedure TfrmBackgroundWorkerImageFactory.btnStartTask4Click(Sender: TObject);
    var
      ov: TOmniValue;
    begin // create and merge FOUR images in parallel
      ov := TOmniValue.CreateNamed(
        ['Left', 0,
        'Top', 0,
        'Right', 1,
        'Bottom', 1,
        'LayersCount', 3,
        'TileSource0', 'base.bmp',
        'TileSource1', 'overlay_1.bmp',
        'TileSource2', 'overlay_2.bmp',
        'TileSource3', 'overlay_3.bmp'
        ]);

      FBackgroundWorkerImageFactory.Schedule(FBackgroundWorkerImageFactory.CreateWorkItem(ov));

      FLogger.Schedule(FLogger.CreateWorkItem('ImageFactory task #4 started - build 4 images with merging'));
    end;

    procedure TfrmBackgroundWorkerImageFactory.btnStartTask5Click(Sender: TObject);
    var
      ov: TOmniValue;
    begin // create and merge FOUR images in parallel (reversed layers order of Task #4)
      ov := TOmniValue.CreateNamed(
        ['Left', 0,
        'Top', 0,
        'Right', 1,
        'Bottom', 1,
        'LayersCount', 3,
        'TileSource0', 'base.bmp',
        'TileSource1', 'overlay_3.bmp',
        'TileSource2', 'overlay_2.bmp',
        'TileSource3', 'overlay_1.bmp'
        ]);

      FBackgroundWorkerImageFactory.Schedule(FBackgroundWorkerImageFactory.CreateWorkItem(ov));

      FLogger.Schedule(FLogger.CreateWorkItem('ImageFactory task #5 started - reversed Task #4'));
    end;


    end.

    Everything running like clockwork. But I have a few questions again:

    1) If I change numTasks value in Renderer := Parallel.ParallelTask.NumTasks(numTasks).NoWait line to Environment.Process.Affinity.Count - 1, the application starts to work incorrectly. The code after Renderer.WaitFor(INFINITE); line is never executed and my app remains hanging in task Manager after closing. Is this my bug or OTL bug?

    2) If I uncomment workItem.Result.OwnsObject := True; line in Asy_Factory procedure and comment out workItem.Result.AsObject.Free; line in HandleRequestDone procedure, I obtain the following AV:

    "Access violation at address 00604CBF in module 'ImageFactory_BackgroundWorker.exe'. Read of address 00000000."

    Why? After all, TOmniValue is the owner of TObject-type data! When a object-owning TOmniValue goes out of scope, the owned object is automatically destroyed. But this is not so in my case, so I would like to know why it happens. Is this OTL bug?

    Dear @Primož Gabrijelčič, please could you answer these questions? Also, your opinion on my ImageFactory code is very important to me! Especially the asynchronous part of the code. Maybe is there a better/easier/rather way to do this?

     

     

     

    test_ImageFactory_BackgroundWorker.zip

×