Sign in to follow this  

Running Shader in DX9 Returns Previous Frame

This topic is 811 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I finally got the code to run a HLSL pixel shader in DX9 almost working.

https://github.com/mysteryx93/AviSynthShader/blob/master/VideoPresenter/D3D9RenderImpl.cpp

 

Here's a weird issue I'm still having. As I load the script, the first frame is black, which is OK. Then, the first time I seek towards a different frame, the frame remains black. The second time I seek to another frame, the first frame is returned. It seems to always returns the frame data of the previous call.

 

So if I make a diff between the input and output frames of the shader, it looks totally wrong; because it's a diff between two different frames.

 

Why is it not returning me the current frame data right away?

 

... just a thought ... could it be that the frame isn't done being processed when I call to get the result?

Share this post


Link to post
Share on other sites

Shaders don’t “return frames”—they don’t run on anything but the frame they are given.
 

could it be that the frame isn't done being processed when I call to get the result?

That’s what double-buffering is. You draw to the current frame while the previous frame is being displayed to the viewer.


L. Spiro

Share this post


Link to post
Share on other sites

So... what do I need to change for GetRenderTargetData to return the result of the frame I had just sent in?

 

I suspect that the code to adapt might be in CopyFromRenderTarget.

https://github.com/mysteryx93/AviSynthShader/blob/master/VideoPresenter/D3D9RenderImpl.cpp#L197

Edited by MysteryX

Share this post


Link to post
Share on other sites

Delay the audio of your video by 1 frame?

 

I have no idea what you can do about it.  That’s up to your project.  You’re the only one who can decide which frame from the video to grab based on what is being displayed through your shader system in such a way that they are synchronized.  Since I expect your video to be played through Direct3D and through your system/shaders, I am not sure why you need to specifically synchronize them other than for debug purposes (validate your output), and for that, again, you can just grab the previous video frame instead of the current.

 

A 1-frame delay never hurt anyone singing karaoke.

 

And…???????????????????

 

 

L. Spiro

Share this post


Link to post
Share on other sites

I don't display anything to the user and I don't use any audio data. The library takes a video frame in, passes it through the shader and returns the frame data back. It has no graphical interface.

Share this post


Link to post
Share on other sites

I'm simply using a Render Target texture. The code is simple and is attached to the post.

 

I asked someone very knowledgeable in this and he answered this

 

 

You may have to flush the GPU to make sure rendering is complete. This is just a guess, maybe flushing won't help, in that case there is probably a bug in your code. There are various ways to flush, from what I remember:

1) You can lock the surface by using LockRect. But only some types of surfaces can be locked, so this might not work.
2) You can StretchRect to another surface, but I'm not sure if this is really reliable, you'd have to try.
3) Use IDirect3DQuery9 to manually flush the GPU. You should find some code via google.

 

I'll need time to explore those options. Do you guys have anything else to add to put me on the right track?

 

After calling GetRenderTargetData, I do call LockRect, so shouldn't that be flushing it already?

Edited by MysteryX

Share this post


Link to post
Share on other sites

There is no way I could be passing an incorrect frame. On each call to ProcessFrame, I have no access to data of any other frame.

 

Most likely cause is that the GPU isn't being flushed and "pending work" isn't being returned.

Share this post


Link to post
Share on other sites
If you start a series of operations on a render target (not to the screen/backbuffer) and then try to read back the results from that render target, all operations performed on it will be flushed and a stall will occur during that time (performance problem). You will get the immediate results of whatever you last did to that render target.


L. Spiro

Share this post


Link to post
Share on other sites

It "should" get flushed; I'd have to dig into why the flushing isn't happening.

 

As for the performance impact, what is the recommended approach to overcome this? One thing that can be done is to run the program 8 times in parallel (each instance would be independent), and I'm not sure what kind of impact that would have on performance. There might be a better way, somehow.

Share this post


Link to post
Share on other sites

I tried adding this code before reading the RenderTarget to flush the GPU and it doesn't help. I also tried adding a CPU loop to make sure the GPU has time to complete and it didn't help either. So perhaps the problem is somewhere else.

IDirect3DQuery9* pEventQuery = NULL;
HR(m_pDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery));
HR(pEventQuery->Issue(D3DISSUE_END));
while (S_FALSE == pEventQuery->GetData(NULL, 0, D3DGETDATA_FLUSH));

Any other idea?

 

The code is simple. GetFrame gets called for every frame that needs to be processed.

https://github.com/mysteryx93/AviSynthShader/blob/master/Shader.cpp

 

Then, GetFrame calls ProcessFrame in this file.

https://github.com/mysteryx93/AviSynthShader/blob/master/VideoPresenter/D3D9RenderImpl.cpp

 

I'm running out of ideas, and it's probably something very simple.

 

In CopyFromRenderTarget, if I copy from m_InputTextures[0].Memory instead of the RenderTarget, then the problem doesn't occur and the right (unprocessed) frames get returned. This proves that I'm passing the right frames in, and that the DirectX processing is causing the output to lag behind the input.

Edited by MysteryX

Share this post


Link to post
Share on other sites

Graphics bugs are best analyzed with graphics debuggers (PIX, Intel GPA Frame Analyzer, dunno but maybe even the VS integrated debugger works with DX9).

 

As for performance: Don't create (and destroy) resources every frame (pTempSurface in CopyFromRenderTarget). Create it at app startup and keep it.

Share this post


Link to post
Share on other sites

Unfortunately, PIX doesn't capture any data at all. Perhaps because my code isn't rendering anything to the screen. My DLL gets loaded by another program which displays the output data in its own way, but there's no DirectX graphical output to capture.

 

I'll give Intel GPA a try to see if it can capture some data.

 

Edit: GPA isn't of any more help. Because my application is a DLL that gets run by another application for testing, and because I'm not rendering anything to the screen, these apps can't capture anything. I also can't render to the screen because I need to work with half-float data and the backbuffer doesn't support that.

Edited by MysteryX

Share this post


Link to post
Share on other sites

Here's one thing that somewhat works. If I pass in the next frame instead of the current frame, then it works when playing in a linear way. The difference between the source and output frames is almost null. Obviously, that doesn't work when seeking to other frames.

 

At least it shows that it is precisely always returning the previous scene, and not skipping 2-3 scenes or doing other weirder stuff.

Share this post


Link to post
Share on other sites
huh... here's an ugly hack that works. Since it returns the previous scene, after calling Present, if I create a second dummy scene, then the correct data is being returned. It's not ideal but it works, and I still have no clue why this is happening.
HR(m_pDevice->Present(NULL, NULL, NULL, NULL));

// The RenderTarget returns the previously generated scene for an unknown reason.
// As a fix, we render another scene so that the previous scene becomes the one returned.
HR(m_pDevice->BeginScene());
HR(m_pDevice->EndScene());
SCENE_HR(m_pDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2), m_pDevice);
return m_pDevice->Present(NULL, NULL, NULL, NULL);
Now I'm thinking... is this by design, to avoid losing times between GPU operations? The previous scene is being rendered while I'm filling in the next scene to process? I can see how that could improve performance, and how that 1-frame delay wouldn't matter under normal scenarios. Edited by MysteryX

Share this post


Link to post
Share on other sites
Erm, he does actually (line 125):
 
return m_pDevice->SetRenderTarget(0, m_pRenderTargetSurface);
 
And pRenderTargetSurface comes from a render target texture. 
 
Whether this works is a different question. I'm particularly concerned about that HR macro used everywhere, which just returns on FAILED silently. You might be missing some calls without noticing.

I'd turn up the DX debug run time, litter the code with OutputDebugStr or some such. And yes, sometimes you have to go through hoops to make PIX or similar work (e.g: Making a non-DLL version). PIX also let's you configure to grab all calls, or at a specific frame number.

Share this post


Link to post
Share on other sites

This topic is 811 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this