[GRASS5] PNG driver problem

I am using the PNG graphics driver in a C++ application that I am writing. At the click of a button, I can zoom in and out by starting the PNG driver, sending region and display commands to GRASS, and stopping the PNG driver. When I behave like a normal user, it seems to work just fine. However, when I try to break my code by clicking the zoom button about 30-50 times in quick succession, I will eventually get the message that the PNG driver is already running, even though the output to the terminal window has verified that the PNG driver has terminated. Is it possible to get such an error message if "d.mon start=PNG" is called too quickly after "d.mon stop=PNG"?

If it turns out that GRASS cannot accept commands to start the PNG graphics driver so quickly after it's been stopped, I'll have to put something in my code that ensures the user doesn't click zoom too fast.

David

David Piasecki wrote:

I am using the PNG graphics driver in a C++ application that I am
writing. At the click of a button, I can zoom in and out by starting
the PNG driver, sending region and display commands to GRASS, and
stopping the PNG driver. When I behave like a normal user, it seems to
work just fine. However, when I try to break my code by clicking the
zoom button about 30-50 times in quick succession, I will eventually
get the message that the PNG driver is already running, even though the
output to the terminal window has verified that the PNG driver has
terminated. Is it possible to get such an error message if "d.mon
start=PNG" is called too quickly after "d.mon stop=PNG"?

d.mon stop=..." runs mon.stop, which calls R_kill_driver() then exits.
R_kill_driver() sends a GRAPH_CLOSE code to the driver, then calls
R_release_driver(), which close()s the socket then returns.

Nothing in this chain waits for the driver to close its end of the
connection, so the driver could still be running after "d.mon
stop=..." has completed (this is more likely to occur with the PNG
driver, as it has to write out the image before it terminates).

BTW, the latest CVS version of the PNG driver has an option to write
the image file after each command. This eliminates the need to stop
and re-start the driver in order to get the image. It also allows you
to create (binary) PPM images as an alternative to PNG.

--
Glynn Clements <glynn.clements@virgin.net>

Excellent! I'll give that a try! Leaving the PNG driver open would definitely solve this problem. I don't have a need to close it anyway. I just have to write the result to a named pipe.

Thanks,
David

On Aug 23, 2004, at 7:44 PM, Glynn Clements wrote:

David Piasecki wrote:

I am using the PNG graphics driver in a C++ application that I am
writing. At the click of a button, I can zoom in and out by starting
the PNG driver, sending region and display commands to GRASS, and
stopping the PNG driver. When I behave like a normal user, it seems to
work just fine. However, when I try to break my code by clicking the
zoom button about 30-50 times in quick succession, I will eventually
get the message that the PNG driver is already running, even though the
output to the terminal window has verified that the PNG driver has
terminated. Is it possible to get such an error message if "d.mon
start=PNG" is called too quickly after "d.mon stop=PNG"?

d.mon stop=..." runs mon.stop, which calls R_kill_driver() then exits.
R_kill_driver() sends a GRAPH_CLOSE code to the driver, then calls
R_release_driver(), which close()s the socket then returns.

Nothing in this chain waits for the driver to close its end of the
connection, so the driver could still be running after "d.mon
stop=..." has completed (this is more likely to occur with the PNG
driver, as it has to write out the image before it terminates).

BTW, the latest CVS version of the PNG driver has an option to write
the image file after each command. This eliminates the need to stop
and re-start the driver in order to get the image. It also allows you
to create (binary) PPM images as an alternative to PNG.

--
Glynn Clements <glynn.clements@virgin.net>

BTW, the latest CVS version of the PNG driver has an option to write
the image file after each command. This eliminates the need to stop
and re-start the driver in order to get the image. It also allows you
to create (binary) PPM images as an alternative to PNG.

What's the advantage of PPM? Less CPU overhead, but less throughput than
TIFF? (so the best solution for display apps where storage space isn't
an issue but CPU is)

Will Tcl output to PNG? It would be nice for the NVIZ image dump menu to
include that so we don't have to run pnmtopng every time.. JPEG?

Winding further off topic, does dumping a max. res PPM work for anyone
on MacOSX from NVIZ? not me, I just get the bits.

Hamish

Hamish wrote:

> BTW, the latest CVS version of the PNG driver has an option to write
> the image file after each command. This eliminates the need to stop
> and re-start the driver in order to get the image. It also allows you
> to create (binary) PPM images as an alternative to PNG.

What's the advantage of PPM? Less CPU overhead, but less throughput than
TIFF? (so the best solution for display apps where storage space isn't
an issue but CPU is)

PPM's main advantage is simplicity. Adding the code to write a PPM
image was a couple of minutes work. Writing a reader is almost as
trivial. One consequence of this is that it's widely supported.

Will Tcl output to PNG? It would be nice for the NVIZ image dump menu to
include that so we don't have to run pnmtopng every time.. JPEG?

My photo(n) manpage (from 8.0) says that Tk supports reading and
writing PPM and GIF images, although it's possible that other formats
have been added in later versions.

However, that isn't relevant to NVIZ. NVIZ supports PPM and TIFF
because the OGSF library includes functions to write those formats
(gsd_img_ppm.c and gsd_img_tif.c); it doesn't use Tk's image support.

Winding further off topic, does dumping a max. res PPM work for anyone
on MacOSX from NVIZ? not me, I just get the bits.

Can you elaborate?

--
Glynn Clements <glynn.clements@virgin.net>

> does dumping a max. res PPM work for anyone on MacOSX from NVIZ? not
> me, I just get the bits.

Can you elaborate?

The NVIZ image dump for a max resolution PPM creates the file by doing
it one section at a time. After rendering all of the panels it then
stiches them togther. Well when I try this on a Mac with Lorenzo's 5.7
binaries, it broke before it finished and all the panels were dumped
onto the desktop individually. (and the temporarily saved state is
restored) I don't have the error or computer in front of me to give an
exact error. Hoping someone else could get it. Random guess was that the
OSX version was missing the stitching fn.

Just trying this on Linux (debian/tcl8.3), it works, but the results are
*much* darker than what was on screen. (Happens in 5.3 and 5.7)

These spit out while it is generating:

Creating PBuffer Using GLX 1.3
Create PixMap Using GLX 1.1
Final Assembled Image will be 2048 x 1770
Writing Tile 1 of 4
Writing Tile 2 of 4
Writing Tile 3 of 4
Writing Tile 4 of 4
Assembling Tiles
Destroy Pixmap and GLXPixmap

[file is written ok, the elevation is very dark.. (beyond fixing in GIMP)]

Then, next time I change a knob and redraw the NVIZ display, it breaks:

X Error of failed request: GLXBadContextTag
  Major opcode of failed request: 144 (GLX)
  Minor opcode of failed request: 1 (X_GLXRender)
  Serial number of failed request: 3400
  Current serial number in output stream: 3401
child process exited abnormally
    while executing
"exec
/usr/local/src/grass/grass53/dist.i686-pc-linux-gnu/etc/nviz2.2/NVWISH2
.2 -f
/usr/local/src/grass/grass53/dist.i686-pc-linux-gnu/etc/nviz2.2/scri..."
    ("eval" body line 1)
    invoked from within
"eval exec $env(GISBASE)/etc/nviz2.2/NVWISH2.2 -f
$env(GISBASE)/etc/nviz2.2/scripts/nviz2.2_script $argv -name NVIZ

&@stdout"

    invoked from within
"if {$argv == ""} {
#no arguments
eval exec $env(GISBASE)/etc/nviz2.2/NVWISH2.2 -f
$env(GISBASE)/etc/nviz2.2/scripts/nviz2.2_script -name NVIZ >&@stdo..."
    (file "/usr/local/src/grass/grass53/dist.i686-pc-linux-gnu/bin/nviz"
line 16)

also saw these when loading/saving state:

Diagnostic: invalid command name "Nviz_animation_save" -- Save
procedure for panel animation may not be defined
Diagnostic: invalid command name "Nviz_kanimator_save" -- Save
procedure for panel kanimator may not be defined
Diagnostic: invalid command name "Nviz_query_save" -- Save procedure
for panel query may not be defined
Diagnostic: invalid command name "Nviz_mkdspf_save" -- Save procedure
for panel mkdspf may not be defined
Diagnostic: invalid command name "Nviz_sdiff_save" -- Save procedure
for panel sdiff may not be defined
Diagnostic: invalid command name "Nviz_label_save" -- Save procedure
for panel label may not be defined
Diagnostic: invalid command name "Nviz_scale_save" -- Save procedure
for panel scale may not be defined
Diagnostic: invalid command name "Nviz_pos_save" -- Save procedure for
panel pos may not be defined

Hamish

Hamish wrote:

> > does dumping a max. res PPM work for anyone on MacOSX from NVIZ? not
> > me, I just get the bits.
>
> Can you elaborate?

Then, next time I change a knob and redraw the NVIZ display, it breaks:

X Error of failed request: GLXBadContextTag
  Major opcode of failed request: 144 (GLX)
  Minor opcode of failed request: 1 (X_GLXRender)

I get exactly the same thing, but right away.

I'm pretty sure I know what's causing it.

Create_OS_Ctx() stores the current drawable an GLX context, and
Destroy_OS_Ctx() restores them. The problem is that the code in
do_zoom.c opens another connection to the X display itself. From the
perspective of the X server, do_zoom.c is a separate client to the
rest of NVIZ. And from the perspective of Xlib, the Display used by
do_zoom.c is a different display to the one which is used by the rest
of NVIZ.

IOW, do_zoom.c shouldn't be trying to do anything with NVIZ' X
resources, because they belong to a different client and a different
display.

If do_zoom.c wants to create its own X connection (even to a different
X server), its own GLX context, and its own X/GLX resources, that's
fine, and it should all work.

If I change the glXMakeCurrent() call which attempts to restore the
old context to simply release the existing context, then it doesn't
crash. However, subsequently moving the viewpoint etc doesn't result
in the display being redrawn (because there isn't a current context).
But if you resize the view window, everything then works again
(presumably, Togl explicitly restores the context as part of the
resize handling).

AFAICT, the correct solution is to force Togl to restore the context
before each redraw, and for do_zoom.c to not to access X resources
(window, GLX context) which belong to a different client and a
different display.

Try the attached patch.

--
Glynn Clements <glynn.clements@virgin.net>

(attachments)

diff.txt (2.07 KB)

This has not worked for me on Mac OSX since an update to NVIZ several months
back. I have the same problems you described earlier today. It **used** to
work earlier this year.

Michael

On 8/23/04 8:44 PM, "Hamish" <hamish_nospam@yahoo.com> wrote:

Winding further off topic, does dumping a max. res PPM work for anyone
on MacOSX from NVIZ? not me, I just get the bits.

Hamish

____________________
C. Michael Barton, Professor
School of Human Diversity and Social Change
PO Box 872402
Arizona State University
Tempe, AZ 85287-2402
USA

Phone: 480-965-6262
Fax: 480-965-7671
www: <www.public.asu.edu/~cmbarton>

[NVIZ max res. PPM image dump causing crash]

> Then, next time I change a knob and redraw the NVIZ display, it
> breaks:
>
> X Error of failed request: GLXBadContextTag
> Major opcode of failed request: 144 (GLX)
> Minor opcode of failed request: 1 (X_GLXRender)

I get exactly the same thing, but right away.

I'm pretty sure I know what's causing it.

Create_OS_Ctx() stores the current drawable an GLX context, and
Destroy_OS_Ctx() restores them. The problem is that the code in
do_zoom.c opens another connection to the X display itself. From the
perspective of the X server, do_zoom.c is a separate client to the
rest of NVIZ. And from the perspective of Xlib, the Display used by
do_zoom.c is a different display to the one which is used by the rest
of NVIZ.

IOW, do_zoom.c shouldn't be trying to do anything with NVIZ' X
resources, because they belong to a different client and a different
display.

If do_zoom.c wants to create its own X connection (even to a different
X server), its own GLX context, and its own X/GLX resources, that's
fine, and it should all work.

If I change the glXMakeCurrent() call which attempts to restore the
old context to simply release the existing context, then it doesn't
crash. However, subsequently moving the viewpoint etc doesn't result
in the display being redrawn (because there isn't a current context).
But if you resize the view window, everything then works again
(presumably, Togl explicitly restores the context as part of the
resize handling).

AFAICT, the correct solution is to force Togl to restore the context
before each redraw, and for do_zoom.c to not to access X resources
(window, GLX context) which belong to a different client and a
different display.

Try the attached patch.

seems to work fine, no need to resize the NVIZ view window.

The resulting image is still very dark.

Hamish

Hamish wrote:

seems to work fine, no need to resize the NVIZ view window.

Right; I figured out a fix for that at the last moment (the added
".top.canvas makecurrent" after Nstart_zoom in nviz2.2_script).

The resulting image is still very dark.

That appears to be a lighting issue. The default lighting results in a
large proportion of the image being saturated (i.e. what you would
expect for a scene with multiple full-intensity lights), while the
zoomed image never quite saturates (i.e. what you would expect for a
scene with a single full-intensity light).

--
Glynn Clements <glynn.clements@virgin.net>

I looked around last night and couldn't seem to find the version of the PNG driver you mentioned. I downloaded the latest 5.3 CVS snapshot, and the documentation for the PNG driver did not mention any option for writing to a file other than calling d.mon stop=PNG. Is the documentation incorrect, or am I looking in the wrong place? Should I be looking for the latest CVS snapshot of 5.7? I tried, but I wasn't able to find it, though I'm still somewhat new to CVS. It often feels like a guessing game when trying to figure out what's available.

Thanks,
David

On Aug 23, 2004, at 7:44 PM, Glynn Clements wrote:

BTW, the latest CVS version of the PNG driver has an option to write
the image file after each command. This eliminates the need to stop
and re-start the driver in order to get the image. It also allows you
to create (binary) PPM images as an alternative to PNG.

David Piasecki wrote:

I looked around last night and couldn't seem to find the version of the
PNG driver you mentioned. I downloaded the latest 5.3 CVS snapshot, and
the documentation for the PNG driver did not mention any option for
writing to a file other than calling d.mon stop=PNG. Is the
documentation incorrect, or am I looking in the wrong place? Should I
be looking for the latest CVS snapshot of 5.7? I tried, but I wasn't
able to find it, though I'm still somewhat new to CVS. It often feels
like a guessing game when trying to figure out what's available.

The change was made within the last week (2004/08/19 21:04:08 for the
5.3 source code; the manual page and 5.7 were updated on the 20th). If
you obtained the code directly from CVS, it should be present. If you
downloaded a CVS snapshot tar file, it may or may not be present,
depending upon the date of the snapshot; the 2004_08_21 snapshots
(both 5.3 and 5.7) should include it.

The file "pngdriver" manual page (generated from the file
html/html/pngdriver.html) should say:

   Environment variables
       Several environment variables effect the operation of the
       PNG driver.
[snip]
                     GRASS_PNG_AUTO_WRITE=[TRUE|FALSE]

                     if set to "TRUE", the image file will be
                     written after each operation (i.e. whenever
                     a client disconnects), rather than only
                     being written out when the driver termi­
                     nates.

--
Glynn Clements <glynn.clements@virgin.net>

Oh, I see now. It's not a separate command. It's simply an environment variable. Sounds like it's being written every time you perform an operation like d.rast. Isn't that a little slow? In some cases, I can see it as beneficial. Now you can load layers and watch them appear one at a time. However, now you have the overhead of writing out a PNG file with every operation. Do you know if there will be a future command for writing the PNG file so we can call it when we please?

As a temporary work around, what do you think of a GRASS script which executes a sequence of commands before exiting? Would the PNG driver consider that as a single operation and write once, or will it write everything automatically?

David

On Aug 25, 2004, at 1:12 PM, Glynn Clements wrote:

David Piasecki wrote:

I looked around last night and couldn't seem to find the version of the
PNG driver you mentioned. I downloaded the latest 5.3 CVS snapshot, and
the documentation for the PNG driver did not mention any option for
writing to a file other than calling d.mon stop=PNG. Is the
documentation incorrect, or am I looking in the wrong place? Should I
be looking for the latest CVS snapshot of 5.7? I tried, but I wasn't
able to find it, though I'm still somewhat new to CVS. It often feels
like a guessing game when trying to figure out what's available.

The change was made within the last week (2004/08/19 21:04:08 for the
5.3 source code; the manual page and 5.7 were updated on the 20th). If
you obtained the code directly from CVS, it should be present. If you
downloaded a CVS snapshot tar file, it may or may not be present,
depending upon the date of the snapshot; the 2004_08_21 snapshots
(both 5.3 and 5.7) should include it.

The file "pngdriver" manual page (generated from the file
html/html/pngdriver.html) should say:

   Environment variables
       Several environment variables effect the operation of the
       PNG driver.
[snip]
                     GRASS_PNG_AUTO_WRITE=[TRUE|FALSE]

                     if set to "TRUE", the image file will be
                     written after each operation (i.e. whenever
                     a client disconnects), rather than only
                     being written out when the driver termi­
                     nates.

--
Glynn Clements <glynn.clements@virgin.net>

Or perhaps the quickest solution is to simply change the environment variable to TRUE just before a display command. However, I see that Client.c simply has a write_image() call on ClientClose() if the AUTO_WRITE environment variable had been set. Is it that simple to create a new binary, something like d.write_png? It could simply call write_image().

David

On Aug 25, 2004, at 1:12 PM, Glynn Clements wrote:

David Piasecki wrote:

I looked around last night and couldn't seem to find the version of the
PNG driver you mentioned. I downloaded the latest 5.3 CVS snapshot, and
the documentation for the PNG driver did not mention any option for
writing to a file other than calling d.mon stop=PNG. Is the
documentation incorrect, or am I looking in the wrong place? Should I
be looking for the latest CVS snapshot of 5.7? I tried, but I wasn't
able to find it, though I'm still somewhat new to CVS. It often feels
like a guessing game when trying to figure out what's available.

The change was made within the last week (2004/08/19 21:04:08 for the
5.3 source code; the manual page and 5.7 were updated on the 20th). If
you obtained the code directly from CVS, it should be present. If you
downloaded a CVS snapshot tar file, it may or may not be present,
depending upon the date of the snapshot; the 2004_08_21 snapshots
(both 5.3 and 5.7) should include it.

The file "pngdriver" manual page (generated from the file
html/html/pngdriver.html) should say:

   Environment variables
       Several environment variables effect the operation of the
       PNG driver.
[snip]
                     GRASS_PNG_AUTO_WRITE=[TRUE|FALSE]

                     if set to "TRUE", the image file will be
                     written after each operation (i.e. whenever
                     a client disconnects), rather than only
                     being written out when the driver termi­
                     nates.

--
Glynn Clements <glynn.clements@virgin.net>

David Piasecki wrote:

Oh, I see now. It's not a separate command. It's simply an environment
variable.

Yes.

Sounds like it's being written every time you perform an
operation like d.rast. Isn't that a little slow?

The extra time seems to be trivial compared to the rest of the
process.

In some cases, I can
see it as beneficial. Now you can load layers and watch them appear one
at a time. However, now you have the overhead of writing out a PNG file
with every operation.

That version of the PNG driver can also write PPM files (by setting
GRASS_PNGFILE to a name which ends in ".ppm"). Also, you can set
GRASS_PNG_COMPRESSION to 0 to disable PNG compression (this may be
significant on systems with a slow CPU, e.g. handhelds, although the
difference seems to be negligible on a PC).

Do you know if there will be a future command for
writing the PNG file so we can call it when we please?

That would involve extending the driver protocol, which I don't
particularly want to do without a compelling reason.

I wouldn't consider this to be a compelling reason unless there's
empirical evidence to show that writing the PNG/PPM file takes a
significant amount of time relative to the other parts of the process.

As a temporary work around, what do you think of a GRASS script which
executes a sequence of commands before exiting? Would the PNG driver
consider that as a single operation and write once, or will it write
everything automatically?

The file is written when a client (d.* program) disconnects. The PNG
driver doesn't know anything about the bigger picture. Any support for
"batch processing" would have to be based upon heuristics, e.g.
delaying the write until no client has connected for a given time
period, or only writing after every N operations.

--
Glynn Clements <glynn.clements@virgin.net>

David Piasecki wrote:

Or perhaps the quickest solution is to simply change the environment
variable to TRUE just before a display command.

That won't work. One process can't change the environment of another.
Whatever values those variables have at the point that the PNG driver
is started (i.e. "d.mon start=PNG") will persist for the lifetime of
the driver process.

However, I see that
Client.c simply has a write_image() call on ClientClose() if the
AUTO_WRITE environment variable had been set. Is it that simple to
create a new binary, something like d.write_png? It could simply call
write_image().

That won't work either. Each monitor runs as a separate process.
Clients (d.* programs) interface with the monitors using the raster
library, which connects to the driver process via the Unix-domain
socket in /tmp/grass-<username>.

--
Glynn Clements <glynn.clements@virgin.net>

Glynn Clements wrote:

> The resulting image is still very dark.

That appears to be a lighting issue. The default lighting results in a
large proportion of the image being saturated (i.e. what you would
expect for a scene with multiple full-intensity lights), while the
zoomed image never quite saturates (i.e. what you would expect for a
scene with a single full-intensity light).

Yep. GS_lights_on() had an off-by-one error, resulting in it failing
to enable the last light.

Fixed in CVS.

--
Glynn Clements <glynn.clements@virgin.net>

> Do you know if there will be a future command for
> writing the PNG file so we can call it when we please?

That would involve extending the driver protocol, which I don't
particularly want to do without a compelling reason.

I wouldn't consider this to be a compelling reason unless there's
empirical evidence to show that writing the PNG/PPM file takes a
significant amount of time relative to the other parts of the process.

> As a temporary work around, what do you think of a GRASS script
> which executes a sequence of commands before exiting? Would the PNG
> driver consider that as a single operation and write once, or will
> it write everything automatically?

The file is written when a client (d.* program) disconnects.

what about using the Unix 'sync' command in the script when
GRASS_PNG_AUTO_WRITE=TRUE

?

Hamish

[max res. PPM image dump from NVIZ]

+: Output now looks great. Image saved ok.

-: After the image dump, it would render the hi-res polygon ok, but the
low res movement 'Grid' has disappeared, then after a few seconds of
playing with that:

Final Assembled Image will be 2048 x 1995
Writing Tile 1 of 4
Writing Tile 2 of 4
Writing Tile 3 of 4
Writing Tile 4 of 4
Assembling Tiles
Destroy Pixmap and GLXPixmap
global-exag = 3.000000
recalculating normals...
100
200
300
global-exag = 3.000000
recalculating normals...
100
200
X Error of failed request: GLXBadContextTag
  Major opcode of failed request: 144 (GLX)
  Minor opcode of failed request: 1 (X_GLXRender)
  Serial number of failed request: 3736
  Current serial number in output stream: 3737
child process exited abnormally
    while executing
"exec
/usr/local/src/grass/grass57/dist.i686-pc-linux-gnu/etc/nviz2.2/NVWISH2
.2 -f
/usr/local/src/grass/grass57/dist.i686-pc-linux-gnu/etc/nviz2.2/scri..."
    ("eval" body line 1)
    invoked from within
"eval exec $env(GISBASE)/etc/nviz2.2/NVWISH2.2 -f
$env(GISBASE)/etc/nviz2.2/scripts/nviz2.2_script $argv -name NVIZ

&@stdout"

?
Hamish

Hamish wrote:

[max res. PPM image dump from NVIZ]

+: Output now looks great. Image saved ok.

-: After the image dump, it would render the hi-res polygon ok, but the
low res movement 'Grid' has disappeared, then after a few seconds of
playing with that:

Final Assembled Image will be 2048 x 1995
Writing Tile 1 of 4
Writing Tile 2 of 4
Writing Tile 3 of 4
Writing Tile 4 of 4
Assembling Tiles
Destroy Pixmap and GLXPixmap
global-exag = 3.000000
recalculating normals...
100
200
300
global-exag = 3.000000
recalculating normals...
100
200
X Error of failed request: GLXBadContextTag
  Major opcode of failed request: 144 (GLX)
  Minor opcode of failed request: 1 (X_GLXRender)
  Serial number of failed request: 3736
  Current serial number in output stream: 3737

I can't reproduce this with 5.3, and I don't have an up-to-date 5.7
built right now. Can you see if it happens for you with 5.3?

Ultimately, it needs to be borne in mind that do_zoom.c is a hack.
None of Togl, the OGSF library nor NVIZ itself were designed with this
kind of usage in mind. Consequently, do_zoom.c's attempts to reproduce
the context's state are bound to be a matter of trial and error.

--
Glynn Clements <glynn.clements@virgin.net>