Continuous Droste Floating Point Exception (Bug?)

I posted this as an issue on the GitHub repo but not sure how often that gets looked at: Continuous Droste Floating Point Exception + Sometimes Causes BSOD · Issue #47 · GreycLab/gmic · GitHub (I can only embed 4 images here but I have more examples on the GitHub issue page)


I’ve been messing around with the continuous droste effect and have been having repeated issues, both with the GUI for the photoshop plugin and the CLI.

Here is an example of a command I’d run, where the droste component of the command comes from copying directly from the Photoshop plugin GUI after resetting to defaults parameters:
gmic -input 1000.png -souphead_droste10 40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0 -output output-1000.png

Extremely often, but not quite consistently, it fails like this, for no apparent reason, it just stops:

The verbose output seems to suggest it stops at a particular fill command. I’ll paste a debug output at the end:

What’s strange is that at one point, using the same command, it started working if the size of the image was smaller enough (roughly 1000x1000 or less), but now it doesn’t again. I’ve also tried this in a virtual machine and it does the same thing. And it also does it with both version 3.3.5 and 3.3.6, and with both update335.gmic and update336.gmic, which I tested by specifying the exact filter file version.

The same thing appears to happen in the photoshop version though, and this one at least gives some kind of error message. In this case, the error occurs immediately when clicking on the Continuous Droste effect, or if the effect was already selected (and therefore it tries to open that immediately), the error occurs immediately after trying to open the plugin:
325695595-bb09dc85-dad1-497e-96f1-055bb73a82ed

The error 0xc000008f means “Floating-point inexact result”. (Technically it could also mean “The system cannot join or substitute a drive to or for a directory on the same drive” but I think the other meaning is more likely).

The reason I noticed image size might be a contributing factor (but not the only factor) is that for a while it worked in the photoshop GUI when I was trying to do the effect on a very large image (6000x6000) and the effect preview window was able to show the effect being done. But if I tried to hit “apply”, it would crash, and if I expanded the GUI preview window too large (as to expand the preview image size) it would also crash. Therefore I tried downsizing the image and then it worked in the CLI where it wouldn’t before with the large version. But again, now for some reason it won’t even work with those same exact smaller images.

Windows Event Viewer log shows that it is indeed the same crash whether in the GUI or CLI, as the “Exception Code” is the same one that is shown in the window of the GUI when it crashes.

What’s more concerning is if I try to run the command via the CLI too many times in succession, it causes a BSOD . And by this I mean, the command fails and gmic.exe exits, and the terminal window shows the C:\whatever> prompt again, and if I do that too many times in a row too quickly, it BSODs. After it happened the first time, I tried the same thing again to see if it was a fluke, but it happened again.The actual stop code for both BSODs was 0000007E aka SYSTEM_THREAD_EXCEPTION_NOT_HANDLED .

More strange behavior - I just tried to replicate the BSOD issue in a virtual machine, and while it didn’t seem to cause it, it demonstrated the weird inconsistency I was talking about. Here you can see the same command ran back-to-back 5 times and failing each time, but randomly working on the 6th time (see that it actually got to the “output image” stage):

I tried compiling the source code in Visual Studio to see if I could run it and debug it, but couldn’t figure out how to do it properly. If it can’t be reproduced perhaps if someone could compile it and include the debug symbols I might be able to figure it out that way. I will probably also try compiling it on Linux, it seems the source code was more for that anyway if I’m not mistaken.


Here’s a full debug example:

C:\Users\Joe\Downloads\gmic_3.3.5_cli_win64>gmic -debug -input 1000.png -souphead_droste10 40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0 -output output-1000.png
[gmic]./ Start G'MIC interpreter (v.3.3.5, debug mode).
<gmic>./ Initial command line: 'cli_start , -debug -input 1000.png -souphead_droste10 40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0 -output output-1000.png'.

<gmic>./ Enter scope './'.
<gmic>./ Item[0]: 'cli_start', selection [].
<gmic>./ Found custom command 'cli_start: ' (takes no arguments).
<gmic>./ Expand command line for command 'cli_start' to: ''.
<gmic>./cli_start/ Return from empty command 'cli_start/'.
<gmic>./ Item[2]: '-debug' -> 'debug', selection [].
<gmic>./ Item[3]: '-input' -> 'input', selection [].
<gmic>./ Command 'input': arguments = '1000.png'.
[gmic]./ Input file '1000.png' at position 0 (1 image 1000x1000x1x3).
<gmic>./ Item[5]: '-souphead_droste10' -> 'souphead_droste10', selection [0].
<gmic>./ Found custom command 'souphead_droste10:  _souphead_droste10 ${1-26},0,${28-31}' (takes arguments).
<gmic>./ Command 'souphead_droste10': arguments = '40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0'.
<gmic>./ Found 31 given arguments for command 'souphead_droste10':
<gmic>./   $1 = '40'
<gmic>./   $2 = '100'
<gmic>./   $3 = '1'
<gmic>./   $4 = '1'
<gmic>./   $5 = '1'
<gmic>./   $6 = '0'
<gmic>./   $7 = '0'
<gmic>./   $8 = '0'
<gmic>./   $9 = '0'
<gmic>./   $10 = '0'
<gmic>./   $11 = '1'
<gmic>./   $12 = '10'
<gmic>./   $13 = '1'
<gmic>./   $14 = '0'
<gmic>./   $15 = '90'
<gmic>./   $16 = '0'
<gmic>./   $17 = '0'
<gmic>./   $18 = '0'
<gmic>./   $19 = '0'
<gmic>./   $20 = '1'
<gmic>./   $21 = '0'
<gmic>./   $22 = '0'
<gmic>./   $23 = '1'
<gmic>./   $24 = '0'
<gmic>./   $25 = '0'
<gmic>./   $26 = '0'
<gmic>./   $27 = '0'
<gmic>./   $28 = '0'
<gmic>./   $29 = '1'
<gmic>./   $30 = '0'
<gmic>./   $31 = '0'
<gmic>./ Expand command line for command 'souphead_droste10' to: ' _souphead_droste10 40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0'.
<gmic>./ Decompose command line into 2 items:
<gmic>./   item[0] = '_souphead_droste10'
<gmic>./   item[1] = '40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0'

<gmic>./souphead_droste10/ Enter scope 'souphead_droste10/'.
<gmic>./souphead_droste10/ Item[0]: '_souphead_droste10', selection [0].
<gmic>./souphead_droste10/ Found custom command '_souphead_droste10:  repeat $! l[$>] to_a +f. 0 sh. 0 f. "* begin(InnerRadius = $1; OuterRadius = $2; Periodicity = $3; Strands = $4; Zoom = $5; Rot(...),ColorOutA=1); i(#1,x,y,0,1)=ColorOutG*255; i(#1,x,y,0,2)=ColorOutB*255; i(#1,x,y,0,3)=ColorOutA*255; ColorOutR*255" k.. done done' (takes arguments).
<gmic>./souphead_droste10/ Command '_souphead_droste10': arguments = '40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0'.
<gmic>./souphead_droste10/ Found 31 given arguments for command '_souphead_droste10':
<gmic>./souphead_droste10/   $1 = '40'
<gmic>./souphead_droste10/   $2 = '100'
<gmic>./souphead_droste10/   $3 = '1'
<gmic>./souphead_droste10/   $4 = '1'
<gmic>./souphead_droste10/   $5 = '1'
<gmic>./souphead_droste10/   $6 = '0'
<gmic>./souphead_droste10/   $7 = '0'
<gmic>./souphead_droste10/   $8 = '0'
<gmic>./souphead_droste10/   $9 = '0'
<gmic>./souphead_droste10/   $10 = '0'
<gmic>./souphead_droste10/   $11 = '1'
<gmic>./souphead_droste10/   $12 = '10'
<gmic>./souphead_droste10/   $13 = '1'
<gmic>./souphead_droste10/   $14 = '0'
<gmic>./souphead_droste10/   $15 = '90'
<gmic>./souphead_droste10/   $16 = '0'
<gmic>./souphead_droste10/   $17 = '0'
<gmic>./souphead_droste10/   $18 = '0'
<gmic>./souphead_droste10/   $19 = '0'
<gmic>./souphead_droste10/   $20 = '1'
<gmic>./souphead_droste10/   $21 = '0'
<gmic>./souphead_droste10/   $22 = '0'
<gmic>./souphead_droste10/   $23 = '1'
<gmic>./souphead_droste10/   $24 = '0'
<gmic>./souphead_droste10/   $25 = '0'
<gmic>./souphead_droste10/   $26 = '0'
<gmic>./souphead_droste10/   $27 = '0'
<gmic>./souphead_droste10/   $28 = '0'
<gmic>./souphead_droste10/   $29 = '1'
<gmic>./souphead_droste10/   $30 = '0'
<gmic>./souphead_droste10/   $31 = '0'
<gmic>./souphead_droste10/ Expand command line for command '_souphead_droste10' to: ' repeat $! l[$>] to_a +f. 0 sh. 0 f. "* begin(InnerRadius = 40; OuterRadius = 100; Periodicity = 1; Strands = 1; Zoom = 1; Rotat(...),ColorOutA=1); i(#1,x,y,0,1)=ColorOutG*255; i(#1,x,y,0,2)=ColorOutB*255; i(#1,x,y,0,3)=ColorOutA*255; ColorOutR*255" k.. done done'.
<gmic>./souphead_droste10/ Decompose command line into 13 items:
<gmic>./souphead_droste10/   item[0] = 'repeat'
<gmic>./souphead_droste10/   item[1] = '$!'
<gmic>./souphead_droste10/   item[2] = 'l[$>]'
<gmic>./souphead_droste10/   item[3] = 'to_a'
<gmic>./souphead_droste10/   item[4] = '+f.'
<gmic>./souphead_droste10/   item[5] = '0'
<gmic>./souphead_droste10/   item[6] = 'sh.'
<gmic>./souphead_droste10/   item[7] = '0'
<gmic>./souphead_droste10/   item[8] = 'f.'
<gmic>./souphead_droste10/   item[9] = '* begin(InnerRadius = 40; OuterRadius = 100; Periodicity = 1; Strands = 1; Zoom = 1; Rotate = 0; XShift = 0; YShift = 0; XCenterShift = 0; YCenterShift = 0; StartingLevel = 1; NumberOfLevels = 10; LevelFrequency = 1; ShowBothPoles = 0; PoleRotation = 90; PoleLong = 0; PoleLat = 0; TilePoles = 0; HyperDroste = 0; FractalPoints = 1; AutoSetPeriodicity = 0; NoTransparency = 0; ExternalTransparency = 1; MirrorEffect = 0; Untwist = 0; DoNotFlattenTransparency = 0; ShowGrid = 0; ShowFrame = 0; Antialias = 1; XEdgeType = 0; YEdgeType = 0; r1 = InnerRadius/100; r2 = OuterRadius/100; p1 = Periodicity; p2 = Strands; xCenterShift = XCenterShift/100; yCenterShift = YCenterShift/100; W = (w-1)/2; H = (h-1)/2; xShift = (XShift*w/W)/100; yShift = (YShift*h/H)/100; tileBasedOnTransparency=!(NoTransparency); transparentPointsIn=!(ExternalTransparency); levelsToLookOut=StartingLevel; levelToShow=LevelFrequency; retwist=!(Untwist); if (AutoSetPeriodicity\,p1=p2/2*(1+sqrt(1-(log(r2/r1)/pi)^2))); if (p1>0\,rotat(...)
<gmic>./souphead_droste10/   item[10] = 'k..'
<gmic>./souphead_droste10/   item[11] = 'done'
<gmic>./souphead_droste10/   item[12] = 'done'

<gmic>./souphead_droste10/_souphead_droste10/ Enter scope '_souphead_droste10/'.
<gmic>./souphead_droste10/_souphead_droste10/ Item[0]: 'repeat', selection [0].
<gmic>./souphead_droste10/_souphead_droste10/ Command 'repeat': arguments = '$!' -> '1'.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/ Start 'repeat...done' block (1 iteration).
<gmic>./souphead_droste10/_souphead_droste10/*repeat/ Item[2]: 'l[$>]' -> 'l[0]', selection [0].
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/ Start 'local...done' block, with image [0].

<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/ Enter scope '*local/'.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/ Item[3]: 'to_a', selection [0].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/ Found custom command 'to_a:  e[^-1] "Force image$? to have an alpha channel." foreach { if !s||s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)." elif s==1||s==3 channels 0,{s} sh. {s-1} f. 255 rm. fi }' (takes no arguments).
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/ Expand command line for command 'to_a' to: ' e[^-1] "Force image$? to have an alpha channel." foreach { if !s||s>4 error[0--5] "Command 'to_a': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)." elif s==1||s==3 channels 0,{s} sh. {s-1} f. 255 rm. fi }'.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/ Decompose command line into 19 items:
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[0] = 'e[^-1]'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[1] = 'Force image$? to have an alpha channel.'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[2] = 'foreach'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[3] = '{'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[4] = 'if'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[5] = '!s||s>4'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[6] = 'error[0--5]'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[7] = 'Command 'to_a': Image [$>] is not a G\,GA\,RGB or RGBA image ({s} channels).'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[8] = 'elif'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[9] = 's==1||s==3'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[10] = 'channels'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[11] = '0,{s}'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[12] = 'sh.'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[13] = '{s-1}'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[14] = 'f.'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[15] = '255'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[16] = 'rm.'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[17] = 'fi'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/   item[18] = '}'

<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/ Enter scope 'to_a/'.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/ Item[0]: 'e[^-1]', selections [0,1,2,3,4].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/ Command 'echo': arguments = 'Force image$? to have an alpha channel.' -> 'Force image [0] to have an alpha channel.'.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/ Force image [0] to have an alpha channel.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/ Item[2]: 'foreach', selection [0].
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/ Start 'foreach...done' block, with image [0].

<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/ Enter scope '*foreach/'.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/ Item[4]: 'if', selection [0].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/ Command 'if': arguments = '!s||s>4'.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Start 'if...endif' block -> condition '!s||s>4' does not hold.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Item[8]: 'elif', selection [0].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Command 'elif': arguments = 's==1||s==3'.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Reach 'elif' block -> condition 's==1||s==3' holds.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Item[10]: 'channels', selection [0].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Found custom command 'channels: skip ${2=$1} e[^-1] "Keep channels $1...$2 of image$?." z 0,0,0,$1,100%,100%,100%,$2' (takes arguments).
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Command 'channels': arguments = '0,{s}' -> '0,3'.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Found 2 given arguments for command 'channels':
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/   $1 = '0'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/   $2 = '3'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Expand command line for command 'channels' to: 'skip 3 e[^-1] "Keep channels 0...3 of image$?." z 0,0,0,0,100%,100%,100%,3'.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Decompose command line into 6 items:
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/   item[0] = 'skip'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/   item[1] = '3'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/   item[2] = 'e[^-1]'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/   item[3] = 'Keep channels 0...3 of image$?.'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/   item[4] = 'z'
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/   item[5] = '0,0,0,0,100%,100%,100%,3'

<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/channels/ Enter scope 'channels/'.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/channels/ Item[0]: 'skip', selection [0].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/channels/ Command 'skip': arguments = '3'.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/channels/ Skip argument '3'.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/channels/ Item[2]: 'e[^-1]', selections [0,1,2,(...),5,6,7].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/channels/ Command 'echo': arguments = 'Keep channels 0...3 of image$?.' -> 'Keep channels 0...3 of image [0].'.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Keep channels 0...3 of image [0].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/channels/ Item[4]: 'z', selection [0].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/channels/ Command 'crop': arguments = '0,0,0,0,100%,100%,100%,3'.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/channels/ Crop image [0] with coordinates (0,0,0,0) - (100%,100%,100%,3) and dirichlet boundary conditions.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/channels/ Exit scope 'channels/'.

<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Item[12]: 'sh.', selection [0].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Command 'shared': arguments = '{s-1}' -> '3'.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Insert shared buffer from channel 3 of image [0].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Item[14]: 'f.', selection [1].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Command 'fill': arguments = '255'.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Fill image [1] with 255.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Item[16]: 'rm.', selection [1].
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Remove image [1] (1 image left).
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ Item[17]: 'fi', selection [0].
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/*if/ End 'if...endif' block.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/ Item[18]: '}', selection [0].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/*foreach/ Exit scope '*foreach/'.

<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/to_a/ Exit scope 'to_a/'.

<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/ Item[4]: '+f.' -> 'f.', selection [0].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/ Command 'fill': arguments = '0'.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/ Fill image [0] with 0.
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/ Item[6]: 'sh.', selection [1].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/ Command 'shared': arguments = '0'.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/ Insert shared buffer from channel 0 of image [1].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/ Item[8]: 'f.', selection [2].
<gmic>./souphead_droste10/_souphead_droste10/*repeat/*local/ Command 'fill': arguments = '* begin(InnerRadius = 40; OuterRadius = 100; Periodicity = 1; Strands = 1; Zoom = 1; Rotate = 0; XShift = 0; YShift = 0; XCenterShift = 0; YCenterShift = 0; StartingLevel = 1; NumberOfLevels = 10; LevelFrequency = 1; ShowBothPoles = 0; PoleRotation = 90; PoleLong = 0; PoleLat = 0; TilePoles = 0; HyperDroste = 0; FractalPoints = 1; AutoSetPeriodicity = 0; NoTransparency = 0; ExternalTransparency = 1; MirrorEffect = 0; Untwist = 0; DoNotFlattenTransparency = 0; ShowGrid = 0; ShowFrame = 0; Antialias = 1; XEdgeType = 0; YEdgeType = 0; r1 = InnerRadius/100; r2 = OuterRadius/100; p1 = Periodicity; p2 = Strands; xCenterShift = XCenterShift/100; yCenterShift = YCenterShift/100; W = (w-1)/2; H = (h-1)/2; xShift = (XShift*w/W)/100; yShift = (YShift*h/H)/100; tileBasedOnTransparency=!(NoTransparency); transparentPointsIn=!(ExternalTransparency); levelsToLookOut=StartingLevel; levelToShow=LevelFrequency; retwist=!(Untwist); if (AutoSetPeriodicity\,p1=p2/2*(1+sqrt(1-(log(r2/r1)/pi)^2)))(...)
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/ Fill image [2] with expression '* begin(InnerRadius = 40; OuterRadius (...)x,y,0,3)=ColorOutA*255; ColorOutR*255'.
1 Like

Hello @ThioJoe , yes I’ve seen the github issue as well. Thanks for the very detailled explanation.
I’ve been very busy this week, so sorry for not responding right away.

I would be interested to know if this problem also occurs with G’MIC development version 3.3.6, which is available here : Index of /files/prerelease

Would it be possible to do exactly the same kind of tests with this version, and tell us is the issue still appears ? I’d like to be sure this is a bug that has not been already fixed.

Thanks!

No worries it’s not exactly critical. But yea I actually was trying it with both 3.3.5 and 3.3.6 and it seems to be the same. Also just now I was able to compile Gmic 3.3.6 from source on an Ubuntu VM (22.04) and it seems to even happen there too, and conveniently mentions a floating point exception, so I guess the issue is cross-platform. Here’s the output on Ubuntu.

joe@joe-ubuntu-vm:~/Repos/gmic/src$ ./gmic -verbose 3 -input 1000.png -souphead_droste10 40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0 -output output-1000.png
[gmic]./ Start G'MIC interpreter (v.3.3.6).
[gmic]./ Set verbosity level to 3.
[gmic]./ Input file '1000.png' at position 0 (1 image 1000x1000x1x3).
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/ Force image [0] to have an alpha channel.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/ Fill image [0] with 0.
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/ Insert shared buffer from channel 0 of image [1].
[gmic]./souphead_droste10/_souphead_droste10/*repeat/*local/ Fill image [2] with expression '* begin(InnerRadius = 40; OuterRadius (...)x,y,0,3)=ColorOutA*255; ColorOutR*255'.Floating point exception (core dumped)

Hi,

i’ve just tried your pipeline on an image on my Linux laptop and it worked fine, 50 times in a row.
Image used (1000x1000):

Hmm I don’t seem to be having an issue with that png either. I wonder if the file size has anything to do with it.

I see that one is a paletted png so it’s pretty small filesize-wise. I opened it in photoshop and saved it as a TrueColor png and now it fails to work maybe 1/10 times for me:

Here’s one that is a bit bigger at 1500x1500 and 1.3MB and consistently does the issue for me:

1 Like

I’m thinking this could be related to inf/-inf.

I added this to rep_henon_phase_diagram:

 if(xn==inf||yn==inf||xn==-inf||yn==-inf,break());

To stop the filter from causing Bluescreen of Death.

The same probably applies here.

Unfortunately, I’m not able to make it crash here (Ubuntu 22.10, freshly compiled G’MIC 3.3.6), even with this image.
Could it be related to different flags used for the compilation ?
@ThioJoe , how did you compile G’MIC 3.3.6 ? Using the provided Makefile, or using cmake?

EDIT: What would be interesting is to compile gmic with the sanitizers activated, so:

$ cd gmic/src
$ make debug

and makes it crash to see what happens.

Another interesting test to do is running with valgrind:

$ cd gmic/src
$ make valgrind
$ ./gmic ...

Interested in anything that can help. I had planed to release G’MIC 3.3.6 next week, but this bug is a stopper for me.

Will try this image once I can get my hand on my computer.

Just did 50 runs with the smiley png and never got a crash.

THen did 50 runs with this photo and didn’t crash either:


Then took one of the output pngs (16mb) and did 50 runs again without problem.

All this with:

  gmic: GREYC's Magic for Image Computing: Command-line interface
        Version 3.3.6 (pre-release #24041510)

Dropped a comment at GitHub. Suspect a shared image misuse.

Excellent bug report. I’m snooping around now…

Nothing so gauche as the donor image of a share being removed whilst child images are still extant, but a design based on shared still…

As observed, a fill command pel processor implements the whole of the droste algorithm (thanks be to Jos Leys for furnishing really nice documentation). Authored by Martin Souphead, Continuous Droste has been around since 1.6.9.

Martin’s game is to:

  1. Load an image ([0]) This is the provided reference image.
  2. Use to_a to endow the reference image with an alpha channel, in case it does not have one.
  3. Create a new image ([1]) with the image geometry of the reference image (with alpha channel, so each pixel of the reference and and new image is a vector4. The new image starts life with all pixels [0,0,0,0]).
  4. Share out channel 0 (red) of the new image to make the third image in the stack ([2]). This third image has the structural appearance of a single channel grayscale, but that channel is shared with its predecessor’s ([1]) channel 0.
  5. Harness fill to house a mathematical expression that nominally iterates over the pels of the share ([2]), but, in practice, also writes to the G, B and A channels of the donor image [1]. So, in effect, Martin is processing [RGBA] vector4’s in image [1], while iterating over the shared-out R channel constituting image [2].
  6. When the fill iterator finishes, the algorithm has fully executed. Martin keeps [1] implicitly deleting [0,2].

Since he is keeping the donor, and deleting the share and reference, there is no obvious share violation. I find the filter’s design ingenious but — in this day and age, the share operator is unnecessary. in the world of modern G’MIC (3.x) we can iterate over images a vector at a time.

It seems to me, in fact, that the fastest way to fix the bug is to get share out of the design; iterate over image [1] with a full pixel processor. Won’t take much re-writing at all. I think if Martin would stop by now, eight years after his original design, he would do that as well. He certainly exhibited sufficient cleverness back in the day, and with the expanded iteration capabilities of G’MIC 3.x, he would opt for the cleaner design.

This puts to one side the still-undiagnosed issue with share. I really do think it is an issue with share — specifically, G’MIC is earning itself a kill signal on @ThioJoe 's Windows platform. The abrupt way of G’MIC’s departure is a hallmark of kills arising from access violations, and, by its nature, share is well-suited to set up such calamities. Certainly, not by design, but by some as-yet unnoticed share bug with an esoteric trip-wire. In this particular instance, what’s the wire? I dunno, but I mistrust share. It is a tricky command. I’ll bet my pocket change that if it was written out of the filter, the bug would go away. @David_Tschumperle may react with dismay at that — there is something lurking in share still, making G’MIC less solid than it could be. My scheme walks around the issue instead of addressing it head on, but it seems the expedient approach for a near-term release. The root of the issue, given the infrequency of its occurrence (few people seem to experience it) suggests that it will be hard to find. Feels to me like gdb sessions on the C\C++ side of the fence.

Well — I could use some practice in that, I suppose…

EDIT:

That’s also interesting. As noted, the algorithm is almost entirely implemented in a math expression. Possibly not share at all, but a math expression parser oopsie…

Could be both. You could take a check at rep_henon_phase_diagram and remove this line and see what happens:

 if(xn==inf||yn==inf||xn==-inf||yn==-inf,break());

Probably similar issue to OP. If you do this, you’ll get BSOD.

I’m also interested by this. Could you tell me what command I can try here to reproduce the bug ?

I’ve also tried to reproduce the bug, on my machine.
But I can’t, no matter how hard I try.
What I’ve done so far :

  • make valgrind, then valgrind ./gmic sp colorful,4096 v 3 -souphead_droste10 40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0 q.
  • make debug then ./gmic sp colorful,4096 v 3 -souphead_droste10 40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0 q.
  • Compile with cmake rather than the custom Makefile.

Everything works! I’m on Ubuntu 22.04.04 LTS with g++ 11.4.0.

Remove the

if(xn==inf||yn==inf||xn==-inf||yn==-inf,break());

within rep_henon_phase_diagram, then do this:

$ 1024,512 rep_henon_phase_diagram 2,1

Repeat at least 12 times, I think. Eventually, your computer will BSOD.

Hm very odd, I wonder if the behavior could be affected by hardware/CPU. Especially since I can reproduce the bug even within both Windows and Ubuntu virtual machines on this computer :thinking:. I have an i9 13900KS which has a bunch of cores with the big/little architecture which is relatively new. Heck it could even be a RAM issue, I’ve seen truly bizarre manifestations of bad RAM in my time.

Maybe try it with an exceptionally large image? This one is 6000x6000 and 28 MB:

Also I was using both the pre-compiled binaries and ones I compiled myself just doing “make cli”, but also was trying “make debug” which did the same. Though apparently mingw debug symbols don’t work with visual studio so I was having trouble debugging thus far. I’ve compiled with ‘make valgrind’ now on Ubuntu and am going to try messing around with that.

1 Like

Nope, everything works also well for me with your procedure.

I’ve just tried with your image, and it works also for me. No bug, even after a dozen runs.

Thanks, as I’m also on Ubuntu, I’d be very interested to be able to reproduce the bug on the same OS, as it would definitely ease investigating for a fix.

I’ve run valgrind a couple different ways, with a very small image (around 16 KB) and that big one at 28 MB. In both cases gmic does get all the way through and outputs an image even with the big one, so I wonder if it’s some kind of race condition, because running with valgrind takes a lot longer. (The 28MB one took AGES, didn’t keep track but probably like 30+ minutes)

Here’s the output for the small image with --leak-check=full --track-origins=yes:
Valgrind Small Image Output.txt (4.2 KB)

And for the large image also with --leak-check=full --track-origins=yes:
Valgrind Big Image Output.txt (4.9 KB)

And here is the small image but with --leak-check=full --show-leak-kinds=all --track-origins=yes (Very long):
Valgrind Small Image Full Output.txt (135.3 KB)

I admit I don’t really know what I’m looking at with valgrind, but I wonder if it’s notable that the summaries are mostly the same regardless of using an extremely small or extremely large image.

2 Likes