Startup loading/Unloading of DirectX Threads

Hi, I’ve just updated to JUCE 8.0.7 from JUCE 6. When starting up my application, I see this kind of debug output:

[...]
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\DXCore.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvppex.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\d3d10warp.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\d3d10warp.dll'
The thread 55460 has exited with code 0 (0x0).
The thread 62480 has exited with code 0 (0x0).
The thread 15924 has exited with code 0 (0x0).
The thread 62640 has exited with code 0 (0x0).
The thread 47236 has exited with code 0 (0x0).
The thread 58152 has exited with code 0 (0x0).
The thread 57960 has exited with code 0 (0x0).
The thread 34356 has exited with code 0 (0x0).
The thread 47184 has exited with code 0 (0x0).
The thread 58680 has exited with code 0 (0x0).
The thread 15100 has exited with code 0 (0x0).
The thread 52624 has exited with code 0 (0x0).
The thread 56348 has exited with code 0 (0x0).
The thread 59100 has exited with code 0 (0x0).
The thread 59480 has exited with code 0 (0x0).
The thread 12176 has exited with code 0 (0x0).
The thread 58096 has exited with code 0 (0x0).
The thread 45900 has exited with code 0 (0x0).
The thread 34528 has exited with code 0 (0x0).
The thread 30556 has exited with code 0 (0x0).
The thread 57804 has exited with code 0 (0x0).
The thread 11092 has exited with code 0 (0x0).
The thread 54828 has exited with code 0 (0x0).
The thread 29904 has exited with code 0 (0x0).
The thread 35364 has exited with code 0 (0x0).
The thread 48096 has exited with code 0 (0x0).
The thread 63476 has exited with code 0 (0x0).
The thread 62020 has exited with code 0 (0x0).
The thread 3924 has exited with code 0 (0x0).
The thread 54508 has exited with code 0 (0x0).
The thread 49884 has exited with code 0 (0x0).
The thread 43632 has exited with code 0 (0x0).
The thread 22312 has exited with code 0 (0x0).
The thread 9504 has exited with code 0 (0x0).
The thread 63172 has exited with code 0 (0x0).
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\umpdc.dll'
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\powrprof.dll'
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvwgf2umx.dll'
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvgpucomp64.dll'
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvldumdx.dll'
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvldumdx.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvgpucomp64.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvwgf2umx.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\powrprof.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\powrprof.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\powrprof.dll'
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\umpdc.dll'. Symbol loading disabled by Include/Exclude setting.
The thread 38720 has exited with code 0 (0x0).
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\d3d10warp.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\d3d10warp.dll'
The thread 61424 has exited with code 0 (0x0).
The thread 15696 has exited with code 0 (0x0).
The thread 37364 has exited with code 0 (0x0).
The thread 52916 has exited with code 0 (0x0).
The thread 37184 has exited with code 0 (0x0).
The thread 58784 has exited with code 0 (0x0).
The thread 29576 has exited with code 0 (0x0).
The thread 15224 has exited with code 0 (0x0).
The thread 36352 has exited with code 0 (0x0).
The thread 63076 has exited with code 0 (0x0).
The thread 13584 has exited with code 0 (0x0).
The thread 39888 has exited with code 0 (0x0).
The thread 3312 has exited with code 0 (0x0).
The thread 58988 has exited with code 0 (0x0).
The thread 33404 has exited with code 0 (0x0).
The thread 9676 has exited with code 0 (0x0).
The thread 25984 has exited with code 0 (0x0).
The thread 19500 has exited with code 0 (0x0).
The thread 42008 has exited with code 0 (0x0).
The thread 3200 has exited with code 0 (0x0).
The thread 42104 has exited with code 0 (0x0).
The thread 29408 has exited with code 0 (0x0).
The thread 43988 has exited with code 0 (0x0).
The thread 61480 has exited with code 0 (0x0).
The thread 44620 has exited with code 0 (0x0).
The thread 55904 has exited with code 0 (0x0).
The thread 56308 has exited with code 0 (0x0).
The thread 34876 has exited with code 0 (0x0).
The thread 26184 has exited with code 0 (0x0).
The thread 5196 has exited with code 0 (0x0).
The thread 32616 has exited with code 0 (0x0).
The thread 63596 has exited with code 0 (0x0).
The thread 50984 has exited with code 0 (0x0).
The thread 44492 has exited with code 0 (0x0).
The thread 61868 has exited with code 0 (0x0).
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\umpdc.dll'
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\powrprof.dll'
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvwgf2umx.dll'
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvgpucomp64.dll'
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvldumdx.dll'
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\powrprof.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\umpdc.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvldumdx.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvgpucomp64.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_0afec3f2050014a0\nvwgf2umx.dll'. Symbol loading disabled by Include/Exclude setting.
The thread 45164 has exited with code 0 (0x0).
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\d3d10warp.dll'. Symbol loading disabled by Include/Exclude setting.
'mtplayer.exe' (Win32): Loaded 'C:\Windows\System32\DWrite.dll'. Symbol loading disabled by Include/Exclude setting.
The thread 39404 has exited with code 0 (0x0).
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\DWrite.dll'
'mtplayer.exe' (Win32): Unloaded 'C:\Windows\System32\d3d10warp.dll'
[and so on...]

In total, d3d10warp.dll is loaded and unloaded 10 times before my code does anything meaningful. Each unload kills ~35 threads, so this is a heavy weight thing. In release mode, start-up time went from ~100ms to 1100ms before my own code is executed. I’d really like to avoid such a delay.

I’ve tried to find the cause. The loading/unloading cycles happen when I instantiate my main Window. The super class constructors of DocumentWindow eventually call into Windows-specific code, where I see temporary instantiations of the DirectX class using:

SharedResourcePointer<DirectX>

Each time, the DirectX class is instantiated, and subsequently deleted.

For example, here:
modules/juce_gui_basics/native/juce_VBlank_windows.cpp:215
Class VBlankDispatcher, method updateDisplay(…):

void updateDisplay (ComponentPeer::VBlankListener& listener, HMONITOR monitor)
{
    [...]
    SharedResourcePointer<DirectX> directX;
    for (const auto& adapter : directX->adapters.getAdapterArray())
    {
        [...]
    }
}
  1. Is my code faulty? Should I do something else prior to instantiating the main window?
  2. Or can I somehow block SharedResourcePointer<DirectX> so that it doesn’t always get deleted multiple times during start-up?
  3. Or do you think this is something that should get fixed in JUCE?

Thanks.

1 Like

That doesn’t seem like it’s intended behavior, so should be fixed in JUCE imo, but as a temporary fix you can (hopefully) instantiate a SharedResourcePointer<DirectX> as a member of your main class (or anything with a longer-lived scope than your window. It looks like the DirectX class would be visible to your code, so that should work (probably wrapped in an #if JUCE_WIN).

Background: SharedResourcePointer<T> is a kind of singleton-ish reference that creates a shared instance of the class when the first reference is created, and supplies the same shared instance for every further reference. It counts references and deletes the shared resource whenever the last reference is deleted. So if you make sure you hold a reference yourself during the time where these multiple instantiations happen, you prevent it from deleting and recreating the shared instance again and again.

@hugoderwolf thanks, I agree. I tried your approach (hold on to SharedResourcePointer<DirectX>), but eventually, it got too messy to #include all the required JUCE-internal dependencies.

Yes, thank you. I didn’t have time to investigate the longer startup time, but I’ve also noticed this. It’s very annoying, as each plugin’s first instance now has an extra delay compared to non-JUCE8 plugins.

Even a tiny app I wrote to show me images with a checkboard background has a 1-2 second delay, whereas when I built it with JUCE7, it opens instantly.

Thanks all for letting us know. This sounds like a bug on our side.

+1 I’m seeing this as well. A workaround would be very welcome.

Now I got intrigued to find a workaround. I noticed that initializing a PNG image would also block the DirectX instance, so here is a workaround:

Just load a PNG image before you create the window. If you don’t need to load a PNG anyway, you can just load a dummy one (which is way faster than creating and destroying up to 350 threads).

I’ve put the following code into the initialise() method of my JUCEApplication class, because that’s where I create the main window.

// START WORKAROUND
#if JUCE_WINDOWS
// JUCE 8.0.7 initializes DirectX multiple times when creating the first window.
// This workaround loads a PNG file before doing any window initialization,
// that will initialize DirectX and hold on to it (as long as the image exists).
// use "the world's smallest PNG image: https://evanhahn.com/worlds-smallest-png/
const unsigned char worldSmallestPng[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x37, 0x6E, 0xF9,
	0x24, 0x00, 0x00, 0x00, 0x0A, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0x63, 0x60, 0x00, 0x00, 0x00,
	0x02, 0x00, 0x01, 0x73, 0x75, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE,
	0x42, 0x60, 0x82};
size_t worldSmallestPngSize = 67;
auto image = ImageFileFormat::loadFrom(worldSmallestPng, worldSmallestPngSize);
#endif //JUCE_WINDOWS
// END WORKAROUND

I did not try it myself, but Matt’s branch is supposed to mitigate this problem: Direct2D Causes Considerable Performance Drop - #80 by zsliu98

You mixing two issues here. Matt didn’t even attempt to fix this particular problem, because he wasn’t aware of it.

Did you read the post I linked?

Yes, and you mentioned loading time in the previous-to-last post. Matt did not comment on it or say that his branch would solve that particular problem.

Ok maybe I should have used the word reported instead of supposed, as this was obviously not Matt’s intent.
Still, it seems to have an effect on said problem.

You claimed that Matt’s branch is supposed to mitigate this problem. That is simply not true. You are confusing people here, leading them to believe that it’s been taken care of or that somebody else (Matt, in this case) has the solution.

1 Like

Don’t think I’ve tackled this specific issue. Looks like the team is on top of it.

Matt

I’m removing the last few posts of this thread because they’re starting to go off topic, and fall below the standard of conduct we expect from participants on the Forum. Please review our Code of Conduct here.

1 Like

I would ask you to remove everything starting with fuo’s misleading post.

1 Like

Wouldn’t it be enough to create a 1x1 image? That would also initialize DirectX to upload that image to the GPU.

Possible, I didn’t bother trying other, possibly simpler workarounds. It was a fun exercise to find the smallest PNG… And I hope to get rid of this code soon, anyway :slight_smile:

I’ve just tried it with a tiny app. I simply created a global variable like this:

juce::Image	dummy { juce::Image::PixelFormat::ARGB, 1, 1, false };

Now, my app has a startup time of 50 - 100ms. Before, it was a second or longer.

The difference in perceived startup time is night and day. Without this workaround, it always felt like I didn’t double-click my icon correctly. Now I’ve barely released the button, and the app is visible.

I can confirm this for an app as well. However I am not sure how to make it work for a plugin. I do see lengthy editor opening times sometimes and think that might be related, but a plugin probably shouldn’t initialize DirectX unless the editor is opened. I tried to create a static juce::Image when the first editor instance is created, but results are mixed so far. Has anyone figured it out?
Obviously a real solution would happen inside JUCE regardless of app or plugin.