Home » Archived » BIRT » How to improve BIRT Chart performance in SWT application
| How to improve BIRT Chart performance in SWT application [message #178658] |
Wed, 19 July 2006 03:24  |
Eclipse User |
|
|
|
Originally posted by: ivo.catapult.com
Hi,
We're rendering Charts onto a SWT Canvas in our RCP application.
Performance is sufficient for simple charts, but when the number
of data-points increases, screen repaints slow down. At 10000 data
points, it takes several seconds each time the Canvas needs to
be repainted. Since the SWT thread is doing the generation
and rendering, the GUI appears unresponsive, and this is turning
into a usability issue. It's possible that the number of data
points will increase to the 100,000 level in the future, at
which stage the rendering will take too long for users to
tolerate without a progress dialog. I had a look at the Chart FAQ
and also into IDeviceRenderer, and I suspect it should be possible
to generate the chart once into an Image, and then use this cached
Image to update the SWT Canvas, but I'm not sure exactly how to
go about doing this. Does someone out there have experience with this
issue and how to go about resolving it?
Thanks,
Ivo
|
|
|
| Re: How to improve BIRT Chart performance in SWT application [message #178705 is a reply to message #178658] |
Wed, 19 July 2006 07:25   |
Eclipse User |
|
|
|
Hi Ivo,
First, you can try using Generator.refresh() and not Generator.build(), if
the only thing that changes is the data. Typically what takes time is not
the generation, but the rendering itself. To make it faster, you want to
minimize the rendering time for each datapoint, which implies:
- a simple datapoint marker graphic with a plain color
- No datapoint label (use a tooltip instead)
- No X Axis label (or skip every 1000 labels if you want to show 10 labels
in a 10000 points chart for instance).
You can try upgrading to 2.1 too, the performance should be better.
As for making the UI responsive, it's indeed a good idea to generate the
chart into an image in a separate thread then update the UI with the new
image. It's like double-buffering and will also avoid any flickering, you
can look in the SWT chart examples of the examples plugin to see how to do
it.
Thanks,
David
"Ivo Bosticky" <ivo@catapult.com> wrote in message
news:e9kmnn$spc$1@utils.eclipse.org...
> Hi,
>
> We're rendering Charts onto a SWT Canvas in our RCP application.
> Performance is sufficient for simple charts, but when the number
> of data-points increases, screen repaints slow down. At 10000 data
> points, it takes several seconds each time the Canvas needs to
> be repainted. Since the SWT thread is doing the generation
> and rendering, the GUI appears unresponsive, and this is turning
> into a usability issue. It's possible that the number of data
> points will increase to the 100,000 level in the future, at
> which stage the rendering will take too long for users to
> tolerate without a progress dialog. I had a look at the Chart FAQ
> and also into IDeviceRenderer, and I suspect it should be possible
> to generate the chart once into an Image, and then use this cached
> Image to update the SWT Canvas, but I'm not sure exactly how to
> go about doing this. Does someone out there have experience with this
> issue and how to go about resolving it?
>
> Thanks,
> Ivo
>
>
|
|
|
| Re: How to improve BIRT Chart performance in SWT application [message #179003 is a reply to message #178705] |
Thu, 20 July 2006 03:03   |
Eclipse User |
|
|
|
Originally posted by: ivo.catapult.com
Hi David,
I was able to render our chart into an Image, and then draw this image on
the SWT Canvas each time it requires a repaint. With this approach most of
the repaint delays have been eliminated, and our problem has been solved.
I still can't get around the initial repaint delay, because I don't seem to
be able to get the Chart to build and render in a non-SWT thread. I get an
Invalid Access Exception while calling
IDeviceRenderer.getDisplayServer().getDpiResolution() while scaling. If the
scaling is taken out then I get the same exception from
Generator.build(...)
Thank you for your help.
Ivo
David Michonneau wrote:
> Hi Ivo,
>
> First, you can try using Generator.refresh() and not Generator.build(), if
> the only thing that changes is the data. Typically what takes time is not
> the generation, but the rendering itself. To make it faster, you want to
> minimize the rendering time for each datapoint, which implies:
>
> - a simple datapoint marker graphic with a plain color
> - No datapoint label (use a tooltip instead)
> - No X Axis label (or skip every 1000 labels if you want to show 10 labels
> in a 10000 points chart for instance).
>
> You can try upgrading to 2.1 too, the performance should be better.
>
> As for making the UI responsive, it's indeed a good idea to generate the
> chart into an image in a separate thread then update the UI with the new
> image. It's like double-buffering and will also avoid any flickering, you
> can look in the SWT chart examples of the examples plugin to see how to do
> it.
>
> Thanks,
>
> David
>
> "Ivo Bosticky" <ivo@catapult.com> wrote in message
> news:e9kmnn$spc$1@utils.eclipse.org...
>> Hi,
>>
>> We're rendering Charts onto a SWT Canvas in our RCP application.
>> Performance is sufficient for simple charts, but when the number
>> of data-points increases, screen repaints slow down. At 10000 data
>> points, it takes several seconds each time the Canvas needs to
>> be repainted. Since the SWT thread is doing the generation
>> and rendering, the GUI appears unresponsive, and this is turning
>> into a usability issue. It's possible that the number of data
>> points will increase to the 100,000 level in the future, at
>> which stage the rendering will take too long for users to
>> tolerate without a progress dialog. I had a look at the Chart FAQ
>> and also into IDeviceRenderer, and I suspect it should be possible
>> to generate the chart once into an Image, and then use this cached
>> Image to update the SWT Canvas, but I'm not sure exactly how to
>> go about doing this. Does someone out there have experience with this
>> issue and how to go about resolving it?
>>
>> Thanks,
>> Ivo
>>
>>
|
|
|
| Re: How to improve BIRT Chart performance in SWT application [message #179090 is a reply to message #179003] |
Thu, 20 July 2006 10:47   |
Eclipse User |
|
|
|
Hi Ivo,
Can you post the stack trace? How do you start the non-UI thread?
Thanks,
David
"Ivo Bosticky" <ivo@catapult.com> wrote in message
news:e9n9sv$lml$1@utils.eclipse.org...
> Hi David,
>
> I was able to render our chart into an Image, and then draw this image on
> the SWT Canvas each time it requires a repaint. With this approach most of
> the repaint delays have been eliminated, and our problem has been solved.
>
> I still can't get around the initial repaint delay, because I don't seem
> to
> be able to get the Chart to build and render in a non-SWT thread. I get an
> Invalid Access Exception while calling
> IDeviceRenderer.getDisplayServer().getDpiResolution() while scaling. If
> the
> scaling is taken out then I get the same exception from
> Generator.build(...)
>
> Thank you for your help.
> Ivo
>
>
>
>
> David Michonneau wrote:
>
>> Hi Ivo,
>>
>> First, you can try using Generator.refresh() and not Generator.build(),
>> if
>> the only thing that changes is the data. Typically what takes time is not
>> the generation, but the rendering itself. To make it faster, you want to
>> minimize the rendering time for each datapoint, which implies:
>>
>> - a simple datapoint marker graphic with a plain color
>> - No datapoint label (use a tooltip instead)
>> - No X Axis label (or skip every 1000 labels if you want to show 10
>> labels
>> in a 10000 points chart for instance).
>>
>> You can try upgrading to 2.1 too, the performance should be better.
>>
>> As for making the UI responsive, it's indeed a good idea to generate the
>> chart into an image in a separate thread then update the UI with the new
>> image. It's like double-buffering and will also avoid any flickering, you
>> can look in the SWT chart examples of the examples plugin to see how to
>> do
>> it.
>>
>> Thanks,
>>
>> David
>>
>> "Ivo Bosticky" <ivo@catapult.com> wrote in message
>> news:e9kmnn$spc$1@utils.eclipse.org...
>>> Hi,
>>>
>>> We're rendering Charts onto a SWT Canvas in our RCP application.
>>> Performance is sufficient for simple charts, but when the number
>>> of data-points increases, screen repaints slow down. At 10000 data
>>> points, it takes several seconds each time the Canvas needs to
>>> be repainted. Since the SWT thread is doing the generation
>>> and rendering, the GUI appears unresponsive, and this is turning
>>> into a usability issue. It's possible that the number of data
>>> points will increase to the 100,000 level in the future, at
>>> which stage the rendering will take too long for users to
>>> tolerate without a progress dialog. I had a look at the Chart FAQ
>>> and also into IDeviceRenderer, and I suspect it should be possible
>>> to generate the chart once into an Image, and then use this cached
>>> Image to update the SWT Canvas, but I'm not sure exactly how to
>>> go about doing this. Does someone out there have experience with this
>>> issue and how to go about resolving it?
>>>
>>> Thanks,
>>> Ivo
>>>
>>>
>
|
|
|
| Re: How to improve BIRT Chart performance in SWT application [message #179279 is a reply to message #179090] |
Thu, 20 July 2006 19:25   |
Eclipse User |
|
|
|
Originally posted by: ivo.catapult.com
David,
I pasted the stack trace below.
Just so that you know, we're using BIRT Chart Engine 2.0.1 in standalone
mode - so not latest release.
If it would make it easier, I can give you my BIRT Chart Canvas code to have
a look at (it should be easy for me to convert it into a stand-alone
application).
Thanks
Ivo
Jul 20, 2006 5:04:29 PM org.eclipse.birt.chart.exception.ChartException
logThis
SEVERE: Exception
org.eclipse.birt.chart.exception.ChartException: $NO-RB$ Invalid thread
access
at org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:867)
at
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.completeUpdate(BirtChartCanvas.java:143)
at
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.run(BirtChartCanvas.java:99)
at java.lang.Thread.run(Thread.java:595)
Caused by: org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:2942)
at org.eclipse.swt.SWT.error(SWT.java:2865)
at org.eclipse.swt.SWT.error(SWT.java:2836)
at org.eclipse.swt.widgets.Display.error(Display.java:995)
at org.eclipse.swt.widgets.Display.checkDevice(Display.java:626 )
at org.eclipse.swt.widgets.Display.getDPI(Display.java:1403)
at
org.eclipse.birt.chart.device.swt.SwtDisplayServer.getDpiRes olution(SwtDisplayServer.java:196)
at
org.eclipse.birt.chart.computation.withaxes.PlotWith2DAxes.<init >(PlotWith2DAxes.java:89)
at org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:860)
... 3 more
org.eclipse.birt.chart.exception.ChartException: $NO-RB$ Invalid thread
access
at org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:867)
at
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.completeUpdate(BirtChartCanvas.java:143)
at
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.run(BirtChartCanvas.java:99)
at java.lang.Thread.run(Thread.java:595)
Caused by: org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:2942)
at org.eclipse.swt.SWT.error(SWT.java:2865)
at org.eclipse.swt.SWT.error(SWT.java:2836)
at org.eclipse.swt.widgets.Display.error(Display.java:995)
at org.eclipse.swt.widgets.Display.checkDevice(Display.java:626 )
at org.eclipse.swt.widgets.Display.getDPI(Display.java:1403)
at
org.eclipse.birt.chart.device.swt.SwtDisplayServer.getDpiRes olution(SwtDisplayServer.java:196)
at
org.eclipse.birt.chart.computation.withaxes.PlotWith2DAxes.<init >(PlotWith2DAxes.java:89)
at org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:860)
... 3 more
David Michonneau wrote:
> Hi Ivo,
>
> Can you post the stack trace? How do you start the non-UI thread?
>
> Thanks,
>
> David
>
> "Ivo Bosticky" <ivo@catapult.com> wrote in message
> news:e9n9sv$lml$1@utils.eclipse.org...
>> Hi David,
>>
>> I was able to render our chart into an Image, and then draw this image on
>> the SWT Canvas each time it requires a repaint. With this approach most
>> of the repaint delays have been eliminated, and our problem has been
>> solved.
>>
>> I still can't get around the initial repaint delay, because I don't seem
>> to
>> be able to get the Chart to build and render in a non-SWT thread. I get
>> an Invalid Access Exception while calling
>> IDeviceRenderer.getDisplayServer().getDpiResolution() while scaling. If
>> the
>> scaling is taken out then I get the same exception from
>> Generator.build(...)
>>
>> Thank you for your help.
>> Ivo
>>
>>
>>
>>
>> David Michonneau wrote:
>>
>>> Hi Ivo,
>>>
>>> First, you can try using Generator.refresh() and not Generator.build(),
>>> if
>>> the only thing that changes is the data. Typically what takes time is
>>> not the generation, but the rendering itself. To make it faster, you
>>> want to minimize the rendering time for each datapoint, which implies:
>>>
>>> - a simple datapoint marker graphic with a plain color
>>> - No datapoint label (use a tooltip instead)
>>> - No X Axis label (or skip every 1000 labels if you want to show 10
>>> labels
>>> in a 10000 points chart for instance).
>>>
>>> You can try upgrading to 2.1 too, the performance should be better.
>>>
>>> As for making the UI responsive, it's indeed a good idea to generate the
>>> chart into an image in a separate thread then update the UI with the new
>>> image. It's like double-buffering and will also avoid any flickering,
>>> you can look in the SWT chart examples of the examples plugin to see how
>>> to do
>>> it.
>>>
>>> Thanks,
>>>
>>> David
>>>
>>> "Ivo Bosticky" <ivo@catapult.com> wrote in message
>>> news:e9kmnn$spc$1@utils.eclipse.org...
>>>> Hi,
>>>>
>>>> We're rendering Charts onto a SWT Canvas in our RCP application.
>>>> Performance is sufficient for simple charts, but when the number
>>>> of data-points increases, screen repaints slow down. At 10000 data
>>>> points, it takes several seconds each time the Canvas needs to
>>>> be repainted. Since the SWT thread is doing the generation
>>>> and rendering, the GUI appears unresponsive, and this is turning
>>>> into a usability issue. It's possible that the number of data
>>>> points will increase to the 100,000 level in the future, at
>>>> which stage the rendering will take too long for users to
>>>> tolerate without a progress dialog. I had a look at the Chart FAQ
>>>> and also into IDeviceRenderer, and I suspect it should be possible
>>>> to generate the chart once into an Image, and then use this cached
>>>> Image to update the SWT Canvas, but I'm not sure exactly how to
>>>> go about doing this. Does someone out there have experience with this
>>>> issue and how to go about resolving it?
>>>>
>>>> Thanks,
>>>> Ivo
>>>>
>>>>
>>
|
|
|
| Re: How to improve BIRT Chart performance in SWT application [message #179304 is a reply to message #179279] |
Fri, 21 July 2006 04:03   |
Eclipse User |
|
|
|
Originally posted by: ivan.markov.wizcom.bg
David,
- The "Invalid Thread Access" happens for us too, with BIRT 2.1 (LATEST).
- Oddly, it didn't happen for us with BIRT 2.0
The stacktrace is below. Here's my take why it happens (I should probably
enter bug report):
Suppose we have 2 threads,
- the regular UI thread
- worker thread calling the Generator.build()
Now:
1. GENERALLY, SWT does allow painting (or measuring stuff) with a GC from a
second thread: this means: you CAN call ANY method of GC from within a
worker thread.
2. BUT: You cannot create a new GC instance from the second thread like
this: "new GC(display)" because this construction calls methods of object
display.
The reason?
- The "display" object which is passed is actually a "widget", and so the
widget rules apply: calling its methods can only be done from the thread
where it was created (i.e. our UI thread). This applies to calls such as
"new GC(display)", display.getDPI() (Ivo's problem), and in fact to all the
methods of class Display.
Options?
- One option is to do new Display() from within the 2nd worker thread, but
this only works on Windows.
- Another option is to do the a) "new GC(display)", b) display.getXXX() in
the UI thread, like the following pattern:
in Generator.build():
Display display = Display.getCurrent();
GC gc;
if(display == null) {
// Bad, no display for this thread => we are not in (a) UI thread
display.syncExec(new Runnable() {void run() { gc = new GC(display);}});
} else {
gc = new GC(display);
}
OR:
Display display = Display.getCurrent();
int dpi;
if(display == null) {
// Bad, no display for this thread => we are not in (a) UI thread
display.syncExec(new Runnable() {void run() { dpi =
display.getDPI();}});
} else {
dpi = display.getDPI();
}
Needless to say this will only work if the UI thread spins the message loop,
and it is best if everything you need from the display is taken at one step.
Probably you should also bug the SWT devs to explain you all the intricasies
of using GC from within worker thread.
Ivan
P.S. The stacktrace:
org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:3374)
at org.eclipse.swt.SWT.error(SWT.java:3297)
at org.eclipse.swt.SWT.error(SWT.java:3268)
at org.eclipse.swt.widgets.Display.error(Display.java:978)
at org.eclipse.swt.widgets.Display.checkDevice(Display.java:638 )
at org.eclipse.swt.widgets.Display.getSystemFont(Display.java:2 038)
at org.eclipse.swt.widgets.Display.internal_new_GC(Display.java :2174)
at org.eclipse.swt.graphics.GC.<init>(GC.java:132)
at org.eclipse.swt.graphics.GC.<init>(GC.java:99)
at
org.eclipse.birt.chart.device.swt.SwtTextMetrics.<init>(SwtTextMetrics.java:
67)
at
org.eclipse.birt.chart.device.swt.SwtDisplayServer.getTextMe trics(SwtDisplay
Server.java:256)
at org.eclipse.birt.chart.computation.Methods.computeBox(Method s.java:764)
at
org.eclipse.birt.chart.model.layout.impl.LabelBlockImpl.getP referredSize(Lab
elBlockImpl.java:263)
at
org.eclipse.birt.chart.internal.layout.LayoutManager.doLayou t_tmp(LayoutMana
ger.java:65)
at
org.eclipse.birt.chart.internal.layout.LayoutManager.doLayou t(LayoutManager.
java:597)
at org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:962)
at
com.sciant.cpgi.ccp.ui.internal.GenerateChartStateJob.run(Ge nerateChartState
Job.java:47)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:58)
"Ivo Bosticky" <ivo@catapult.com> wrote in message
news:e9p3dp$nhv$1@utils.eclipse.org...
> David,
>
> I pasted the stack trace below.
>
> Just so that you know, we're using BIRT Chart Engine 2.0.1 in standalone
> mode - so not latest release.
>
> If it would make it easier, I can give you my BIRT Chart Canvas code to
have
> a look at (it should be easy for me to convert it into a stand-alone
> application).
>
> Thanks
> Ivo
>
> Jul 20, 2006 5:04:29 PM org.eclipse.birt.chart.exception.ChartException
> logThis
> SEVERE: Exception
> org.eclipse.birt.chart.exception.ChartException: $NO-RB$ Invalid thread
> access
> at
org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:867)
> at
>
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.completeU
pdate(BirtChartCanvas.java:143)
> at
>
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.run(BirtC
hartCanvas.java:99)
> at java.lang.Thread.run(Thread.java:595)
> Caused by: org.eclipse.swt.SWTException: Invalid thread access
> at org.eclipse.swt.SWT.error(SWT.java:2942)
> at org.eclipse.swt.SWT.error(SWT.java:2865)
> at org.eclipse.swt.SWT.error(SWT.java:2836)
> at org.eclipse.swt.widgets.Display.error(Display.java:995)
> at org.eclipse.swt.widgets.Display.checkDevice(Display.java:626 )
> at org.eclipse.swt.widgets.Display.getDPI(Display.java:1403)
> at
>
org.eclipse.birt.chart.device.swt.SwtDisplayServer.getDpiRes olution(SwtDispl
ayServer.java:196)
> at
>
org.eclipse.birt.chart.computation.withaxes.PlotWith2DAxes.<init >(PlotWith2D
Axes.java:89)
> at
org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:860)
> ... 3 more
> org.eclipse.birt.chart.exception.ChartException: $NO-RB$ Invalid thread
> access
> at
org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:867)
> at
>
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.completeU
pdate(BirtChartCanvas.java:143)
> at
>
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.run(BirtC
hartCanvas.java:99)
> at java.lang.Thread.run(Thread.java:595)
> Caused by: org.eclipse.swt.SWTException: Invalid thread access
> at org.eclipse.swt.SWT.error(SWT.java:2942)
> at org.eclipse.swt.SWT.error(SWT.java:2865)
> at org.eclipse.swt.SWT.error(SWT.java:2836)
> at org.eclipse.swt.widgets.Display.error(Display.java:995)
> at org.eclipse.swt.widgets.Display.checkDevice(Display.java:626 )
> at org.eclipse.swt.widgets.Display.getDPI(Display.java:1403)
> at
>
org.eclipse.birt.chart.device.swt.SwtDisplayServer.getDpiRes olution(SwtDispl
ayServer.java:196)
> at
>
org.eclipse.birt.chart.computation.withaxes.PlotWith2DAxes.<init >(PlotWith2D
Axes.java:89)
> at
org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:860)
> ... 3 more
>
> David Michonneau wrote:
>
> > Hi Ivo,
> >
> > Can you post the stack trace? How do you start the non-UI thread?
> >
> > Thanks,
> >
> > David
> >
> > "Ivo Bosticky" <ivo@catapult.com> wrote in message
> > news:e9n9sv$lml$1@utils.eclipse.org...
> >> Hi David,
> >>
> >> I was able to render our chart into an Image, and then draw this image
on
> >> the SWT Canvas each time it requires a repaint. With this approach most
> >> of the repaint delays have been eliminated, and our problem has been
> >> solved.
> >>
> >> I still can't get around the initial repaint delay, because I don't
seem
> >> to
> >> be able to get the Chart to build and render in a non-SWT thread. I get
> >> an Invalid Access Exception while calling
> >> IDeviceRenderer.getDisplayServer().getDpiResolution() while scaling. If
> >> the
> >> scaling is taken out then I get the same exception from
> >> Generator.build(...)
> >>
> >> Thank you for your help.
> >> Ivo
> >>
> >>
> >>
> >>
> >> David Michonneau wrote:
> >>
> >>> Hi Ivo,
> >>>
> >>> First, you can try using Generator.refresh() and not
Generator.build(),
> >>> if
> >>> the only thing that changes is the data. Typically what takes time is
> >>> not the generation, but the rendering itself. To make it faster, you
> >>> want to minimize the rendering time for each datapoint, which implies:
> >>>
> >>> - a simple datapoint marker graphic with a plain color
> >>> - No datapoint label (use a tooltip instead)
> >>> - No X Axis label (or skip every 1000 labels if you want to show 10
> >>> labels
> >>> in a 10000 points chart for instance).
> >>>
> >>> You can try upgrading to 2.1 too, the performance should be better.
> >>>
> >>> As for making the UI responsive, it's indeed a good idea to generate
the
> >>> chart into an image in a separate thread then update the UI with the
new
> >>> image. It's like double-buffering and will also avoid any flickering,
> >>> you can look in the SWT chart examples of the examples plugin to see
how
> >>> to do
> >>> it.
> >>>
> >>> Thanks,
> >>>
> >>> David
> >>>
> >>> "Ivo Bosticky" <ivo@catapult.com> wrote in message
> >>> news:e9kmnn$spc$1@utils.eclipse.org...
> >>>> Hi,
> >>>>
> >>>> We're rendering Charts onto a SWT Canvas in our RCP application.
> >>>> Performance is sufficient for simple charts, but when the number
> >>>> of data-points increases, screen repaints slow down. At 10000 data
> >>>> points, it takes several seconds each time the Canvas needs to
> >>>> be repainted. Since the SWT thread is doing the generation
> >>>> and rendering, the GUI appears unresponsive, and this is turning
> >>>> into a usability issue. It's possible that the number of data
> >>>> points will increase to the 100,000 level in the future, at
> >>>> which stage the rendering will take too long for users to
> >>>> tolerate without a progress dialog. I had a look at the Chart FAQ
> >>>> and also into IDeviceRenderer, and I suspect it should be possible
> >>>> to generate the chart once into an Image, and then use this cached
> >>>> Image to update the SWT Canvas, but I'm not sure exactly how to
> >>>> go about doing this. Does someone out there have experience with this
> >>>> issue and how to go about resolving it?
> >>>>
> >>>> Thanks,
> >>>> Ivo
> >>>>
> >>>>
> >>
>
|
|
|
| Re: How to improve BIRT Chart performance in SWT application [message #179698 is a reply to message #179304] |
Mon, 24 July 2006 06:29   |
Eclipse User |
|
|
|
Hi Ivan,
please file a bugzilla entry with your investigation so we resolve it in the
chart engine.
Thanks,
David
"Ivan Markov" <ivan.markov@wizcom.bg> wrote in message
news:e9q1og$f20$1@utils.eclipse.org...
> David,
>
> - The "Invalid Thread Access" happens for us too, with BIRT 2.1 (LATEST).
> - Oddly, it didn't happen for us with BIRT 2.0
>
> The stacktrace is below. Here's my take why it happens (I should probably
> enter bug report):
> Suppose we have 2 threads,
> - the regular UI thread
> - worker thread calling the Generator.build()
>
> Now:
> 1. GENERALLY, SWT does allow painting (or measuring stuff) with a GC from
> a
> second thread: this means: you CAN call ANY method of GC from within a
> worker thread.
> 2. BUT: You cannot create a new GC instance from the second thread like
> this: "new GC(display)" because this construction calls methods of object
> display.
>
> The reason?
> - The "display" object which is passed is actually a "widget", and so the
> widget rules apply: calling its methods can only be done from the thread
> where it was created (i.e. our UI thread). This applies to calls such as
> "new GC(display)", display.getDPI() (Ivo's problem), and in fact to all
> the
> methods of class Display.
>
> Options?
> - One option is to do new Display() from within the 2nd worker thread, but
> this only works on Windows.
> - Another option is to do the a) "new GC(display)", b) display.getXXX() in
> the UI thread, like the following pattern:
>
> in Generator.build():
>
> Display display = Display.getCurrent();
> GC gc;
> if(display == null) {
> // Bad, no display for this thread => we are not in (a) UI thread
> display.syncExec(new Runnable() {void run() { gc = new GC(display);}});
> } else {
> gc = new GC(display);
> }
>
> OR:
>
> Display display = Display.getCurrent();
> int dpi;
> if(display == null) {
> // Bad, no display for this thread => we are not in (a) UI thread
> display.syncExec(new Runnable() {void run() { dpi =
> display.getDPI();}});
> } else {
> dpi = display.getDPI();
> }
>
> Needless to say this will only work if the UI thread spins the message
> loop,
> and it is best if everything you need from the display is taken at one
> step.
> Probably you should also bug the SWT devs to explain you all the
> intricasies
> of using GC from within worker thread.
>
> Ivan
>
> P.S. The stacktrace:
>
> org.eclipse.swt.SWTException: Invalid thread access
> at org.eclipse.swt.SWT.error(SWT.java:3374)
> at org.eclipse.swt.SWT.error(SWT.java:3297)
> at org.eclipse.swt.SWT.error(SWT.java:3268)
> at org.eclipse.swt.widgets.Display.error(Display.java:978)
> at org.eclipse.swt.widgets.Display.checkDevice(Display.java:638 )
> at org.eclipse.swt.widgets.Display.getSystemFont(Display.java:2 038)
> at org.eclipse.swt.widgets.Display.internal_new_GC(Display.java :2174)
> at org.eclipse.swt.graphics.GC.<init>(GC.java:132)
> at org.eclipse.swt.graphics.GC.<init>(GC.java:99)
> at
> org.eclipse.birt.chart.device.swt.SwtTextMetrics.<init>(SwtTextMetrics.java:
> 67)
> at
> org.eclipse.birt.chart.device.swt.SwtDisplayServer.getTextMe trics(SwtDisplay
> Server.java:256)
> at org.eclipse.birt.chart.computation.Methods.computeBox(Method s.java:764)
> at
> org.eclipse.birt.chart.model.layout.impl.LabelBlockImpl.getP referredSize(Lab
> elBlockImpl.java:263)
> at
> org.eclipse.birt.chart.internal.layout.LayoutManager.doLayou t_tmp(LayoutMana
> ger.java:65)
> at
> org.eclipse.birt.chart.internal.layout.LayoutManager.doLayou t(LayoutManager.
> java:597)
> at org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:962)
> at
> com.sciant.cpgi.ccp.ui.internal.GenerateChartStateJob.run(Ge nerateChartState
> Job.java:47)
> at org.eclipse.core.internal.jobs.Worker.run(Worker.java:58)
>
>
>
> "Ivo Bosticky" <ivo@catapult.com> wrote in message
> news:e9p3dp$nhv$1@utils.eclipse.org...
>> David,
>>
>> I pasted the stack trace below.
>>
>> Just so that you know, we're using BIRT Chart Engine 2.0.1 in standalone
>> mode - so not latest release.
>>
>> If it would make it easier, I can give you my BIRT Chart Canvas code to
> have
>> a look at (it should be easy for me to convert it into a stand-alone
>> application).
>>
>> Thanks
>> Ivo
>>
>> Jul 20, 2006 5:04:29 PM org.eclipse.birt.chart.exception.ChartException
>> logThis
>> SEVERE: Exception
>> org.eclipse.birt.chart.exception.ChartException: $NO-RB$ Invalid thread
>> access
>> at
> org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:867)
>> at
>>
> com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.completeU
> pdate(BirtChartCanvas.java:143)
>> at
>>
> com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.run(BirtC
> hartCanvas.java:99)
>> at java.lang.Thread.run(Thread.java:595)
>> Caused by: org.eclipse.swt.SWTException: Invalid thread access
>> at org.eclipse.swt.SWT.error(SWT.java:2942)
>> at org.eclipse.swt.SWT.error(SWT.java:2865)
>> at org.eclipse.swt.SWT.error(SWT.java:2836)
>> at org.eclipse.swt.widgets.Display.error(Display.java:995)
>> at org.eclipse.swt.widgets.Display.checkDevice(Display.java:626 )
>> at org.eclipse.swt.widgets.Display.getDPI(Display.java:1403)
>> at
>>
> org.eclipse.birt.chart.device.swt.SwtDisplayServer.getDpiRes olution(SwtDispl
> ayServer.java:196)
>> at
>>
> org.eclipse.birt.chart.computation.withaxes.PlotWith2DAxes.<init >(PlotWith2D
> Axes.java:89)
>> at
> org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:860)
>> ... 3 more
>> org.eclipse.birt.chart.exception.ChartException: $NO-RB$ Invalid thread
>> access
>> at
> org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:867)
>> at
>>
> com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.completeU
> pdate(BirtChartCanvas.java:143)
>> at
>>
> com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.run(BirtC
> hartCanvas.java:99)
>> at java.lang.Thread.run(Thread.java:595)
>> Caused by: org.eclipse.swt.SWTException: Invalid thread access
>> at org.eclipse.swt.SWT.error(SWT.java:2942)
>> at org.eclipse.swt.SWT.error(SWT.java:2865)
>> at org.eclipse.swt.SWT.error(SWT.java:2836)
>> at org.eclipse.swt.widgets.Display.error(Display.java:995)
>> at org.eclipse.swt.widgets.Display.checkDevice(Display.java:626 )
>> at org.eclipse.swt.widgets.Display.getDPI(Display.java:1403)
>> at
>>
> org.eclipse.birt.chart.device.swt.SwtDisplayServer.getDpiRes olution(SwtDispl
> ayServer.java:196)
>> at
>>
> org.eclipse.birt.chart.computation.withaxes.PlotWith2DAxes.<init >(PlotWith2D
> Axes.java:89)
>> at
> org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:860)
>> ... 3 more
>>
>> David Michonneau wrote:
>>
>> > Hi Ivo,
>> >
>> > Can you post the stack trace? How do you start the non-UI thread?
>> >
>> > Thanks,
>> >
>> > David
>> >
>> > "Ivo Bosticky" <ivo@catapult.com> wrote in message
>> > news:e9n9sv$lml$1@utils.eclipse.org...
>> >> Hi David,
>> >>
>> >> I was able to render our chart into an Image, and then draw this image
> on
>> >> the SWT Canvas each time it requires a repaint. With this approach
>> >> most
>> >> of the repaint delays have been eliminated, and our problem has been
>> >> solved.
>> >>
>> >> I still can't get around the initial repaint delay, because I don't
> seem
>> >> to
>> >> be able to get the Chart to build and render in a non-SWT thread. I
>> >> get
>> >> an Invalid Access Exception while calling
>> >> IDeviceRenderer.getDisplayServer().getDpiResolution() while scaling.
>> >> If
>> >> the
>> >> scaling is taken out then I get the same exception from
>> >> Generator.build(...)
>> >>
>> >> Thank you for your help.
>> >> Ivo
>> >>
>> >>
>> >>
>> >>
>> >> David Michonneau wrote:
>> >>
>> >>> Hi Ivo,
>> >>>
>> >>> First, you can try using Generator.refresh() and not
> Generator.build(),
>> >>> if
>> >>> the only thing that changes is the data. Typically what takes time is
>> >>> not the generation, but the rendering itself. To make it faster, you
>> >>> want to minimize the rendering time for each datapoint, which
>> >>> implies:
>> >>>
>> >>> - a simple datapoint marker graphic with a plain color
>> >>> - No datapoint label (use a tooltip instead)
>> >>> - No X Axis label (or skip every 1000 labels if you want to show 10
>> >>> labels
>> >>> in a 10000 points chart for instance).
>> >>>
>> >>> You can try upgrading to 2.1 too, the performance should be better.
>> >>>
>> >>> As for making the UI responsive, it's indeed a good idea to generate
> the
>> >>> chart into an image in a separate thread then update the UI with the
> new
>> >>> image. It's like double-buffering and will also avoid any flickering,
>> >>> you can look in the SWT chart examples of the examples plugin to see
> how
>> >>> to do
>> >>> it.
>> >>>
>> >>> Thanks,
>> >>>
>> >>> David
>> >>>
>> >>> "Ivo Bosticky" <ivo@catapult.com> wrote in message
>> >>> news:e9kmnn$spc$1@utils.eclipse.org...
>> >>>> Hi,
>> >>>>
>> >>>> We're rendering Charts onto a SWT Canvas in our RCP application.
>> >>>> Performance is sufficient for simple charts, but when the number
>> >>>> of data-points increases, screen repaints slow down. At 10000 data
>> >>>> points, it takes several seconds each time the Canvas needs to
>> >>>> be repainted. Since the SWT thread is doing the generation
>> >>>> and rendering, the GUI appears unresponsive, and this is turning
>> >>>> into a usability issue. It's possible that the number of data
>> >>>> points will increase to the 100,000 level in the future, at
>> >>>> which stage the rendering will take too long for users to
>> >>>> tolerate without a progress dialog. I had a look at the Chart FAQ
>> >>>> and also into IDeviceRenderer, and I suspect it should be possible
>> >>>> to generate the chart once into an Image, and then use this cached
>> >>>> Image to update the SWT Canvas, but I'm not sure exactly how to
>> >>>> go about doing this. Does someone out there have experience with
>> >>>> this
>> >>>> issue and how to go about resolving it?
>> >>>>
>> >>>> Thanks,
>> >>>> Ivo
>> >>>>
>> >>>>
>> >>
>>
>
>
|
|
|
| Re: How to improve BIRT Chart performance in SWT application [message #179707 is a reply to message #179698] |
Mon, 24 July 2006 07:41  |
Eclipse User |
|
|
|
Originally posted by: ivan.markov.wizcom.bg
Done:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=151576
Ivan
"David Michonneau" <dmichonneau@actuate.com> wrote in message
news:ea27ec$e3d$1@utils.eclipse.org...
> Hi Ivan,
>
> please file a bugzilla entry with your investigation so we resolve it in
the
> chart engine.
>
> Thanks,
>
> David
>
> "Ivan Markov" <ivan.markov@wizcom.bg> wrote in message
> news:e9q1og$f20$1@utils.eclipse.org...
> > David,
> >
> > - The "Invalid Thread Access" happens for us too, with BIRT 2.1
(LATEST).
> > - Oddly, it didn't happen for us with BIRT 2.0
> >
> > The stacktrace is below. Here's my take why it happens (I should
probably
> > enter bug report):
> > Suppose we have 2 threads,
> > - the regular UI thread
> > - worker thread calling the Generator.build()
> >
> > Now:
> > 1. GENERALLY, SWT does allow painting (or measuring stuff) with a GC
from
> > a
> > second thread: this means: you CAN call ANY method of GC from within a
> > worker thread.
> > 2. BUT: You cannot create a new GC instance from the second thread like
> > this: "new GC(display)" because this construction calls methods of
object
> > display.
> >
> > The reason?
> > - The "display" object which is passed is actually a "widget", and so
the
> > widget rules apply: calling its methods can only be done from the thread
> > where it was created (i.e. our UI thread). This applies to calls such as
> > "new GC(display)", display.getDPI() (Ivo's problem), and in fact to all
> > the
> > methods of class Display.
> >
> > Options?
> > - One option is to do new Display() from within the 2nd worker thread,
but
> > this only works on Windows.
> > - Another option is to do the a) "new GC(display)", b) display.getXXX()
in
> > the UI thread, like the following pattern:
> >
> > in Generator.build():
> >
> > Display display = Display.getCurrent();
> > GC gc;
> > if(display == null) {
> > // Bad, no display for this thread => we are not in (a) UI thread
> > display.syncExec(new Runnable() {void run() { gc = new
GC(display);}});
> > } else {
> > gc = new GC(display);
> > }
> >
> > OR:
> >
> > Display display = Display.getCurrent();
> > int dpi;
> > if(display == null) {
> > // Bad, no display for this thread => we are not in (a) UI thread
> > display.syncExec(new Runnable() {void run() { dpi =
> > display.getDPI();}});
> > } else {
> > dpi = display.getDPI();
> > }
> >
> > Needless to say this will only work if the UI thread spins the message
> > loop,
> > and it is best if everything you need from the display is taken at one
> > step.
> > Probably you should also bug the SWT devs to explain you all the
> > intricasies
> > of using GC from within worker thread.
> >
> > Ivan
> >
> > P.S. The stacktrace:
> >
> > org.eclipse.swt.SWTException: Invalid thread access
> > at org.eclipse.swt.SWT.error(SWT.java:3374)
> > at org.eclipse.swt.SWT.error(SWT.java:3297)
> > at org.eclipse.swt.SWT.error(SWT.java:3268)
> > at org.eclipse.swt.widgets.Display.error(Display.java:978)
> > at org.eclipse.swt.widgets.Display.checkDevice(Display.java:638 )
> > at org.eclipse.swt.widgets.Display.getSystemFont(Display.java:2 038)
> > at org.eclipse.swt.widgets.Display.internal_new_GC(Display.java :2174)
> > at org.eclipse.swt.graphics.GC.<init>(GC.java:132)
> > at org.eclipse.swt.graphics.GC.<init>(GC.java:99)
> > at
> >
org.eclipse.birt.chart.device.swt.SwtTextMetrics.<init>(SwtTextMetrics.java:
> > 67)
> > at
> >
org.eclipse.birt.chart.device.swt.SwtDisplayServer.getTextMe trics(SwtDisplay
> > Server.java:256)
> > at
org.eclipse.birt.chart.computation.Methods.computeBox(Method s.java:764)
> > at
> >
org.eclipse.birt.chart.model.layout.impl.LabelBlockImpl.getP referredSize(Lab
> > elBlockImpl.java:263)
> > at
> >
org.eclipse.birt.chart.internal.layout.LayoutManager.doLayou t_tmp(LayoutMana
> > ger.java:65)
> > at
> >
org.eclipse.birt.chart.internal.layout.LayoutManager.doLayou t(LayoutManager.
> > java:597)
> > at org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:962)
> > at
> >
com.sciant.cpgi.ccp.ui.internal.GenerateChartStateJob.run(Ge nerateChartState
> > Job.java:47)
> > at org.eclipse.core.internal.jobs.Worker.run(Worker.java:58)
> >
> >
> >
> > "Ivo Bosticky" <ivo@catapult.com> wrote in message
> > news:e9p3dp$nhv$1@utils.eclipse.org...
> >> David,
> >>
> >> I pasted the stack trace below.
> >>
> >> Just so that you know, we're using BIRT Chart Engine 2.0.1 in
standalone
> >> mode - so not latest release.
> >>
> >> If it would make it easier, I can give you my BIRT Chart Canvas code to
> > have
> >> a look at (it should be easy for me to convert it into a stand-alone
> >> application).
> >>
> >> Thanks
> >> Ivo
> >>
> >> Jul 20, 2006 5:04:29 PM org.eclipse.birt.chart.exception.ChartException
> >> logThis
> >> SEVERE: Exception
> >> org.eclipse.birt.chart.exception.ChartException: $NO-RB$ Invalid thread
> >> access
> >> at
> > org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:867)
> >> at
> >>
> >
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.completeU
> > pdate(BirtChartCanvas.java:143)
> >> at
> >>
> >
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.run(BirtC
> > hartCanvas.java:99)
> >> at java.lang.Thread.run(Thread.java:595)
> >> Caused by: org.eclipse.swt.SWTException: Invalid thread access
> >> at org.eclipse.swt.SWT.error(SWT.java:2942)
> >> at org.eclipse.swt.SWT.error(SWT.java:2865)
> >> at org.eclipse.swt.SWT.error(SWT.java:2836)
> >> at org.eclipse.swt.widgets.Display.error(Display.java:995)
> >> at
org.eclipse.swt.widgets.Display.checkDevice(Display.java:626 )
> >> at org.eclipse.swt.widgets.Display.getDPI(Display.java:1403)
> >> at
> >>
> >
org.eclipse.birt.chart.device.swt.SwtDisplayServer.getDpiRes olution(SwtDispl
> > ayServer.java:196)
> >> at
> >>
> >
org.eclipse.birt.chart.computation.withaxes.PlotWith2DAxes.<init >(PlotWith2D
> > Axes.java:89)
> >> at
> > org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:860)
> >> ... 3 more
> >> org.eclipse.birt.chart.exception.ChartException: $NO-RB$ Invalid thread
> >> access
> >> at
> > org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:867)
> >> at
> >>
> >
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.completeU
> > pdate(BirtChartCanvas.java:143)
> >> at
> >>
> >
com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$U pdater.run(BirtC
> > hartCanvas.java:99)
> >> at java.lang.Thread.run(Thread.java:595)
> >> Caused by: org.eclipse.swt.SWTException: Invalid thread access
> >> at org.eclipse.swt.SWT.error(SWT.java:2942)
> >> at org.eclipse.swt.SWT.error(SWT.java:2865)
> >> at org.eclipse.swt.SWT.error(SWT.java:2836)
> >> at org.eclipse.swt.widgets.Display.error(Display.java:995)
> >> at
org.eclipse.swt.widgets.Display.checkDevice(Display.java:626 )
> >> at org.eclipse.swt.widgets.Display.getDPI(Display.java:1403)
> >> at
> >>
> >
org.eclipse.birt.chart.device.swt.SwtDisplayServer.getDpiRes olution(SwtDispl
> > ayServer.java:196)
> >> at
> >>
> >
org.eclipse.birt.chart.computation.withaxes.PlotWith2DAxes.<init >(PlotWith2D
> > Axes.java:89)
> >> at
> > org.eclipse.birt.chart.factory.Generator.build(Generator.jav a:860)
> >> ... 3 more
> >>
> >> David Michonneau wrote:
> >>
> >> > Hi Ivo,
> >> >
> >> > Can you post the stack trace? How do you start the non-UI thread?
> >> >
> >> > Thanks,
> >> >
> >> > David
> >> >
> >> > "Ivo Bosticky" <ivo@catapult.com> wrote in message
> >> > news:e9n9sv$lml$1@utils.eclipse.org...
> >> >> Hi David,
> >> >>
> >> >> I was able to render our chart into an Image, and then draw this
image
> > on
> >> >> the SWT Canvas each time it requires a repaint. With this approach
> >> >> most
> >> >> of the repaint delays have been eliminated, and our problem has been
> >> >> solved.
> >> >>
> >> >> I still can't get around the initial repaint delay, because I don't
> > seem
> >> >> to
> >> >> be able to get the Chart to build and render in a non-SWT thread. I
> >> >> get
> >> >> an Invalid Access Exception while calling
> >> >> IDeviceRenderer.getDisplayServer().getDpiResolution() while scaling.
> >> >> If
> >> >> the
> >> >> scaling is taken out then I get the same exception from
> >> >> Generator.build(...)
> >> >>
> >> >> Thank you for your help.
> >> >> Ivo
> >> >>
> >> >>
> >> >>
> >> >>
> >> >> David Michonneau wrote:
> >> >>
> >> >>> Hi Ivo,
> >> >>>
> >> >>> First, you can try using Generator.refresh() and not
> > Generator.build(),
> >> >>> if
> >> >>> the only thing that changes is the data. Typically what takes time
is
> >> >>> not the generation, but the rendering itself. To make it faster,
you
> >> >>> want to minimize the rendering time for each datapoint, which
> >> >>> implies:
> >> >>>
> >> >>> - a simple datapoint marker graphic with a plain color
> >> >>> - No datapoint label (use a tooltip instead)
> >> >>> - No X Axis label (or skip every 1000 labels if you want to show 10
> >> >>> labels
> >> >>> in a 10000 points chart for instance).
> >> >>>
> >> >>> You can try upgrading to 2.1 too, the performance should be better.
> >> >>>
> >> >>> As for making the UI responsive, it's indeed a good idea to
generate
> > the
> >> >>> chart into an image in a separate thread then update the UI with
the
> > new
> >> >>> image. It's like double-buffering and will also avoid any
flickering,
> >> >>> you can look in the SWT chart examples of the examples plugin to
see
> > how
> >> >>> to do
> >> >>> it.
> >> >>>
> >> >>> Thanks,
> >> >>>
> >> >>> David
> >> >>>
> >> >>> "Ivo Bosticky" <ivo@catapult.com> wrote in message
> >> >>> news:e9kmnn$spc$1@utils.eclipse.org...
> >> >>>> Hi,
> >> >>>>
> >> >>>> We're rendering Charts onto a SWT Canvas in our RCP application.
> >> >>>> Performance is sufficient for simple charts, but when the number
> >> >>>> of data-points increases, screen repaints slow down. At 10000 data
> >> >>>> points, it takes several seconds each time the Canvas needs to
> >> >>>> be repainted. Since the SWT thread is doing the generation
> >> >>>> and rendering, the GUI appears unresponsive, and this is turning
> >> >>>> into a usability issue. It's possible that the number of data
> >> >>>> points will increase to the 100,000 level in the future, at
> >> >>>> which stage the rendering will take too long for users to
> >> >>>> tolerate without a progress dialog. I had a look at the Chart FAQ
> >> >>>> and also into IDeviceRenderer, and I suspect it should be possible
> >> >>>> to generate the chart once into an Image, and then use this cached
> >> >>>> Image to update the SWT Canvas, but I'm not sure exactly how to
> >> >>>> go about doing this. Does someone out there have experience with
> >> >>>> this
> >> >>>> issue and how to go about resolving it?
> >> >>>>
> >> >>>> Thanks,
> >> >>>> Ivo
> >> >>>>
> >> >>>>
> >> >>
> >>
> >
> >
>
>
|
|
|
Goto Forum:
Current Time: Wed Nov 05 11:33:28 EST 2025
Powered by FUDForum. Page generated in 0.06169 seconds
|