|
|||
mystran wrote: rw wrote: You can safely support at least OGL 2.0 in this day and age, doing away with fixed function pipeline and the awful texture combiners. All that stuff is emulated by the driver anyway.
Actually turns out some people are still using cards (eg G550) that don't even support texture_env_combine. I'm sure OGL2.0 gives you a fairly wide user base, but I like to keep as much backwards compatibility as reasonably possible. It's not like it matters much for GUI drawing anyway. It depends what you're drawing. I'm working on a multi-channel oscilloscope so I'm offloading as much drawing and DSP to the GPU as I can to free up the CPU for more important tasks. None of this would be possible with the fixed function pipeline and I'm not concerned with isolating the tiny fraction of pre-2000 GPUs that cannot handle such duties. That's not to say that it couldn't be implemented with OpenGL 1.1, rather the CPU and RAM overhead would be far greater than using OpenGL 1.1 features. For garden variety GUI rendering, I agree, OpenGL 1.1 is certainly up to the task. |
|||
| ^ | Joined: 21 Mar 2012 Member: #277342 | ||
|
|||
Yeah ok, I admit shaders can be useful if you can take a raw buffer of audio data and transform it into a nice complex visual representation completely on GPU. If I wanted to do something like that then I'd probably drop fixed function support as well. It's not like I ever touch any state manually anyway (eg. API wrapper implements simple "shaders" that map desired functionality to texture combines and render state). In fact I don't touch the API at all directly; in my GUI lib it took me a day to write OpenGL backend to replace the original D3D backend and I can't really remember needing to change anything outside the actual backend (the two backends can even happily co-exist in the same binary so you could select one or the other at runtime; maybe some day I'll write a software-only fall-back to support the guy that's still using CL5434).
Anyway, for "basic user interfaces" I guess the only thing that could reasonably benefit from shaders would be text rendering; since it pretty much comes down to a quad per glyph (unless you cache static text directly), it's a bit wasteful on bus-bandwidth to push 4 vertices per quad when you could instance on hardware. For on-the-fly (ie not cached RGB like GDI) sub-pixels using fixed function is even more data since the whole sub-pixel crap doesn't map very nicely to texture combiners (or I'm just stupid; that's also possible) where as all of it would be trivial with simple shaders. That's something I might actually do that at some point (as an optional optimization where supported, ofcourse In my "other project" (ie the reason I haven't really released much audio stuff lately, if we ignore the fact that I've been playing too much FPS games) that does a bit more on the GPU than "cheap alpha-blending", I actually don't touch the fixed function at all. Fortunately that doesn't need to run in a plugin so it can happily use D3D (and has performance requirements high enough that you can just ignore any Intel graphics even exist). |
|||
| ^ | Joined: 11 Feb 2006 Member: #97939 Location: Helsinki, Finland | ||
|
|||
I am only okay with C and not very good with C++ yet. I have got a framework up and running already but my lack of knowledge is holding me up. How do I give variables to the editor class from the plugin class? I tried using global scope but I did it wrong lol. Does it involve more inheritance than just one class from AudioEffectX and one from AEffEditor? I am using the VSTGL sdk. ---- ![]() |
|||
| ^ | Joined: 16 Feb 2005 Member: #58183 | ||
|
|||
Testing further, I found that having multiple instances on the screen caused frames to drop between them. The more instances, the more frames dropped. Vertical sync is not the issue. I am guessing everything would have to be done in the same context, and for a VST host that might require IPC? Well I am disappoint because OGL looks amazing for plugins. |
|||
| ^ | Joined: 16 Feb 2005 Member: #58183 | ||
|
|||
camsr wrote: Testing further, I found that having multiple instances on the screen caused frames to drop between them. The more instances, the more frames dropped. Vertical sync is not the issue. I am guessing everything would have to be done in the same context, and for a VST host that might require IPC? Well I am disappoint because OGL looks amazing for plugins.
If you are not creating a separate context and properly setting/restoring it every time you touch OpenGL then you're asking for trouble. All the state and resources (eg textures etc) are tied to particular OpenGL context, so you must always make sure you have the right one active or you'll be stepping on other plugins/hosts/whatever feet. As far as dropping frames, if you try to wait (eg glFinish() or probably on SwapBuffers if you try to use VSync) then no other instance/plugin will run while your stalling the GUI thread (never EVER wait in GUI thread). This obviously also means no other plugin will update while your waiting. If you're not already using it, enable double buffering too (to make sure SwapBuffers is as low overhead as possible). Other than that though, I don't see why there should be any problems. Obviously if your drawing is very CPU/GPU heavy and/or you do it too often, then you can generate more work than your system can handle which will obviously lead to dropped frames. |
|||
| ^ | Joined: 11 Feb 2006 Member: #97939 Location: Helsinki, Finland | ||
|
|||
mystran wrote: camsr wrote: Testing further, I found that having multiple instances on the screen caused frames to drop between them. The more instances, the more frames dropped. Vertical sync is not the issue. I am guessing everything would have to be done in the same context, and for a VST host that might require IPC? Well I am disappoint because OGL looks amazing for plugins.
If you are not creating a separate context and properly setting/restoring it every time you touch OpenGL then you're asking for trouble. All the state and resources (eg textures etc) are tied to particular OpenGL context, so you must always make sure you have the right one active or you'll be stepping on other plugins/hosts/whatever feet. As far as dropping frames, if you try to wait (eg glFinish() or probably on SwapBuffers if you try to use VSync) then no other instance/plugin will run while your stalling the GUI thread (never EVER wait in GUI thread). This obviously also means no other plugin will update while your waiting. If you're not already using it, enable double buffering too (to make sure SwapBuffers is as low overhead as possible). Other than that though, I don't see why there should be any problems. Obviously if your drawing is very CPU/GPU heavy and/or you do it too often, then you can generate more work than your system can handle which will obviously lead to dropped frames. Just fixed it One thing that is troublesome is the windows in my host (FL) when dragged get choppy. Do you think it would help to use some kind of framerate limiting? ---- ![]() |
|||
| ^ | Joined: 16 Feb 2005 Member: #58183 | ||
|
|||
I haven't been able to solve the problem of framerate dividing between additional instances yet. I don't know if the problem is the Timer class in VSTGL or if I am not using the context correctly. Do I need to destroy the rendering context after every update, or just release the device context? void VSTGLEditor::refreshGraphics()
{ dc = GetDC(tempHWnd); wglMakeCurrent(dc, glRenderingContext); draw(); swapBuffers(); //wglDeleteContext(glRenderingContext); ReleaseDC(tempHWnd, dc); } This has the Timer interval (refresh) dividing by the number of instances (functionally). The Timer is a Singleton and as far as I can tell, uses static types to share it. The VSYNC and Antialiasing settings made no difference. ---- ![]() |
|||
| ^ | Joined: 16 Feb 2005 Member: #58183 | ||
|
|||
You obviously need one timer per plugin instance, so singleton won't work.
As far as setting/restoring contexts (remember you also need to do this when you want to do things like load textures or whatever; I'd really recommend a RAII helper unless you never touch OpenGL outside your drawing logic).. void VSTGLEditor::refreshGraphics() { HDC hdc, hOldDC; HGLRC hOldRC; // get previous DC and render context hOldDC = wglGetCurrentDC(); hOldRC = wglGetCurrentContext(); // get DC for the hwnd of the window to draw in hdc = GetDC(hwnd); // set rendering context and DC wglMakeCurrent(hdc, glRC); // draw, swap, whatever else you want in the context draw(); SwapBuffers(); // clear errors to avoid confusing other code // it's fine to call this and ignore errors if you don't // care about them; it's just so it reports success on next call glGetError(); // restore previous context and DC wglMakeCurrent(hOldDC, hOldRC); // release the DC we allocated ReleaseDC(hwnd, hdc); } Note that if you use OWN_DC (which I'd really NOT recommend) then the above might not be correct. Remember to ValidateRect() the window in response to WM_PAINT or you'll immediately get a new WM_PAINT request afterwards. Last edited by mystran on Wed Apr 11, 2012 1:05 pm; edited 1 time in total |
|||
| ^ | Joined: 11 Feb 2006 Member: #97939 Location: Helsinki, Finland | ||
|
|||
mystran wrote: You obviously need one timer per plugin instance, so singleton won't work.
As far as setting/restoring contexts (remember you also need to do this when you want to do things like load textures or whatever; I'd really recommend a RAII helper unless you never touch OpenGL outside your drawing logic).. void VSTGLEditor::refreshGraphics() { HDC hdc, hOldDC; HGLRC hOldRC; // get previous DC and render context hOldDC = wglGetCurrentDC(); hOldRC = wglGetCurrentContext(); // get DC for the hwnd of the window to draw in hdc = GetDC(hwnd); // set rendering context and DC wglMakeCurrent(hdc, glRC); // draw, swap, whatever else you want in the context draw(); SwapBuffers(); // clear errors to avoid confusing other code // it's fine to call this and ignore errors if you don't // care about them; it's just so it reports success on next call glGetError(); // restore previous context and DC wglMakeCurrentContext(hOldDC, hOldRC); // release the DC we allocated ReleaseDC(hwnd, hdc); } Note that if you use OWN_DC (which I'd really NOT recommend) then the above might not be correct. Remember to ValidateRect() the window in response to WM_PAINT or you'll immediately get a new WM_PAINT request afterwards. wglMakeCurrentContext(), is this a depreceated function? I can't even find it in my headers and google doesn't return much, not even from opengl.org. I tried MakeCurrent instead and everything looks correct. Thanks! ValidateRect(tempHWnd, NULL) I added this after ReleaseDC, but I don't know if it's needed? In GLWndProc(), the case of WM_PAINT calls to idle(), and GLWndProc returns DefWindowProc(). I am confused as to why this is needed, but it looks for compatibility. Is it the Timer's fault for the dividing framerate?! It only divides when another instance's editor is open, so I guess it is something to do with Timer. I thought the whole point of the static singleton was to prevent contexts from stepping on each others toes? Or is this unnecessary (are the OpenGL calls buffered?) I've seen quite a few examples of multithreaded programs with multiple GL contexts in different threads (including JUCE), and everything looks in sync and full framerate! I would use JUCE for this, but I cannot afford the license and I want to sell a couple plugins I make. Actually, I am not sure I can close source my VST with VSTGL? The License says this: "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software." This is only the VSTGL portion or the VST itself? ---- ![]() |
|||
| ^ | Joined: 16 Feb 2005 Member: #58183 | ||
|
|||
| ^ | Joined: 16 Feb 2005 Member: #58183 | ||
|
|||
camsr wrote: mystran wrote: You obviously need one timer per plugin instance, so singleton won't work.
As far as setting/restoring contexts (remember you also need to do this when you want to do things like load textures or whatever; I'd really recommend a RAII helper unless you never touch OpenGL outside your drawing logic).. [...] Note that if you use OWN_DC (which I'd really NOT recommend) then the above might not be correct. Remember to ValidateRect() the window in response to WM_PAINT or you'll immediately get a new WM_PAINT request afterwards. wglMakeCurrentContext(), is this a depreceated function? I can't even find it in my headers and google doesn't return much, not even from opengl.org. I tried MakeCurrent instead and everything looks correct. Thanks! No, wglMakeCurrent() ofcourse.. sorry I had a brain malfunction while writing that. Quote: ValidateRect(tempHWnd, NULL) I added this after ReleaseDC, but I don't know if it's needed? When a window (or a region of the same) is "invalid" or "dirty" the system will keep generating WM_PAINT messages until you validate it. BeginPaint() that GDI drawing normally calls does this automatically, but otherwise you'll need to ValidateRect() or something similar. Quote: In GLWndProc(), the case of WM_PAINT calls to idle(), and GLWndProc returns DefWindowProc(). I am confused as to why this is needed, but it looks for compatibility. Urgh. I guess DefWindowProc() will validate the window for you, but where do you draw then? Quote: Is it the Timer's fault for the dividing framerate?! It only divides when another instance's editor is open, so I guess it is something to do with Timer. I thought the whole point of the static singleton was to prevent contexts from stepping on each others toes? Or is this unnecessary (are the OpenGL calls buffered?) You should have one timer per instance, no? Normal way to do this is to setup a WM_TIMER (or similar) for the editor window. OpenGL contexts have nothing to do with timers. The OpenGL context is like a virtual device; you can have (within reason) as many as you like, and they are pretty much completely independent. The point is, any OpenGL state or resources you set/alloc/whatever belongs to a single context, so you can safely play in your own sandbox (context) without confusing other code in other sandboxes. |
|||
| ^ | Joined: 11 Feb 2006 Member: #97939 Location: Helsinki, Finland | ||
|
|||
As far as I can tell the timer DOES create a new timer instance for each editor instance. It is using map.h with keys, but I don't know what that is yet. ---- ![]() |
|||
| ^ | Joined: 16 Feb 2005 Member: #58183 | ||
|
|||
So I finally figured out how to utilize the editor and effect pointers. This stuff is pretty new to me as most of my programming hasn't seen OOP and more inline.
I am trying to decouple my editor's redraw rate from the host's rate. So far, the graphics refresh is timer based, so there is no coupling there. EXCEPT, in the case of utilizing a variable that is updated inside the processReplacing() loop. Is there a way to push variables out of, or pull variables into the editor from processReplacing asynchronously? Or will processReplacing guarantee completion before other functions are called? In case you are wondering, I am attempting to make an awesome peak meter. ---- ![]() |
|||
| ^ | Joined: 16 Feb 2005 Member: #58183 | ||
|
|||
For most purposes you can expect there to be two threads, one for audio, one for GUI. Which functions run in which threads isn't exactly very well documented, but generally you can assume that resume/suspend/processReplacing/Events run in audio thread (so only one is active at any given time) and anything to do with the editor runs in GUI thread.
Then there's stuff like set/getParameter/Label/Display which can pretty much be called in any thread at any time (and should probably be re-entrant too). There's also a lot of gray matter that one would expect (by common sense) to run in one thread or another but you probably shouldn't assume too much (eg hosts might change presets from GUI thread while processReplacing() is running or other similar stuff that you probably should be prepared for). Anyway, basically this means that audio thread does audio processing and GUI thread does GUI interaction and drawing and these two go on completely asynchronously. The audio thread runs at high (usually RT) priority and obviously shouldn't ever have to wait for GUI thread (which usually runs at default priority; high priority threads so never wait for lower priority threads). Waiting for the audio (in GUI thread) is generally fine as long as the critical sections (ie the duration of wait) is sensible (holding a lock for the whole processReplacing() is not a good idea). Now, you can really use either push or pull or a combination of both (which is what I do). The way I do it is basically to keep a local copy of data in audio thread, then at the end of every processReplacing() I attempt to take a lock (but without waiting; eg POSIX calls it trylock() and in Windows you specify zero timeout so it fails if it can't succeed immediately). If the lock succeeds, the data is copied into a shared buffer (if not, we'll just try again next processReplacing() call). In GUI timer then, I take the same lock (except this time it's fine to wait since audio is higher priority) and copy the shared buffer to a GUI local buffer. So there's the "audio local" buffer that's always up-to-date. Then there's the "shared" buffer that's updated regularly, but can sometimes miss an update. Then there's the "GUI local" copy of whatever was in the shared buffer during the last timer interrupt; the last copy is just so the "GUI side" critical section (ie duration the lock is held) is the minimum possible (eg a single memcpy of the buffer) which minimizes the chance of audio thread not being able to get the lock when it wants (one would miss a whole lot more updates if the lock was held for a complete redraw; while missing updates might sound bad it doesn't make much difference and in practice it almost never happens anyway if the critical sections are short). If you don't mind adding a bit of latency to the pipeline you can further optimize it by adding a flag (protected by the above mentioned lock) that the GUI sets to notify that more data is needed, and the audio clears to notify GUI that new data is available. Doesn't matter for a peak meter but could be useful if you're copying more data around. The above isn't the only way (nor do I claim it's the best way) to handle the issue, but it has the nice properties that it (1) is safe and (2) can handle arbitrary blobs of data just fine (well, as long as the amount of data isn't huge; if you needed something like last 10 seconds of audio for a scope/spectrum you might want to do some additional logic to only copy the changes, but the general idea still works). |
|||
| ^ | Joined: 11 Feb 2006 Member: #97939 Location: Helsinki, Finland | ||
|
|||
Thanks for the help mystran. I didn't know VST is already multi threaded. Is it the same on Mac OS X?
My original idea was to use a circular buffer and iterator that loops around the end of an array, with the index being incremented per sample. Is this thread safe? Because the way it works now, the values it needs from processReplacing are only received at the end of the block. I want the editor to receive those values, on its interrupt. I don't understand multi threading, but thanks for any advice. ---- ![]() |
|||
| ^ | Joined: 16 Feb 2005 Member: #58183 |
| KVR Forum Index » DSP and Plug-in Development | All times are GMT - 8 Hours |
|
Printable version |
Disclaimer: All communications made available as part of this forum and any opinions, advice, statements, views or other information expressed in this forum are solely provided by, and the responsibility of, the person posting such communication and not of kvraudio.com (unless kvraudio.com is specifically identified as the author of the communication).
Powered by phpBB © phpBB Group


















