AMD could do better in notifying of their updates and fixes for the video adapter hardware drivers

Quite a number of AMD GPU based video cards are running outdated drivers for such modern task as low latency game streaming, and users have no clue that the video driver is letting them down. For example, a slice of version structure for those who “have things going rather well”:

Current recommended (“stable”?) version is 20.4.2 (just 20%) released on 15-May-2020 10 weeks ago, and optional (“latest”, “beta”?) is 20.7.2. Many users have 19.xx just because they install the driver pulled by Windows Update expecting this to be a good driver. It is not and with many adapters it simply does not work for some of the video encoding tasks because it does not follow the documented behavior. Quite some users just have no guess that their “standard” video driver brought to them via Windows Update channel is hugely outdated and multiple updates have been available.

Now the structure for AMD RX 5×00 XT series (especially popular RX 5700 XT):

The small fraction of 20.5.1 reflects the broken state of the driver: video encoder there fails to process video. Yes, it is fixed in 20.7.1 but only users who check and install optional updates of AMD Adrenaline 2020 have a chance to be aware of availability of fixing update.

Another confusing thing is that there is a recommended version of AMD driver software and it seems to be the default setting to pull recommended updates. Yet the driver download section (link above) suggests to install optional/latest version 20.7.2 of the driver software package.

1 GB limit for Windows::Storage::FileIO::ReadLinesAsync?

There seem to be a limit of 1GB for FileIO::ReadLinesAsync API even though documentation is silent on this.

StorageFile EventStorageFile { TryGetFile(m_Configuration.m_ApplicationStorageFolder, m_FileName) };
if(!EventStorageFile)
    return;
using namespace winrt::Windows::Storage;
auto const LineVector { FileIO::ReadLinesAsync(EventStorageFile).get() };

The exception message itself adds no clarity:

WinRT originate error – 0x80070057 : ‘The parameter is incorrect.’.

The function starts working well when the input file is slightly reduced in size (under 1050 MB).

C++/WinRT version of SetFileTime

SetFileTime is simple and does not deserve a blog post: you have a file handle, you have date/time, you set it.

StorageFile File { ... };
FILETIME DateCreatedFileTime = ...;
winrt::file_handle File { CreateFile(File.Path().c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr) };
winrt::check_bool(SetFileTime(File.get(), &DateCreatedFileTime, &DateCreatedFileTime, &DateCreatedFileTime));

C++/WinRT and UWP make the journey much more exciting.

There is StorageItemContentProperties Class which provides access to the content-related properties of an item (like a file or folder). This includes file times which are exposed as named properties, like “System.DateModified”. StorageItemContentProperties.SavePropertiesAsync Method is to save properties associated with the item.

SavePropertiesAsync is asynchronous so you have to deal with this too, but it is not to worry since C++/WinRT does not let you down with this at least.

One another thing is you need a Windows::Foundation::DateTime value for the time which is something you might be not have good understanding for, as opposed to old fashioned FILETIME's definition of “a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC)”.

In C++/WinRT Windows::Foundation::DateTime is directly mapped to std::chrono::time_point and there is also winrt::clock as well, and conversion helpers which are easy to use but you have to be aware of them.

Moving on? With Windows::Foundation::DateTime value on hands you need to put it into a key-value collection of properties to save. However, since values, in general, might be of different types you need to convert the time to variant IInspectable. That is, to “box” it. Luckily, C++/WinRT has it all for you already:

C++/WinRT provides the winrt::box_value function, which takes a scalar value and returns the value boxed into an IInspectable. For unboxing an IInspectable back into a scalar value, there are the winrt::unbox_value and winrt::unbox_value_or functions.

Now we reached a hard one. SavePropertiesAsync signature is this:

...SavePropertiesAsync(param::async_iterable<Windows::Foundation::Collections::IKeyValuePair<hstring, Windows::Foundation::IInspectable>> const& propertiesToSave) const
{
  ...
}

So IInspectable there is the boxed value. This needs to be taken into something convertible to Windows::Foundation::Collections::IKeyValuePair which is already a bit scary. But the truth is the real bastard is param::async_iterable.

I have to admit MSDN documentation for C++/WinRT is awesome. There are so many things already mentioned there, including, for example, this article: Standard C++ data types and C++/WinRT, which is relevant and helpful. C++/WinRT is awesome too.

So you might think you could just have a std::map and it will be converted to Windows::Foundation::Collections::IKeyValuePair automagically and then it would be picked up by param::async_iterable as an argument? No.

[...]
error C2664: 'winrt::Windows::Foundation::IAsyncAction winrt::impl::consume_Windows_Storage_FileProperties_IStorageItemExtraProperties::SavePropertiesAsync(void) const': cannot convert argument 1 from 'std::map,std::allocator>>' to 'const winrt::param::async_iterable> &'
[...]
Reason: cannot convert from 'std::map,std::allocator>>' to 'const winrt::param::async_iterable>'
[...]
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

The thing is, and it’s mentioned here, for example, that async_iterable need to acquire ownership of the collection when it takes it as an argument in a thin and lightweight way. So you have got to std::move it there before the code starts building.

What do we got? Thing do build and call UWP API correctly. Everything is important here:

StorageFile File { ... };
FILETIME DateCreatedFileTime = ...;
std::map<winrt::hstring, winrt::Windows::Foundation::IInspectable> PropertyMap;
PropertyMap.emplace(L"System.DateModified", winrt::box_value(winrt::clock::from_file_time(DateCreatedFileTime)));
File.Properties().SavePropertiesAsync(std::move(PropertyMap)).get();

Now the problem is that runtime behavior is this: E_INVALIDARG 0x80070057 : ‘Cannot write to the specified property (System.DateModified). The property is read-only.’.

That is, you don’t change/save this property from UWP.

Another API exception which should not have been thrown

From the documentation on StorageFolder.TryGetItemAsync(String) Method:

Tries to get the file or folder with the specified name from the current folder. Returns null instead of raising a FileNotFoundException if the specified file or folder is not found.

using IStorageItem = winrt::Windows::Storage::IStorageItem;
IStorageItem EventStorageItem { m_Configuration.m_ApplicationStorageFolder.TryGetItemAsync(m_FileName).get() };

Exception thrown at 0x00007FFB06BDA799 (KernelBase.dll) in DownloadIssue.exe: WinRT originate error – 0x80070002 : ‘An item cannot be found with the specified name (Issue-1714684927.tsv).’.

It looks like TryGetItemAsync is a wrapper over GetItemAsync to suppress exception if file is not found, and it should be the other way around: if there is no file, there is nullptr result with no exception. In conjunction with missing mode equivalent to OPEN_ALWAYS it makes it not really convenient to write code free from exceptions.

Exceptionless workaround via file query:

#if 1
	using namespace winrt::Windows::Storage;
	StorageFile EventStorageFile(nullptr);
	Search::StorageFileQueryResult const FileQueryResult { m_Configuration.m_ApplicationStorageFolder.CreateFileQuery() };
	auto const FileVector { FileQueryResult.GetFilesAsync().get() };
	for(auto FileVectorIterator { FileVector.First() }; FileVectorIterator.HasCurrent(); FileVectorIterator.MoveNext())
	{
		auto const StorageFile { FileVectorIterator.Current() };
		if(StorageFile.IsOfType(StorageItemTypes::File) && _wcsicmp(StorageFile.Name().c_str(), m_FileName.c_str()) == 0)
		{
			EventStorageFile = StorageFile;
			break;
		}
	}
	if(!EventStorageFile)
		return;
#else
	// TODO: Get rid of exception if file is missing
	IStorageItem EventStorageItem { m_Configuration.m_ApplicationStorageFolder.TryGetItemAsync(m_FileName).get() };
	if(!EventStorageItem)
		return;
	StorageFile EventStorageFile { EventStorageItem.as<StorageFile>() };
#endif

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}
D3D_FEATURE_LEVEL_12_0
✔️
  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

Binaries:

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)
LibraryVersion.lib
DLLApi-ms-win-core-version-l1-1-0.dll

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.