protonscr

Oscillating overall performance (D3D11)

dxvkclosed
doitsujin/dxvk#3165 · opened 2023-01-03 by PeterTh · updated 2023-01-09 · 7 comments · github
PPeterTh 2023-01-03 github

When testing testing the performance of an upcoming game on the Steam Deck, I discovered a very strange issue. I don't expect you to "fix" this for me based on just this description, but maybe someone experienced with the inner workings of DXVK might have an idea of what might be going on.

Some preliminaries:

  • the general behavior I describe here is happening in the same way on both Linux (Steam Deck / RADV) and Windows
  • it also happens on both AMD and NV GPUs
  • it also happens in the same way going from DXVK 2.0 all the way back to 1.0
    (just at higher absolute levels of performance with newer versions - nice)

In the particular location of the game that I will use for this description, performance sits at a very stable 160 FPS in DX11 (on one of the systems tested -- but as I said, the behavior is the same across all of them, just at different performance levels). This does not change over time at all.
With DXVK, it starts off at 115 FPS, and then after ~40 seconds flips over to running at 160 FPS (so the same as DX11). At that point, I wouldn't be too confused, I'd just think that some background optimization finished.
However, the strange part is that, after another ~40 seconds, it flips back to running at a consistent 115 FPS. And then, after another time period, back to 160, and so on and so forth.
On Steam deck, the same "oscillation" happens, but with ~38 FPS and ~60 FPS -- so at a much more critical level -- that's why I first started looking into it.

Any ideas on what might cause something like this, or advice on narrowing it down?

Edit: as always when posting something like this I had decent idea right afterwards.
If I speed up the in-game dt by a factor of 5, the switch between the two performance profiles happens every 8 seconds rather than every 40. Which confirms that the game is doing something different (which was the most likely explanation anyway).
So that narrows the question down to what the game might be doing differently in terms of rendering that has (i) no visual impact and (ii) affects DXVK to the tune of ~40% performance, but DX11 not at all.

BBlisto91 2023-01-03 github

What's the game if i might ask? :slightly_smiling_face: (Not a dev)

Edit: Oh it's your own game. Understood :+1:

PPeterTh 2023-01-03 github

I sadly can't answer that for now.

But I do have some news: after doing lots of non-obvious things, I did the obvious (and trivial) thing and looked at the full DXVK overlay. And this provided some pretty definite insight: in the "good" performance scenario there are 3 queue submissions and 0 queue syncs, in the "bad" performance scenario there are 5 queue submissions and 1 queue sync.

As you might guess, my next question is, what can cause a queue sync?
Edit: a quick look through the code reveals that it's waiting for a resource to become available. Is there any reasonable path towards getting from that resource handle back to something I can identify at the DX11 level?

KK0bin maintainer 2023-01-03 github

Queue syncs often mean stalling until the GPU finishes work for some reason. First thing to check would be how you're using D3D11DeviceContext::Map. Specifically with a MapType other than D3D11_MAP_WRITE_DISCARD or D3D11_MAP_WRITE_NO_OVERWRITE.

PPeterTh 2023-01-03 github

Thanks @K0bin!
I actually did just that (right after making the previous post and thinking about what would realistically cause a stall). Luckily there weren't too many reads on any data produced by previous frames, so I just went through them individually until I found the cause.

Now that I know what it is I can probably fix it (e.g. by using even older data; it's not critical that it is recent). I do think it's curious though how large the performance impact is in this case, and how it doesn't really happen at all in DX11. Note that the readback in question is basically just a single uint32.
Still I can fix it, so I'm happy.

KK0bin maintainer 2023-01-03 github

Great, really cool that you're testing DXVK compatibility and performance before launch!

Ddoitsujin maintainer 2023-01-08 github

@PeterTh bit late to the party (was still on vacation at the time), but I'm curious what resource type this is and how you're writing to it, and when the readback happens relative to the write.

As a general note though, DXVK's command submissions are generally fairly coarse (3-5 per frame is on the low side of things but very much in the expected range), and Map will block until the entire command list that the resource you're trying to map was last used in has completed in its entirety.

What's likely happening here is that the last write to your readback resource sometimes happens at the end of a command list that gets submitted shortly after (good performance case), or right at the beginning of a command list that still gets to do a lot of work afterwards (poor performance case), and you're performing the map (somewhat) shortly after the last GPU write.

If this is what's happening, consider explicitly calling ID3D11DeviceContext::Flush after the last write to your readback resource (obviously needs to happen on the immediate context). This is easy and should really just be enough to solve the problem.

DXVK currently does not track stalls on resource mapping, only on event queries (which is an optimization I originally implemented for Unreal Engine 4). We kind of tried to extend this to resources at some point but it didn't really deliver the expected results.

I think in the end this is down to our flush heuristic just not being very good for low-latency readbacks like that, it's something we can try to improve (and actually have tried in the past), but it's certainly not easy to satisfy all workloads.

PPeterTh 2023-01-09 github

Thanks for the feedback! I've already fixed the issue by delaying the readback to a subsequent frame, since it's not time critical.
It was written by a compute shader invocation.

Generally, I think the code is now better than it was -- even if apparently all the DX11 drivers we tested were good (or better than DXVK) at dealing with this pattern, I think having the pattern in the first case when not strictly necessary was bad design.

Nothing extracted yet.