Why there is more D3D10/11Device::Draw (and some other functions) implementations?

Started by
2 comments, last by Big Muscle 11 years, 1 month ago

Hello,

I have a problem which I am trying to solve for a longer time and have not found a correct solution yet. I need to hook certain functions in Direct3D and replace them with my own implementation. Hooking itself is not the problem. The problem is that Direct3D library uses more implementations for some functions and "randomly" switches between them. So if I hook e.g. Draw function, it works only for a while and then the function is replaced with another implementation so my hook is not called until I rehook this another implementation too.

After debugging Direct3D library, I noticed it really happens. I have found functions such as: D3D10Device1::Draw_<0>, D3D10Device1::Draw_<1>, D3D10Device1::Draw_<2> etc. It is much worse for D3D11Device as there is 8 different implementation of Draw function.

Does anybody know the technical details why (or when) each of the <0>, <1> etc.. is called? What is the difference between them?

My current solution for hooks is to periodically check Draw pointer and if it changes then rehook it again. It works but I don't see it as good solution (just because it requires additional code which can slow the things down).

Advertisement

This would be non-public info about something that's internal to the DX runtimes so speculation is all I can personally offer, unless/until someone who knows more comes along.

So - I'm guessing that there are fast-paths, slow-paths, special cases, etc, and depending on the current states, which shader stages are active, etc a different Draw function may be called. It may make sense to - for example - have a separate Draw function for when a GS is active versus when one is not.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

This would be non-public info about something that's internal to the DX runtimes so speculation is all I can personally offer, unless/until someone who knows more comes along.

So - I'm guessing that there are fast-paths, slow-paths, special cases, etc, and depending on the current states, which shader stages are active, etc a different Draw function may be called. It may make sense to - for example - have a separate Draw function for when a GS is active versus when one is not.

I also don't have any specific information, but this would make sense. Have you tried to track when the changes occur? What is happening between calls when you see the changes? I would imagine that it has something to do with the input configuration of the pipeline, such as the number of vertex buffers bound, or perhaps limited by the system values needed by the pipeline.

Do you ever see one of the old functions come back after it has been replaced?

Yes, the old functions come back (as I remember correctly it was something like Draw<0> is executed e.g. 500x, then function was swapped). Weird thing is that it does not happen always. Currently I wanted to track down how often the functions are swapped but it does not occur now :-/

However, some time ago, I was using following snippet running as background thread:


while(true)
{
   if(device->Draw != myDraw)
      rehook();

   Sleep(xxx);
}

it just hooks Draw function still around. If sleep is not used then it works "correctly" (there is still possible race condition but very rare) - but consumes 100% CPU.. When there is Sleep (with > 0 ms) then it does not work, so the functions are swapped very often. Also, it is not something like "if(some_condition_is_true) Draw<0> else Draw<1>", The function pointer is changed directly in the device object's virtual address table.

This topic is closed to new replies.

Advertisement