The best way would be the same way as any other multi-threaded renderer.
The main thread fills a custom-made dispatch queue full of rendering commands, thread-safely swaps it, and then awakens the rendering thread with a signal.
Because of this, the rendering thread will not have been keeping a lock on the queue and the thread-safe swap will be with no overhead.
The rendering thread will then process each command in the queue and translate them to Direct3D or OpenGL API calls in order. It is working on a swapped copy so the main thread is free to send more commands to the other render queue.
You could even triple-buffer the command queue if necessary to avoid stalls.
When the render thread is done it unlocks the swapped queue, which allows the main thread to send a new set of commands and to fill the previous buffer with new commands. In a double-buffered system the only possible wait time is when the main thread wants to add commands to a queue that the render thread is using. You can get around this by using more queues. Triple-buffering, quad-buffering, sextuplet-buffering, etc.
It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums