[DX11] DeviceContext.Draw

Started by
6 comments, last by Nik02 13 years, 9 months ago
Hello!

My program should perform rendering of graph with totla amount of 5 000 000 vertices. Vecrtices are separated to groups and each of them are rendered as LineStrips. Total amunt of LineStrips are 65 000. So, In method Render() I make something like the following

for (...)
{
deviceContext.Draw(...);
}
swapChain.Present(0,0);

The problem is, that sometimes it seems that DirectX simply skips some draw calls and I see only part of image as an output.

What kind of problem could it be? Maybe you can give some advices about organization or rendering in my case?

Thanks!
Advertisement
The system shouldn't skip anything regardless of the amount of batches. However, the operation queue of the graphics driver is of finite length and it is possible that you overflow its capacity. This would be a driver bug.

If you are using a professional card such as NVidia Quadro, have you tried to use a professional driver (CAD optimized) instead of the "game driver" that is more commonly used? The pro drivers usually allocate more memory for stuff like this at slight cost of performance.

65000 geometry batches per frame is a very high number; usually, most applications using GPU to draw (games and CAD apps) limit their batch counts to minimum (tens to hundreds) so as to avoid the significant processing overhead on the CPU side.

It is worth noting that modern D3D11 cards can draw an entire list of 5 000 000 primitives in one call.

Niko Suni

Thank you for quick reply!

Yes, it can be driver bug, but I'm using 9_1 profile and I've seen this problem on different machines (9_1 - 11_0 profiles). Is there any restrictions in number of Draw calls for different feature levels or smth else?
There is no restrictions to the number of draw calls per frame per se, but:

  • IIRC there is a timeout in which the drawing operations must finish. This is due to the fact that the render target is locked by the device itself during the actual drawing, and locking it for an extended period of time may cause problems with other processes' drawing operations.
  • 65000 sounds suspiciously close to 65535, which is the upper bound of a 16-bit integer. Without better knowledge right now, I would still guess that the internal drawing queue of the driver might just be using this datatype as the counter. This would lead to a situation in which the queue buffer could overflow before the hardware could process it.
  • It would make sense that the drawing operation buffer would be the same actual buffer regardless of the API used to fill it, even though the commands themselves are more plentiful in later versions of APIs. Therefore, it wouldn't matter which version of D3D you would use.


Have you based your tests on hardware from single manufacturer? Driver bugs like this often affect all (or most) hardware of a given manufacturer.

Also, try to call ID3D11DeviceContext::Flush manually to flush the command buffer more frequently than once per frame (which happens implicitly by the time of Present).

Niko Suni

Flush() Solved my problem! Now I see the whole image constantly:) Thank you!

Yeah, that was exactly overload of command buffer. The most interesting question now is how to determine, when I should call flush, because, if I call it after each Draw() call, the whole system works extremelly slow, and setting delay to smth like 100 calls solves the problem very good.
Off the top of my hat, I would call Flush no more than after every 500 to 1000 calls or so.

If the batch queue limit is indeed 65535, then it should be sufficient to call flush just before it goes full. All draw calls and unique state changes between those calls go into the queue before the hardware schedules and executes them.

Do note that the underlying issue is caused by a driver bug. If the driver would work perfectly, it would detect when the queue is in danger of becoming full and would perform the flush automatically in response to this condition. As to why the queue might be of fixed size - it may be that the actual hardware instruction queue dictates the buffer length. This could be fixed by virtualizing the queue in the driver and splitting it appropriately before sending it to hardware, though. It probably hasn't been a priority for the driver writers since practically all real-world usage tries to minimize the batch counts per frame for performance.

The fact that you can call Flush manually implies that the D3D core team anticipated that such bugs could occur in some edge cases like yours.

Niko Suni

One other way round it could be to render them as line lists. You're currently averaging about 77 lines per draw call which isn't very efficient. By using line lists you could say do 154 draw calls with 32,000 lines each, which should also run much quicker. You probably also want to use indexed primitives to avoid the need to specify each point twice.
I agree with Adam in that changing your batching strategy is ultimately the best option anyway.

Niko Suni

This topic is closed to new replies.

Advertisement