|
Post by troycheek on Jun 24, 2015 3:47:50 GMT 1
Does anybody know of any GFA BASIC 32 code for saving images in any common file format? I occasionally like to throw together a program that creates "art" and of course would like to share it with people. The best I've been able to do so far is a hack of an uncompressed TGA 24-bit format that usually creates valid files. That's the simplest file header and structure I've found. Nothing uses TGA anymore so I have to use ffmpeg or similar to convert it to PNG or some other format.
The Get command does a great job of getting graphics data from a window into a string. I don't think it was meant for copying entire windows, especially 1920x1080, but it works. The Get data appears to be plain raw color data, 32 bits per pixel, with a 24-byte header. I've figured out the height and width and number of bits per pixel. There are a couple of other numbers in there, but most of the header is 00. Since the Get data is 32 bits per pixel, I'd love to create a TGA file that's 32 bits per pixel just to make it easy, but all the 32 bit TGA files I've created so far only load in one piece of software I have. I think I've got the alpha channels wrong in the header.
Anyway, here's my code for creating TGA files. Forgive the plethora of print statements. I was arranging things the same way as an example I found online. To use this, open a window, create something interesting, then call the following:
Get 0, 0, _X - 1, _Y - 1, p$ Open "picture.tga" for Output As # 2 Print # 2; Chr$(0); Print # 2; Chr$(0); Print # 2; Chr$(2); Print # 2; Chr$(0); : Print # 2; Chr$(0); Print # 2; Chr$(0); : Print # 2; Chr$(0); Print # 2; Chr$(0); Print # 2; Chr$(0); : Print # 2; Chr$(0); Print # 2; Chr$(0); : Print # 2; Chr$(0); Print # 2; Mid$(p$, 5, 2); Print # 2; Mid$(p$, 9, 2); Print # 2; Chr$(24); Print # 2; Chr$(32); t$ = Mid$(p$, 25) a% = 0 While a% < Len(t$) BPut # 2, V:t$ + a%, 3 a% = a% + 4 Wend Close # 2
To create a 32 bit version, change the (24) towards the bottom to (32). Drop a% and the While/Wend loop. Change BPut to BPut # 2, V:t$, Len(t$)
That'll create a file that loads into Sony Vegas Pro 11 but nothing else I've tried. It either errors out or shows a blank image. Any suggestions would be appreciated.
Edit: I've been searching and downloading converter programs for a while now. Apparently, pretty much all online converters and all utilities/viewers are based on ffmpeg (or whatever library ffmpeg uses), and it turns out that ffmpeg has a problem with 32 bit TGA files. I finally found a freeware program (TGA Viewer) that can at least load 32 bit TGA and save as PNG. I mean, I'm not really using the other 8 bits or anything. It's just so elegant to grab the screen with Get, twiddle a few bits to make a new header, and write the whole thing to disk as a TGA file. I guess I'll have to continue outputting 3 out of 4 bytes. The convenience of being able to feed the file into ffmepg for conversion is just too, well, convenient.
But I still want to hear what other options I have for saving image files with GFA BASIC 32!
|
|
|
Post by troycheek on Jun 28, 2015 16:40:18 GMT 1
As we say in the South, if it'd been a snake, it would have bit me. There is apparently a SavePicture command. Duh. I swear I scoured the help file time after time looking for a command like that and never saw it. I was looking through some example programs and found mention of LoadPicture, looked it up in the help file, found SavePicture, had a fit, did some testing. "SavePicture Win_1.Image filename$" will save a 1280x720x24 uncompressed (2.6 MB) bitmap file to RAMDisk in 10-11 ms. The only save format is bitmap. LoadPicture will also load GIF and JPG files, but they're converted to BMP in the process.
|
|
|
Post by dragonjim on Jun 28, 2015 23:07:06 GMT 1
Hi, Sorry I missed your earlier post and glad you found SavePicture in the help file. You are correct that it is only possible to use SavePicture to save files in the Windows bitmap format. However, there is a way of saving an image as a .jpg by using the FreeImage DLL (download from here); below is a very crude example of how to use the FreeImage functions to do just that: Declare LIB "FreeImage.dll" Declare Function FI_Initialise Lib "FreeImage.dll" Alias "_FreeImage_Initialise@4" (UseLocalPlugIns As Long) As Long Declare Function FI_GetFIF Lib "FreeImage.dll" Alias "_FreeImage_GetFIFFromFilename@4" (ByVal Filename$) As Int Declare Function FI_Load Lib "Freeimage.dll" Alias "_FreeImage_Load@12" (ByVal fif%, ByVal filename$, ByVal flags%) As Int Declare Function FI_Save Lib "Freeimage.dll" Alias "_FreeImage_Save@16" (ByVal fif%, ByVal dib%, ByVal filename$, ByVal flags%) As Bool
FI_Initialise(False) Enum FIF_BMP = 0, FIF_JPEG = 2 Local h As Handle, pic As Picture Color 255 PBox 100, 100, 200, 200 Get 50, 50, 150, 150, h Set pic = CreatePicture(h, False) SavePicture pic, App.Path & "\pic.bmp" Trace FI_GetFIF(App.Path & "\pic.bmp") h = FI_Load(FIF_BMP, App.Path & "\pic.bmp", 0) FI_Save(FIF_JPEG, h, App.Path & "\pic.jpg", $80)The FreeImage DLL needs to be in the same directory as a saved version of the above example. Another good example can be found here on Peter Heinzig's excellent website which hosts numerous examples of image manipulation by using GFA Basic 32; he also has a more detailed FreeImage example which is worth looking at. I hope you find these useful.
|
|
|
Post by troycheek on Jun 29, 2015 10:32:26 GMT 1
Thank you! I have actually been to Peter's website several times, but mein Deutsch ist nicht so gut. Judging by the color of the link, I'd downloaded the FreeImage demo before, but apparently it didn't register with me at the time what it was good for. I probably tried to run it from inside the ZIP file and it failed because it couldn't find the DLL. FreeImage is definitely something I'll be using in the future. Thanks to your example, I have successfully created JPEG and PNG versions of my BMP files using FreeImage. GIF files are apparently more complicated, what with their color palettes and all, but I think it's just a matter of declaring a few more functions and following the methods used in the demo. The images I'm creating have large blocks of solid colors, so PNG is the way to go. Using FreeImage, I can save a screenshot in PNG format (fastest compression) in 50 ms compared to 10 ms for saving an uncompressed BMP. The reason I'm harping on how long the saves take is that I'm recording snapshots as I'm generating graphics to later create videos or animated GIFs. I'm aiming for 25 fps, which is one image every 40 ms. I don't have to capture them in real time, since I can always just pause generation for however long it a snapshot takes, but I can get a much better feel for the finished product if the snapshot process doesn't take too long. Unfortunately, with fast BMP saving I can only save 20 seconds to memory or about a minute to a 4 GB RAM disk. Here's an example of what I've been doing: youtu.be/X35FA-6WNGEI'm experimenting with a monitoring program that watches the folder where I'm saving BMP files and automatically converting them to another format and then deleting the original file. Primitive multitasking. The conversion takes longer than the snapshots, so the monitoring program quickly falls behind, but it does extend my recording time. (All this because saving to the mechanical hard drive where I have terabytes of free space "feels" slow and uneven, I don't want to burn out my solid state drive by creating and deleting thousands of files every time I generate a video of pretty pictures, and I'm limited to 4 GB on my RAM disk because that's the size I used when I set it up and I can't remember how to change it. I think the proper phrase is "solving the wrong problem.")
|
|
|
Post by dragonjim on Jun 29, 2015 12:39:36 GMT 1
Your work is certainly interesting and I like your idea of doing the time consuming file conversion using a background function.
I assume you are using GB32's LINE command to create your art which is quite versatile. Have you tried tapping into the anti-aliasing properties of the GDI+ library? Once again, Peter Heinzig has an excellent example (and a full GDI+ library converted for GB32) here.
A word of caution though: if you decide to use GDI+ for line drawing, restrict it's use to anything that requires anti-aliasing or any other functions that the standard LINE command does not have as it is markedly slower to render than the LINE commands (which are based on the older GDI library). Also, try and use the integer functions (those ending in a capital 'I') if you are not using floating point values as they are quicker.
And another word of caution: the GDI+ library uses ARGB colour values (to allow Alpha transparency) rather than the more standard RGB values; these are not just RGB values with the Alpha value tagged on the front as the R and B values are also switched meaning that an RGB value of $000000FF prints red, while an ARGB value of $FF0000FF (the first FF is the alpha value necessary to make the colour visible) prints blue. [Sorry if you already know this]. Below are two simple functions which convert between the two:
Function RGBtoARGB(r, Optional a = $FF) Return MakeL4H(a, GetRValue(r), GetGValue(r), GetBValue(r)) EndFunction
Function ARGBtoRGB(col, Optional trans?) Local Int32 a, b, g, r If Not trans? // Convert a fully opaque ARGB colour (quicker) Return RGB(GetByte1(col), GetByte2(col), GetByte3(col)) Else // Convert a partially transparent ARGB colour (slower) a = GetByte0(col) : r = $FF - (Xor($FF, GetByte1(col)) * (a / $FF)) g = $FF - (Xor($FF, GetByte2(col)) * (a / $FF)) : b = $FF - (Xor($FF, GetByte3(col)) * (a / $FF)) Return RGB(r, g, b) EndIf EndFunction
|
|
|
Post by troycheek on Jun 30, 2015 14:11:05 GMT 1
Using FreeImage to convert frames as they were generated (create image, grab screen, save as BMP, load BMP with FreeImage, Save PNG) worked fine the first 650 or so times, then produced an out of memory error at the grab screen part. I have to restart the IDE each time to continue. I suspect some memory is getting reserved somewhere and not getting released. I haven't taken the time to investigate properly yet. I was distracted by trying to track down why FreeImage stopped working at all, then realized that I was trying to use the 64 bit version with 32 bit GB32. I've yet to update my background image converter to use FreeImage, but that's my next project. I suspect it will work better than my current method of using the System command to run FFmpeg. I kind of want to do it all with one program, though. I've suggested to Sjouke Hamstra that he write a blog entry about threads or fibers or whatever GB32 uses for multitasking or parallel processing or whatever it's officially called and explain how to implement it in a way the average programmer can understand it. He wrote back implying that nobody really understands it. I actually wasn't using LINE so much as PLOT, though I'd considered rewriting it to use LINE to speed things up. The program uses a depth first search algorithm to implement a slow and clumsy fill routine. 16 competing parallel fill routines using different creates some interesting effects when they collide. It's a distracting offshoot of my maze generator program youtu.be/oi1P8mNHSHQI've bookmarked your GDI+ information for future use.
|
|
|
Post by dragonjim on Jun 30, 2015 19:38:19 GMT 1
FreeImage - Out of Memory
Once you have loaded, converted and saved an image using FreeImage, do you use FreeImage_Unload to remove the image from memory? The function is included in Peter Heinzig's example, but here is the declaration for it anyway:
Declare Sub FI_Unload Lib "Freeimage.dll" Alias "_FreeImage_Unload@4" (ByVal dib%)
If that doesn't sort it, try the GB32 command FreeBmp(bitmap.hWnd).
Multithreading
Multithreading is very fiddly and temperamental in GB32 but it is possible. Below is a quick example of how to do it:
OpenW 1 : Win_1.AutoRedraw Try Global Int32 n = 0, threadHandle, threadID : Global Large n1 threadHandle = CreateThread(0, 0, ProcAddr(ThreadTwo), 0, 0, V:threadID) ThreadOne ~TerminateThread(threadID, 0) Catch ~TerminateThread(threadID, 0) Message Err$ EndCatch CloseW 1
Procedure ThreadOne For n = 1 To 10 : Print AT(1, 1); "Thread one output: "; n , "Thread two output:"; n1 : Delay 1 : DoEvents : Next n EndProcedure
Procedure ThreadTwo While threadID <> 0 And n < 11 : Inc n1 : Wend EndProcedureLet me know how you get on with this...
|
|
|
Post by troycheek on Jul 2, 2015 13:15:30 GMT 1
Thanks for the info. I did indeed miss the Unload function, which was what I needed, though I did think to try FreeBmp. Peter only declared about 30 functions, so naturally I missed a few. I was going mostly by your example and some information I gleaned from the FreeImage header files (selecting level of png compression, for example). By adding the unload, my test program was able to convert several runs of 5,000 bmp files to png in about 45 ms each without reporting any memory errors. That's off a RAM disk, BTW. As an aside, I tried to solve my bmp problem by enabling NTFS compression for my TEST folder full of bmp files. I was surprised to find that said compression works on RAM disks. I got good compression of my huge uncompressed bmp images full of big blocks of color, but it took a while. And I could occasionally access a file while it was in the process of compression and get errors. Now the big problems for my background bmp to png conversion program are that I use a very inefficient way of looking for new files to convert, so that process pretty much maxes out a single core and would probably saturate the I/O channels of a physical disk drive, and the fact that it's now fast enough (thanks to you, Peter, and FreeImage) that it can convert files faster than I am creating them, so the converter eventually catches up and tries to convert a file that hasn't been completely written yet. There's a couple of sample programs I need to look at that show some way of monitoring folders with Windows message events being fired when new files appear. I also need to look into some kind of open file technique that will fail inside of a Try/Catch if the file is still being written. Or maybe monitor the file length over the course of a few ms to see if it's still growing. I also want to see if there's some way to stream images to FFmpeg and let it convert to video on the fly. Hmm, is there a FreeVideo.dll out there somewhere? During some of my tests, for no apparent reason, I sometimes get some physical disk activity according to the indicator LED on my computer, and conversion takes 1-2 whole seconds. It's almost like Windows is paging out some virtual memory to disk, which shouldn't happen. A 32 bit program like GB32 should be limited to only a few GB of memory usage, and I have several GB of RAM free (16 GB total), so there's no reason to swap out memory even if it isn't being actively used, which it is. (I seem to currently have a 30 GB pagefile.sys. Must remember to reboot.) It doesn't happen all the time and I can't duplicate it when I want to, so I'm having trouble tracking it down. I blame Windows. Thanks to your example I finally had an idea of how to implement multithreading. Unfortunately, all the threads seem to run within the same process and compete for time on the same CPU core, so I might not get any speed benefits by generating/saving images with one thread and converting them to png with another. Then again if I can convert them in the background while... Needs more experimentation. And thanks again for all your help.
|
|
|
Post by dragonjim on Jul 2, 2015 16:49:34 GMT 1
Great to hear you are making huge advances. I'm a bit short of time at the moment so this reply will be a bit terse - apologies.
Opening files that you are working on If FreeImage has ownership of a file, it should Lock it. If you are loading the file through BLoad or LoadPicture then the file will not be locked. To keep your converter accessing the file you are working on, try using Open filename for Input Lock Read as #n, even if you have used a different method to load the picture into your programme. This should prevent access from any other process.
Unexpected Disk Activity This could be down to drive caching (which is used even for RAM Disks, as far as I'm aware). You could try flushing the cache using Commit or Flush or the FlushFileBuffers API call; these require either file channel numbers or handles, whichever method you are using.
Searching for updated files This could be a good use for a OCX Timer function for a background application. Create a simple form (hidden if you wish), set up a standard Do : Sleep : Until Me Is Nothing loop and then have an OCX Timer control trigger a file search every 10 seconds or so. While it is not searching, the background process will take up very little CPU time.
Multithreading It was interesting that my example only worked on one core on your system as an expanded version (of four threads) had all four of mine humming away at 90+%. But, as I said before, multithreading with GB32 is neither straightforward or the results predictable. It may be that Windows has reserved your other core(s) for applications running in the background - try exiting a few of these and see if that makes a difference.
Once again, sorry for the short reply. Keep in touch regarding your progress...
|
|
|
Post by troycheek on Jul 2, 2015 19:13:31 GMT 1
Opening files that you are working on - Actually, my problem is the other way around. My conversion routine has FreeImage trying to open files that haven't been completely saved yet. I'm using SavePicture Win_1.Image, filename$ to save screen grabs, which takes about 10 ms and seems to keep the file open that whole time. The conversion program sees that filename$ exists and tries to load it with FreeImage, save it as a png, then delete the original file. If SavePicture is still writing the file, the load/save process silently fails and the routine fails with an error when it tries to delete the original file, or I later find some length 0 png files. I solved the problem by inserting Try Open filename$ for input as #17:close #17 catch sleep 1000 endcatch. If the file is in use, the Open will fail and the routine will sleep for 1 second, allowing that file to finish writing and a few more to pile up. And, yes, I know GB32 has a command to find free file numbers, so I don't need to pick a weird number and hope I haven't accidentally used it somewhere else for a file that's still open.
Unexpected Disk Activity - This might have been my fault with a buggy earlier version of some program I was working on, because once I noticed the problem and started looking for it, it went away. Of course, I think it's ridiculous that with 16 GB of RAM, Windows 7 is deciding to ever cache anything to disk. If I've somehow managed to use that much RAM, please give me an out of memory error so I know I'm doing something wrong.
Searching for updated files - I added a sleep command in the right place in the scan part and the routine behaves much more nicely, at least until I start processes and it grabs all the CPU it can, but that's okay.
Multithreading - I swear that my first couple of tests showed everything running on one core, but when I tried a "real world" application, suddenly I was multitasking! I've got one thread generating and saving graphics, and another converting them with FreeImage. Together, they're using 28% of my total CPU power. (8 cores, 12.5% each, plus a few % for general housekeeping.) The next step would be to separate the generating graphics from saving screenshots. I think I've already gotten that figured out in my head. A possible further step would be to parallelize generating the pretty pictures. That should be possible as I currently use iteration to have 16 competing DSF fill operations, but with 16 threads on 8 cores the OS might spend more time shuffling between tasks than actually doing them. Oh, and I was utterly shocked when the spellchecker didn't flag parallelize. I thought I was making that word up.
And the commands are so simple (CreateThread, TerminateThread) that even I can use them. I'm currently using global variables like status$ to pass messages between threads. My conversion thread sets status$="busy" when it still has files to convert, status$="ready" when waiting for more.
By the way, may I ask where you are getting your information? The help files I have don't have anything about thread commands (now that I know what to look for, I found a single mention in the ReStop section), and the sample files I've found seem to use a totally different method. That's why I never tried this before.
Hmm, I did a search inside the GFA Editor EXE file and found... CreateThread CreateRemoteThread GetCurrentThread GetCurrentThreadId SetThreadAffinityMask SetThreadPriority GetThreadPriority GetThreadTimes ExitThread TerminateThread GetExitCodeThread GetThreadSelectorEntry GetThreadContext SetThreadContext SuspendThread ResumeThread
SetThreadAffinityMask sounds interesting, because the name suggests to me that you could assign threads to particular cores and in theory get a few more % performance out of them (OS doesn't spend time shuffling them around between cores).
|
|
|
Post by dragonjim on Jul 2, 2015 21:35:55 GMT 1
It looks like you've managed to solve most of your problems now - and well done with the word parallelize, that was impressive!
Regarding 'Unexpected Disk Activity': my mistake on that one. I meant 'buffer' rather than 'cache'. However, I'm glad you've got that sorted out now. Other problems with disk access can be caused by antivirus programs and windows indexing...
On to Multithreading: As you have surmised, it is advisable to keep the numbers of threads below - or at the most equal - to the number of cores you have as the moment you exceed that number, you start getting time lags due to memory swapping, etc and so forth. Hence, rather than running 16 fill operation on one thread as you are now, you will probably find it quicker to run 2 fill operations in eight separate threads than 1 in sixteen. More importantly, though, it is best to use local variables as much as possible and, if global ones have to be used - as with your status$ - while all threads can read it, only one should edit it: two threads trying to edit one shared variable at the same time may end in data corruption or GFA may decide to throw a fatal error; the 'GFA Basic has stopped working' report is the most common sight when trying to get multithreading running smoothly so it is always best to click 'Save' before you click 'Run'.
As to the absence of multithreading in the help file (except for reStop which seems to be broken), that is simply because I had not yet got round to adding any, although a simple tutorial similar to the example I sent you is in draft form which I intend to polish, integrate into the Help file and upload over the next week or so. Going back before I took over the help file, the absence could be explained by the fact that there are no dedicated keywords in GB32 - CreateThread() and TerminateThread() are both Windows APIs. As to where I got the information, that mainly comes from years of experimenting.
Finally, to SetThreadAffinityMask(): I have never had cause to use this function as most of my applications are either too linear to merit multithreading or aren't CPU-intensive enough to require fine tuning. However, for your project I can see that it may make a noticeable difference so, if you have any problems getting it working, let me know and I'll have a look at it - two minds are better than one, as they say. If you do get it working and have the time to post a short example to this thread, I'll tag it onto the multithreading help page.
|
|
|
Post by troycheek on Jul 3, 2015 0:21:51 GMT 1
Disk Activity: I'm not scanning the RAM disk with antivirus but I checked and it did still have indexing turned on. I didn't even think of that. I turned it off. We'll see if that happens again. Multithreading: There are occasionally some advantages to multithreading even if the threads outnumber the cores. The OS might do a better job of slicing up processor time than you can manually. Well, than I can. But not if you're going for max speed or full CPU usage. I had guessed that local variables would be best in threads, and the global status variables I'm using are only set in the threads and read in the main program. I saw an example where variables were set in the main program and read in the threads; they where used to tell the threads that it was time to exit, so the threads ended themselves gracefully instead of being terminated. I kind of like that idea from a philosophical standpoint and may start using it. Edit: I just read where Microsoft recommends against using TerminateThread. My glorified fill operations all operate on the same array, as it's their interaction that makes the pretty pictures, so I probably can't subdivide the work into threads without an error. In fact, I need to look back on that routine because I got an array out of bounds error earlier while testing threading. I didn't make any changes to it when adding the multithreading stuff, so whatever code caused the problem has been around for a while. Curses. Anyway, the crash left GB32 in a state where re-running the program would not create the file conversion thread anymore. I rebooted just to be safe. I didn't realize you were the one updating the help file until after I wrote that. Windows API explains why several keywords I'd seen in sample code around the net didn't appear in the help file. It also explains why I had trouble finding info online about multithreading. I'd been ignoring the Windows sites while looking for GB and other niche languages. Still, that short example program you showed me was all I needed. Definitely include that. SetThreadAffinityMask probably has no real use anymore. I remember back when multicore processors were a new thing I'd occasionally have to set affinity to a single core to get good performance out of some games. Windows has gotten a lot better now. To be completely honest, the only reason I was wanting to use it was to write a program that spiked a couple of cores on the usage graph instead of getting evenly distributed, just to watch that happen, then I'd probably go back to letting the OS handle things. Edit: Just playing around after a quick google search, and I already figured it out. ~SetThreadAffinityMask(threadHandle,bitmask) where threadHandle is the handle of the thread reported by CreateThread, and bitmask is 0x00000000 (that's binary, right?) or the like where you set 1 bit for each CPU/core you want to use, so decimal 1 is CPU 1 only, 2 is CPU 2 only, 4 is CPU 3 only, etc. You would think that setting multiple bits, say 3, would utilize CPUs 1 and 2, as was suggested in a random webpage I just checked, but that doesn't seem to be the case, or the effect is too subtle for me to notice. 256, which would have been core 9 on my 8 core machine, didn't seem to do anything, but the program ended mysteriously right after I terminated the thread, so that probably isn't recommended. I really should check return codes instead of just using void and ~ all the time. I'll bet you can use GetCurrentThread to get the handle of the main process and use that to set the affinity... Yes! somehandle = GetCurrentThread() I was able to set my main program to use core 1 and my conversion routine to use core 2 by simply using Global Int32 threadHandle, threadID, mainHandle mainHandle = GetCurrentThread() threadHandle = CreateThread(0, 0, ProcAddr(bmp2png), 0, 0, V:threadID) ~SetThreadAffinityMask(threadHandle, 2) ~SetThreadAffinityMask(mainHandle, 1) Edit: corrected the above. You have to set all the daughter threads before you set the main thread, as the daughter thread can only have a subset of the affinity mask the main thread has at the time it's set. Yes, I did run this several times just to see those cores peg on the CPU usage gadget. I'm going to pause now for a mad scientist laugh. Mwahahahahahaha!
|
|
|
Post by dragonjim on Jul 3, 2015 22:11:05 GMT 1
You seem to be advancing in leaps and bounds at that end. Just one more note of caution with multithreading: I seem to recall sometime back I had errors and crashes galore when I tried used the Print command in more than one thread at a time - after I started using Text instead, they all mysteriously disappeared... I can only assume that, because GFA logs the position at the end of the Print statement to know where to put the next one, throwing conflicting statements at it from different threads was confusing it unnecessarily. There may well be other commands and functions that also affect internal values - I can't remember running into them, but I'm sure there are - so if you run into some odd bugs or errors, keep this fact in mind and try using a different, more flexible command.
As to the question in your reply: 0x00000000 is hexadecimal not binary. Where you may have got confused is that &x00000000 IS binary. If you are ever unsure, check the Numeric Literals page in the help file or simply enter the expression after a Print command in GB32 and check the result.
Good work with the SetThreadAffinityMask() example - can I use that in the help file?
Finally, if you notice any other shortcomings, errors or omissions in the help file, please let me know along with any suggestions you may have as to its improvement. Like many things, it's difficult to know how good a job you're doing without feedback and, sadly, these days the GFA Basic community - with a few noted exceptions - are not good on feedback.
|
|
|
Post by troycheek on Jul 4, 2015 3:19:39 GMT 1
I just assumed that multiple threads printing/drawing/writing to the same window would be bad, so I've been giving each thread it's own window. The main thread of my little project outputs all the graphics to the main window. The bmp to png thread has its own secondary window where it keeps me updated on which file it's currently converting. This also helps a lot with the debug stuff, as each thread has it's own output, and if a window stops updating or closes, I know something has gone horribly wrong in that thread. The errors I'm getting are Array Out of Bounds and Access Violation which I thought were programming errors on my end, but the same code without the multiple threads seems to run okay, so I think it has to do with my use of global variables to pass information between the threads. I assumed that two threads could read the same global variable at the same time okay, and that two writing to the same variable would be Very Bad (tm), but according to a random C++ forum I browsed, just having one thread read a variable while another is writing to it is also very bad. I think I've got a solution to this, but I'm going to start another thread (pun intended) for that discussion because this one has been derailed far enough. Please use the SetThreadAffinityMask example; that's why I mentioned it. I'll probably never use it in a real program and it's best that someone remember it. Just put something in there about how with modern Windows it's better to let the OS distribute the workload and this is included for information only yada yada yada blah blah blah. While I've found several omissions in the help file, I think they all fall under the category of Windows API. I read somewhere that GB32 has about 1000 of those, so you probably don't need to include them all. For very selfish reasons, I would suggest including all the thread-related commands you can because until a few days ago I would have sworn that GB32 couldn't multitask and now I'm very excited about it. At the very least, CreateThread (and in most cases don't create more threads than you have cores/processors, and I need to come up with an easy way to determine number of cores) and TerminateThread (though it's better to let the procedure or function you're running as a thread exit normally or the function return) and SetThreadAffinityMask (since we've already figured that one out). By the way, I think you're doing a good job.
|
|