Your engine and your game must be super flexible to allow the possibility of drawing two times with no state change.
A usual game loop ties very closely all updates in a sequential fashion.
One thread, one loop, one point to flush events, one point to execute processing updates (entity/component processors, or simply manager's updates), and finally one render call which triggers the whole engine 1-frame render.
Whole engine 1 frame render can be made in two fashions, extrospective (intrusive), by accessing (with visitors) the state of the drwable objects of the game, and create the rendering states, passes and commands that go with it.
Or it could be fully retained, and the state of the engine was set BY the processors during the logic update, and it holds sufficient information per se to draw a frame in isolation.
This is a classical game loop. It uses a
delta_time variable that is given to all managers during update.
If you have a physical manager, usually there is another loop within the Update itself, that will use a fixed
dt and iterate as many time to fill the lagging-time to reach
delta_time. And store the remainder for the next frame's refresh, to execute as part of the first loop next time.
Now, its possible to have a more advanced game loop by separating event treatments, by using a thread specific system event queue, preparing a pool of timestamped events, and when the main thread is in the event treatment update, it pulls this queue.
Its hardly possible to completely asychronize managers updates and frame rendering. Many engine state updates are not atomic. Even a fully retained engine will have limitations. DirectX11 introduces isolated command lists (deferred devices, in their parlance), which can help to design such an engine. But if you have such basic doubts about your own render loop, I suppose you don't use such things, yet.
Since you are in java, and you talk about ticks, I imagine you must have plugged your refresh function to a periodic timer. Of course this is sub-par as a game loop design, even from the most basic loop design described above. This is not how real time systems are designed, doing this, you rely on a framework, that depends on operating system native kernel tick rate, and java's virtual machine to relay you the wakening events (and scheduling) to fire up your frame routine. This will incur bad sampling rates (aliasing) and randomness from various sources. You will have to suffer from potentially long sleep times that are not controlled, in the thread ready queue, or kernel timer tick rounding issues. This will all misalign with your framerate presentation, and result in stutterings, and if the event queue is not fully depleted at each refresh, you will also have input lag, and potentially, building up with time. Worse, since you are event based, your events I fear, are consumed by event treatment, therefore your processing time will vary according to input, and delay your frame rendering. Potentially totally masking it out. This is like a security flaw, lots of input could overflow the game and make it unresponsive.
Event based systems are not made for real time applications, they are made for basically idling applications.
Now, back to your question, it should not be a problem to fraw at 40FPS when your game updates at 20. Also be careful, in a timer based system, 20 FPS = 50ms, it will likely round up to 60ms (4*15 ; 15=typical kernel), therefore you'll get 16FPS. Use some precise chronometers to display frame update time on screen, and another one to display display rate. (use a counter around the
Present call. Or framebuffer
Swap (OpenGL term), or simply install Fraps.)
This will give you some statistics.
The people advocating that high display rates are bad probably refer to useless loss of resource, resulting in machine heating up, or the useless frame taking time to finish, the GPU is not ready to render the next frame when its ready, that is to say, likely right in the middle of the useless redraw. Which is why, no redraw = better responsiveness, leaving the GPU available right away when the next frame is ready.
Also tearing, is an argument often heard. Tearing is caused by swappings (presentations) frequencies that are non multiples of the screen frequency. the
VSynch is the feature that helps that, by using hardware support, the VGA/DVI/HDMI cable transports a tick signal that says "I flipped!", the graphic cards receives it, signal it to the CPU with an interrupt (most likely), and the driver gets it and communicate it to the currently waiting OpenGL/DirectX presenting threads. This unleash a fast back-to-front copy, which executes in much less that the 60Hz timeframe, and then when the screen next flips, a full image is presented, this avoids perfectly the tearing.
The best is to respect this presentation rate, it is a tempo given by hardware, and the whole game/engine thread is put to sleep by the graphics API itself inside of the
Present call, which allows to rest the CPU, allowing it to cool down, and allows perfect frame/game synchronization if using a linear loop like mentioned above.
Provided your game can update faster than 60Hz. Otherwise, it will fallback immediately to 30Fps naturally, then 20, then 15, by plateaus. This is a natural response.