DXGI desktop snapshot taking

One more system check tool to quickly enumerate available monitors (using DXGI), take snapshots and was them in PNG files (using WIC).

Apart from straightforward desktop snapshot taking, the tool offers a few more functions:

  1. Goes through the entire list of available video adapters and connected monitors
  2. Uses three slightly different methods to do the same thing: “pass A” and files starting with “A” – using Direct3D 11; “pass B” – basically the same but with a Direct3D 11 device created without specifying adapter; “pass C” – same as pass A but using Direct3D 10.1 API
  3. Displayed (printed out) are the monitor connected to video adapter outputs, including the case of sharing/mirroring displays; when two displays are showing the same signal via mirroring the output will list them along with connector type, e.g. same picture is displayed on two physical displays connected with DisplayPort and HDMI cables respectively:
Output: \.\DISPLAY5
LG Ultra HD, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL, \?\DISPLAY#GSM5B09#5&7f9757e&0&UID260#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
LG Ultra HD, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI, \?\DISPLAY#GSM5B08#4&1540260c&0&UID206371#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
  1. Detects multi-GPU systems and automatically repeats attempt to take snapshots applying different GPU preferences (“Power Saving” preference vs. “High Performance”); the reason for this is that use of wrong GPU preference is a notorious reason for DXGI Desktop Duplication API to not provide duplication service. Files named A1, A2 (as opposed to A0) correspond to use of power saving (1) or high performance (2) adapter:
  1. Last but not least, command line option “-DebugLayers” enables the tool to include DirectX Debug Layer messages in the option (such as, to troubleshoot errors in greater detail); the layer should be installed, of course.

Download links


GetFileVersionInfoSize and API sets

GetFileVersionInfoSizeW function in Requirements section lists:

Minimum supported clientWindows Vista [desktop apps only]
Minimum supported serverWindows Server 2008 [desktop apps only]
Target PlatformWindows
Headerwinver.h (include Windows.h)

and this is inaccurate. The actual requirement DLL is api-ms-win-core-version-l1-1-1.dll instead. However, what does it mean exactly? Windows API sets:

API Sets rely on operating system support in the library loader to effectively introduce a namespace redirection component into the library binding process. Subject to various inputs, including the API Set name and the binding (import) context, the library loader performs a runtime redirection of the reference to a target host binary that houses the appropriate implementation of the API Set.

The decoupling between implementation and interface contracts provided by API Sets offers many engineering advantages, but can also potentially reduce the number of DLLs loaded in a process.

The “hyphen one” DLL (api-ms-win-core-version-l1-1-1.dll) is missing in Windows Server 2012 R2 and so the documented promise to offer support starting Windows Server 2008 is incorrect. Windows Server 2012 R2 has only “hyphen zero” DLL.

The hyphen zero DLL exposes, however, GetFileVersionInfoSizeExW entry point so the application developers addressing backward compatibility should switch from use of GetFileVersionInfoSize to GetFileVersionInfoSizeEx even though the former is not documented as deprecated explicitly (probably another out of date aspect of the documentation).

The same applies to GetFileVersionInfo functions.

Also related, this part of MSDN documentation API Sets available in Windows 8.1 and Windows Server 2012 R2 looks good and has no mention of GetFileVersionInfoSize and GetFileVersionInfo.

Incorrect breaking #import behavior in recent (e.g. 16.6.2) MSVC versions

Yesterday’s bug is not the only “news”. Some time ago I already saw weird broken behavior of rebuild of DirectShowSpy.dll with current version of Visual Studio and MSVC.

Now the problem is getting more clear.

Here is some interface:

interface IMuxFilter : IUnknown
    HRESULT IsTemporaryIndexFileEnabled();
    HRESULT SetTemporaryIndexFileEnabled([in] BOOL bTemporaryIndexFileEnabled);
    HRESULT GetAlignTrackStartTimeDisabled();
    HRESULT SetAlignTrackStartTimeDisabled([in] BOOL bAlignTrackStartTimeDisabled);
    HRESULT GetMinimalMovieDuration([out] LONGLONG* pnMinimalMovieDuration);
    HRESULT SetMinimalMovieDuration([in] LONGLONG nMinimalMovieDuration);

Compiled into type library it looks okay. Windows SDK 10.0.18362 COM/OLE Object Viewer shows the correct definition obtained from the type library:

interface IMuxFilter : IUnknown {
    HRESULT _stdcall IsTemporaryIndexFileEnabled();
    HRESULT _stdcall SetTemporaryIndexFileEnabled([in] long bTemporaryIndexFileEnabled);
    HRESULT _stdcall GetAlignTrackStartTimeDisabled();
    HRESULT _stdcall SetAlignTrackStartTimeDisabled([in] long bAlignTrackStartTimeDisabled);
    HRESULT _stdcall GetMinimalMovieDuration([out] int64* pnMinimalMovieDuration);
    HRESULT _stdcall SetMinimalMovieDuration([in] int64 nMinimalMovieDuration);

Now what happens when MSVC #import takes it into Win32 32-bit code:

struct __declspec(uuid("6ce45967-f228-4f7b-8b93-83dc599618ca"))
IMuxFilter : IUnknown
    // Raw methods provided by interface

      virtual HRESULT __stdcall IsTemporaryIndexFileEnabled ( ) = 0;
    virtual HRESULT _VtblGapPlaceholder1( ) { return E_NOTIMPL; }
      virtual HRESULT __stdcall SetTemporaryIndexFileEnabled (
        /*[in]*/ long bTemporaryIndexFileEnabled ) = 0;
    virtual HRESULT _VtblGapPlaceholder2( ) { return E_NOTIMPL; }
      virtual HRESULT __stdcall GetAlignTrackStartTimeDisabled ( ) = 0;
    virtual HRESULT _VtblGapPlaceholder3( ) { return E_NOTIMPL; }
      virtual HRESULT __stdcall SetAlignTrackStartTimeDisabled (
        /*[in]*/ long bAlignTrackStartTimeDisabled ) = 0;
    virtual HRESULT _VtblGapPlaceholder4( ) { return E_NOTIMPL; }
      virtual HRESULT __stdcall GetMinimalMovieDuration (
        /*[out]*/ __int64 * pnMinimalMovieDuration ) = 0;
    virtual HRESULT _VtblGapPlaceholder5( ) { return E_NOTIMPL; }
      virtual HRESULT __stdcall SetMinimalMovieDuration (
        /*[in]*/ __int64 nMinimalMovieDuration ) = 0;
    virtual HRESULT _VtblGapPlaceholder6( ) { return E_NOTIMPL; }

WTF _VtblGapPlaceholder1??? That was uncalled for!

It looks like some 32/64 bullshit added by MSVC from some point (cross-compilation issue?) for no good reason reason. A sort of gentle reminder that one should get rid of #import in C++ code.

Please have it fixed, 32-bit code is something still being used.

#import of Microsoft’s own quartz.dll, for example, has the same invalid gap insertion:

struct __declspec(uuid("56a868bc-0ad4-11ce-b03a-0020af0ba770"))
IMediaTypeInfo : IDispatch
    // Raw methods provided by interface

      virtual HRESULT __stdcall get_Type (
        /*[out,retval]*/ BSTR * strType ) = 0;
    virtual HRESULT _VtblGapPlaceholder1( ) { return E_NOTIMPL; }
      virtual HRESULT __stdcall get_Subtype (
        /*[out,retval]*/ BSTR * strType ) = 0;
    virtual HRESULT _VtblGapPlaceholder2( ) { return E_NOTIMPL; }

Something got broken around version 16.6.1 of Visual C++ compiler

Ancient piece of code started giving troubles:

template <typename T>
class ATL_NO_VTABLE CMediaControlT :
        T* MSVC_FIX_VOLATILE pT = static_cast<T*>(this); // <<--------------------------------
        CRoCriticalSectionLock GraphLock(pT->m_GraphCriticalSection);
        __D(pT->GetMediaControl(), E_NOINTERFACE);

When MSVC_FIX_VOLATILE is nothing, it appears that optimizing compiler forgets pT and uses just some variation of adjusted this, which makes sense overall because static cast between the two can be resolved at compile time.

However, the problem is that the value of this is wrong and there is undefined behavior scenario.

If I make MSVC_FIX_VOLATILE to be volatile and have the variable pT somewhat “heavier”, optimizing compiler would forget this and uses pT directly with everything working as expected.

The problem still exists in current 16.6.2.

Intel Media SDK H.264 encoder buffer and target bitrate management

I might have mentioned that Intel Media SDK has a ridiculously eclectic design and is a software package for the brave. Something to stay well clear of for as long as you possibly can.

To be on par on the customer support side Intel did something that caused blocking of Intel Developer Zone account. Over time I did a few attempts to restore the account and only once out of the blue someone followed up from there with a surprising response: “You do not have the enterprise login account”. That’s unbelievable, I could register account, I could post like this, I can still request password reset links and receive them, but the problem is I don’t have “enterprise account”.

Back to Intel Media SDK where things are designed to work about the same obvious and reliable as their forums. A bit of code from very basic tutorial:

    //5. Initialize the Media SDK encoder
    sts = mfxENC.Init(&mfxEncParams);

    // Retrieve video parameters selected by encoder.
    // - BufferSizeInKB parameter is required to set bit stream buffer size
    mfxVideoParam par;
    memset(&par, 0, sizeof(par));
    sts = mfxENC.GetVideoParam(&par);

    //6. Prepare Media SDK bit stream buffer
    mfxBitstream mfxBS;
    memset(&mfxBS, 0, sizeof(mfxBS));
    mfxBS.MaxLength = par.mfx.BufferSizeInKB * 1000;
    mfxBS.Data = new mfxU8[mfxBS.MaxLength];

Even though who was that genius who designed it to measure buffers in kilobytes, the snippet makes great sense. You ask SDK for required buffer size and then you provide the space. I myself am even more generous than that: I grant 1024 bytes for every kilobyte in question.

The thing is that hardware encoder is still hitting scenarios where it is unable to fit the data into the space sized the mentioned way. What happens if encoder has more data on hands, maybe it emits a warning? “Well I just screwed things up, be aware”? Buffer overflow error? Buffer reallocation request? Oh no, the SDK makes it smarter: it fills the buffer completely trimming the excess making the bitstream incompliant and triggering frame corruption later on decoder end. Then encoder continues as if nothing important has happened.

There is an absolute rule in the software technology that if the thing is designed to be able to get broken in certain aspect, once in a while there will be a consumer hit by this flaw. Maybe just this once Intel guys thought it would not be the case.

Heterogeneous Media Foundation pipelines

Just a small test here to feature use of multiple GPUs within single Media Foundation pipeline. The initial idea here is pretty simple: quite so many systems are equipped with multiple GPUs, some have “free” onboard Intel GPU idling in presence of regular video card. Some other systems have integrated “iGPU” and discrete “dGPU” seamlessly blended by DXGI.

Media Foundation API does not bring any specific feature set to leverage multiple GPUs at a time, but this is surely possible to take advantage of.

The application creates a 20 second long video clips by combining GPUs: one GPU is used for video rendering and another is a host of hardware H.264 video encoding. No system memory is used for uncompressed video: system memory jumps in first to receive encoded H.264 bitstream. The Media Foundation pipeline hence is:

  • Media Source generating video frames off its video stream using the first GPU
  • Transform to combine multiple GPUs
  • H.264 video encoder transform specific to second GPU
  • Stock MP4 Media Sink

The pipeline runs in a single media session pretty much like normal pipeline. Media Foundation is designed in the way that primitives do not have to be aligned in their GPU usage with the pipeline. Surely they have to share devices and textures so that they all could operate together, but pipeline itself does not put much of limitations there.

Microsoft Windows [Version 10.0.18363.815]
(c) 2019 Microsoft Corporation. All rights reserved.

HeterogeneousRecordFile.exe 20200502.1-11-gd3d16d5 (Release)
HEAD -> master, origin/master
2020-05-09 23:51:54 +0300
Found 2 DXGI adapters
Trying heterogeneous configurations…
Using NVIDIA GeForce GTX 1650 to render video and Intel(R) HD Graphics 4600 to encode the content
Using filename HeterogeneousRecordFile-20200509-235406.mp4 for recording
Using Intel(R) HD Graphics 4600 to render video and NVIDIA GeForce GTX 1650 to encode the content
Using filename HeterogeneousRecordFile-20200509-235411.mp4 for recording
Trying trivial configuration with loopback data transfer…
Using NVIDIA GeForce GTX 1650 to render video and NVIDIA GeForce GTX 1650 to encode the content
Using filename HeterogeneousRecordFile-20200509-235416.mp4 for recording
Using Intel(R) HD Graphics 4600 to render video and Intel(R) HD Graphics 4600 to encode the content
Using filename HeterogeneousRecordFile-20200509-235419.mp4 for recording

This is just a simple use case, I believe there can be other: GPUs are pretty powerful for certain specific tasks, and they are also equipped with video specific ASICs.

Download links


A readable version of HelloDirectML sample

So it came to my attention that there is a new API in DirectX family: Direct Machine Learning (DirectML).

Direct Machine Learning (DirectML) is a low-level API for machine learning. It has a familiar (native C++, nano-COM) programming interface and workflow in the style of DirectX 12. You can integrate machine learning inferencing workloads into your game, engine, middleware, backend, or other application. DirectML is supported by all DirectX 12-compatible hardware.

You might want to check out this introduction video if you are interested:

I updated HelloDirectML code and restructured it to be readable and easy to comprehend. In my variant I have two operators of addition and multiplication following one another with a UAV resource barrier in between. The code does (1.5 * 2) ^ 2 math in tensor space.

Here is my fork with updated HelloDirectML, with the top surface code with tensor math in less than 150 lines of code starting here. If you are a fan of spaghetti style (according to Wiki it appears what I prefer is referred to as “Ravioli code”), the original sample is there.