mediafoundation: Add mediafoundation frontend

Reviewed-by: <yuboxie@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/34843>
This commit is contained in:
Pohsiang (John) Hsu 2025-05-05 10:43:16 -07:00 committed by Marge Bot
parent 45a2f02876
commit d348fd5fb5
115 changed files with 61925 additions and 2 deletions

View file

@ -694,6 +694,23 @@ if va_drivers_path == ''
va_drivers_path = join_paths(get_option('libdir'), 'dri')
endif
_mediafoundation_drivers = [
with_gallium_d3d12_video,
]
with_gallium_mediafoundation = get_option('gallium-mediafoundation') \
.require(host_machine.system() == 'windows', error_message : 'mediafoundation only supported on Windows') \
.require(_mediafoundation_drivers.contains(true),
error_message : 'Media foundation state tracker requires at least one of the following gallium drivers: d3d12 (with option gallium-d3d12-video).') \
.enabled()
with_gallium_mediafoundation_test = get_option('gallium-mediafoundation-test')
if with_gallium_mediafoundation_test
if not with_gallium_mediafoundation
error('The mediafoundation test requires mediafoundation.')
endif
endif
with_gallium_xa = get_option('gallium-xa') \
.require(system_has_kms_drm, error_message : 'XA state tracker can only be built on unix-like OSes.') \
.require(with_gallium_nouveau or with_gallium_freedreno or with_gallium_i915 or with_gallium_svga,
@ -2378,6 +2395,9 @@ endif
if with_gallium_va
video_apis += 'va'
endif
if with_gallium_mediafoundation
video_apis += 'mediafoundation'
endif
if with_any_vk
video_apis += 'vulkan'
endif
@ -2409,6 +2429,9 @@ if with_gallium
if with_gallium_va
gallium_frontends += 'va'
endif
if with_gallium_mediafoundation
gallium_frontends += 'mediafoundation'
endif
if with_gallium_st_nine
gallium_frontends += 'nine'
endif

View file

@ -117,6 +117,20 @@ option(
description : 'enable gallium va frontend.',
)
option(
'gallium-mediafoundation',
type : 'feature',
deprecated: {'true': 'enabled', 'false': 'disabled'},
description : 'enable gallium mediafoundation frontend.',
)
option(
'gallium-mediafoundation-test',
type : 'boolean',
value : false,
description : 'enable gallium mediafoundation frontend tests.',
)
option(
'va-libs-path',
type : 'string',
@ -178,6 +192,20 @@ option(
'defaults to libgallium_d3d10.dll to match DRI',
)
option(
'mediafoundation-windows-dll-name',
type : 'string',
value : 'mediafoundation_hmft',
description : 'name of gallium mediafoundation DLL built for Windows. ',
)
option(
'mediafoundation-store-dll',
type : 'boolean',
value : 'false',
description : 'Selects whether the gallium mediafoundation DLL is built for the store. ',
)
option(
'static-libclc',
type : 'array',

View file

@ -561,7 +561,7 @@ libgalliumvl = static_library(
# some drivers export their screen creation function globally, so all frontends have to contain the
# full libgalliumvl. So we'll handle this here globally for everybody.
if (with_gallium_va or with_gallium_vdpau or with_dri or with_gallium_radeonsi)
if (with_gallium_mediafoundation or with_gallium_va or with_gallium_vdpau or with_dri or with_gallium_radeonsi)
libgalliumvl_stub = libgalliumvl
else
libgalliumvl_stub = _libgalliumvl_stub

View file

@ -0,0 +1,66 @@
BasedOnStyle: InheritParentConfig
DisableFormat: false
ColumnLimit: 132
SortIncludes: true
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignTrailingComments: true
PenaltyExcessCharacter: 75
AlwaysBreakTemplateDeclarations: Yes
BreakConstructorInitializers: BeforeColon
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 3
IndentCaseLabels: true
BreakBeforeTernaryOperators: false
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterNamespace: true
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: false
BeforeCatch: false
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
FixNamespaceComments: true
KeepEmptyLinesAtTheStartOfBlocks: true
AlwaysBreakAfterReturnType: TopLevel
BinPackArguments: false
BinPackParameters: false
Cpp11BracedListStyle: false
SpaceAfterCStyleCast: true
SpaceBeforeCpp11BracedList: true
SpaceBeforeCtorInitializerColon: true
SpacesInContainerLiterals: true
SpaceBeforeParens: Custom
SpaceBeforeParensOptions:
AfterControlStatements: false
SpacesBeforeTrailingComments: 3
SpacesInAngles: false
SpacesInParentheses: true
SpacesInSquareBrackets: false
SpacesInCStyleCastParentheses: false
SpaceAfterLogicalNot: false
SpaceBeforeAssignmentOperators: true
SpaceInEmptyParentheses: false
SpaceInEmptyBlock: true
MaxEmptyLinesToKeep: 3

View file

@ -0,0 +1,9 @@
# Making changes to IDL files on mediafoundation frontend
When changing any .idl on this folder, these must be recompiled with midlrt to produce the public, private headers and the .winmd files.
## How to build the modified IDL files
```
midlrt.EXE <input_idl_file>.idl /metadata_dir C:\Windows\System32\WinMetadata
```

View file

@ -0,0 +1,321 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* Header file automatically generated from dx12enchmft.idl */
/*
* File built with Microsoft(R) MIDLRT Compiler Engine Version 10.00.0231
*/
#pragma warning( disable: 4049 ) /* more than 64k source lines */
/* verify that the <rpcndr.h> version is high enough to compile this file*/
#ifndef __REQUIRED_RPCNDR_H_VERSION__
#define __REQUIRED_RPCNDR_H_VERSION__ 500
#endif
/* verify that the <rpcsal.h> version is high enough to compile this file*/
#ifndef __REQUIRED_RPCSAL_H_VERSION__
#define __REQUIRED_RPCSAL_H_VERSION__ 100
#endif
#include <rpc.h>
#include <rpcndr.h>
#ifndef __RPCNDR_H_VERSION__
#error this stub requires an updated version of <rpcndr.h>
#endif /* __RPCNDR_H_VERSION__ */
#ifndef COM_NO_WINDOWS_H
#include <windows.h>
#include <ole2.h>
#endif /*COM_NO_WINDOWS_H*/
#ifndef __dx12enchmft_h__
#define __dx12enchmft_h__
#ifndef __dx12enchmft_p_h__
#define __dx12enchmft_p_h__
#pragma once
// Ensure that the setting of the /ns_prefix command line switch is consistent for all headers.
// If you get an error from the compiler indicating "warning C4005: 'CHECK_NS_PREFIX_STATE': macro redefinition", this
// indicates that you have included two different headers with different settings for the /ns_prefix MIDL command line switch
#if !defined(DISABLE_NS_PREFIX_CHECKS)
#define CHECK_NS_PREFIX_STATE "always"
#endif // !defined(DISABLE_NS_PREFIX_CHECKS)
#pragma push_macro("MIDL_CONST_ID")
#undef MIDL_CONST_ID
#define MIDL_CONST_ID const __declspec(selectany)
// API Contract Inclusion Definitions
#if !defined(SPECIFIC_API_CONTRACT_DEFINITIONS)
#if !defined(WINDOWS_APPLICATIONMODEL_ACTIVATION_ACTIVATEDEVENTSCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_ACTIVATION_ACTIVATEDEVENTSCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_APPLICATIONMODEL_ACTIVATION_ACTIVATEDEVENTSCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_ACTIVATION_ACTIVATIONCAMERASETTINGSCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_ACTIVATION_ACTIVATIONCAMERASETTINGSCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_APPLICATIONMODEL_ACTIVATION_ACTIVATIONCAMERASETTINGSCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_ACTIVATION_CONTACTACTIVATEDEVENTSCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_ACTIVATION_CONTACTACTIVATEDEVENTSCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_APPLICATIONMODEL_ACTIVATION_CONTACTACTIVATEDEVENTSCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_ACTIVATION_WEBUISEARCHACTIVATEDEVENTSCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_ACTIVATION_WEBUISEARCHACTIVATEDEVENTSCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_APPLICATIONMODEL_ACTIVATION_WEBUISEARCHACTIVATEDEVENTSCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_BACKGROUND_BACKGROUNDALARMAPPLICATIONCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_BACKGROUND_BACKGROUNDALARMAPPLICATIONCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_APPLICATIONMODEL_BACKGROUND_BACKGROUNDALARMAPPLICATIONCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_CALLS_BACKGROUND_CALLSBACKGROUNDCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_CALLS_BACKGROUND_CALLSBACKGROUNDCONTRACT_VERSION 0x40000
#endif // defined(WINDOWS_APPLICATIONMODEL_CALLS_BACKGROUND_CALLSBACKGROUNDCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_CALLS_CALLSPHONECONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_CALLS_CALLSPHONECONTRACT_VERSION 0x70000
#endif // defined(WINDOWS_APPLICATIONMODEL_CALLS_CALLSPHONECONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_CALLS_CALLSVOIPCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_CALLS_CALLSVOIPCONTRACT_VERSION 0x40000
#endif // defined(WINDOWS_APPLICATIONMODEL_CALLS_CALLSVOIPCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_CALLS_LOCKSCREENCALLCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_CALLS_LOCKSCREENCALLCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_APPLICATIONMODEL_CALLS_LOCKSCREENCALLCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_COMMUNICATIONBLOCKING_COMMUNICATIONBLOCKINGCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_COMMUNICATIONBLOCKING_COMMUNICATIONBLOCKINGCONTRACT_VERSION 0x20000
#endif // defined(WINDOWS_APPLICATIONMODEL_COMMUNICATIONBLOCKING_COMMUNICATIONBLOCKINGCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_FULLTRUSTAPPCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_FULLTRUSTAPPCONTRACT_VERSION 0x20000
#endif // defined(WINDOWS_APPLICATIONMODEL_FULLTRUSTAPPCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_SEARCH_SEARCHCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_SEARCH_SEARCHCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_APPLICATIONMODEL_SEARCH_SEARCHCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_STARTUPTASKCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_STARTUPTASKCONTRACT_VERSION 0x30000
#endif // defined(WINDOWS_APPLICATIONMODEL_STARTUPTASKCONTRACT_VERSION)
#if !defined(WINDOWS_APPLICATIONMODEL_WALLET_WALLETCONTRACT_VERSION)
#define WINDOWS_APPLICATIONMODEL_WALLET_WALLETCONTRACT_VERSION 0x20000
#endif // defined(WINDOWS_APPLICATIONMODEL_WALLET_WALLETCONTRACT_VERSION)
#if !defined(WINDOWS_DEVICES_PRINTERS_EXTENSIONS_EXTENSIONSCONTRACT_VERSION)
#define WINDOWS_DEVICES_PRINTERS_EXTENSIONS_EXTENSIONSCONTRACT_VERSION 0x20000
#endif // defined(WINDOWS_DEVICES_PRINTERS_EXTENSIONS_EXTENSIONSCONTRACT_VERSION)
#if !defined(WINDOWS_DEVICES_SMARTCARDS_SMARTCARDBACKGROUNDTRIGGERCONTRACT_VERSION)
#define WINDOWS_DEVICES_SMARTCARDS_SMARTCARDBACKGROUNDTRIGGERCONTRACT_VERSION 0x30000
#endif // defined(WINDOWS_DEVICES_SMARTCARDS_SMARTCARDBACKGROUNDTRIGGERCONTRACT_VERSION)
#if !defined(WINDOWS_DEVICES_SMARTCARDS_SMARTCARDEMULATORCONTRACT_VERSION)
#define WINDOWS_DEVICES_SMARTCARDS_SMARTCARDEMULATORCONTRACT_VERSION 0x60000
#endif // defined(WINDOWS_DEVICES_SMARTCARDS_SMARTCARDEMULATORCONTRACT_VERSION)
#if !defined(WINDOWS_DEVICES_SMS_LEGACYSMSAPICONTRACT_VERSION)
#define WINDOWS_DEVICES_SMS_LEGACYSMSAPICONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_DEVICES_SMS_LEGACYSMSAPICONTRACT_VERSION)
#if !defined(WINDOWS_FOUNDATION_FOUNDATIONCONTRACT_VERSION)
#define WINDOWS_FOUNDATION_FOUNDATIONCONTRACT_VERSION 0x40000
#endif // defined(WINDOWS_FOUNDATION_FOUNDATIONCONTRACT_VERSION)
#if !defined(WINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION)
#define WINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION 0xf0000
#endif // defined(WINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION)
#if !defined(WINDOWS_GAMING_INPUT_GAMINGINPUTPREVIEWCONTRACT_VERSION)
#define WINDOWS_GAMING_INPUT_GAMINGINPUTPREVIEWCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_GAMING_INPUT_GAMINGINPUTPREVIEWCONTRACT_VERSION)
#if !defined(WINDOWS_GLOBALIZATION_GLOBALIZATIONJAPANESEPHONETICANALYZERCONTRACT_VERSION)
#define WINDOWS_GLOBALIZATION_GLOBALIZATIONJAPANESEPHONETICANALYZERCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_GLOBALIZATION_GLOBALIZATIONJAPANESEPHONETICANALYZERCONTRACT_VERSION)
#if !defined(WINDOWS_MEDIA_CAPTURE_APPBROADCASTCONTRACT_VERSION)
#define WINDOWS_MEDIA_CAPTURE_APPBROADCASTCONTRACT_VERSION 0x20000
#endif // defined(WINDOWS_MEDIA_CAPTURE_APPBROADCASTCONTRACT_VERSION)
#if !defined(WINDOWS_MEDIA_CAPTURE_APPCAPTURECONTRACT_VERSION)
#define WINDOWS_MEDIA_CAPTURE_APPCAPTURECONTRACT_VERSION 0x40000
#endif // defined(WINDOWS_MEDIA_CAPTURE_APPCAPTURECONTRACT_VERSION)
#if !defined(WINDOWS_MEDIA_CAPTURE_APPCAPTUREMETADATACONTRACT_VERSION)
#define WINDOWS_MEDIA_CAPTURE_APPCAPTUREMETADATACONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_MEDIA_CAPTURE_APPCAPTUREMETADATACONTRACT_VERSION)
#if !defined(WINDOWS_MEDIA_CAPTURE_CAMERACAPTUREUICONTRACT_VERSION)
#define WINDOWS_MEDIA_CAPTURE_CAMERACAPTUREUICONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_MEDIA_CAPTURE_CAMERACAPTUREUICONTRACT_VERSION)
#if !defined(WINDOWS_MEDIA_CAPTURE_GAMEBARCONTRACT_VERSION)
#define WINDOWS_MEDIA_CAPTURE_GAMEBARCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_MEDIA_CAPTURE_GAMEBARCONTRACT_VERSION)
#if !defined(WINDOWS_MEDIA_DEVICES_CALLCONTROLCONTRACT_VERSION)
#define WINDOWS_MEDIA_DEVICES_CALLCONTROLCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_MEDIA_DEVICES_CALLCONTROLCONTRACT_VERSION)
#if !defined(WINDOWS_MEDIA_MEDIACONTROLCONTRACT_VERSION)
#define WINDOWS_MEDIA_MEDIACONTROLCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_MEDIA_MEDIACONTROLCONTRACT_VERSION)
#if !defined(WINDOWS_MEDIA_PROTECTION_PROTECTIONRENEWALCONTRACT_VERSION)
#define WINDOWS_MEDIA_PROTECTION_PROTECTIONRENEWALCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_MEDIA_PROTECTION_PROTECTIONRENEWALCONTRACT_VERSION)
#if !defined(WINDOWS_NETWORKING_CONNECTIVITY_WWANCONTRACT_VERSION)
#define WINDOWS_NETWORKING_CONNECTIVITY_WWANCONTRACT_VERSION 0x20000
#endif // defined(WINDOWS_NETWORKING_CONNECTIVITY_WWANCONTRACT_VERSION)
#if !defined(WINDOWS_NETWORKING_SOCKETS_CONTROLCHANNELTRIGGERCONTRACT_VERSION)
#define WINDOWS_NETWORKING_SOCKETS_CONTROLCHANNELTRIGGERCONTRACT_VERSION 0x30000
#endif // defined(WINDOWS_NETWORKING_SOCKETS_CONTROLCHANNELTRIGGERCONTRACT_VERSION)
#if !defined(WINDOWS_PHONE_PHONECONTRACT_VERSION)
#define WINDOWS_PHONE_PHONECONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_PHONE_PHONECONTRACT_VERSION)
#if !defined(WINDOWS_PHONE_PHONEINTERNALCONTRACT_VERSION)
#define WINDOWS_PHONE_PHONEINTERNALCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_PHONE_PHONEINTERNALCONTRACT_VERSION)
#if !defined(WINDOWS_SECURITY_ENTERPRISEDATA_ENTERPRISEDATACONTRACT_VERSION)
#define WINDOWS_SECURITY_ENTERPRISEDATA_ENTERPRISEDATACONTRACT_VERSION 0x50000
#endif // defined(WINDOWS_SECURITY_ENTERPRISEDATA_ENTERPRISEDATACONTRACT_VERSION)
#if !defined(WINDOWS_STORAGE_PROVIDER_CLOUDFILESCONTRACT_VERSION)
#define WINDOWS_STORAGE_PROVIDER_CLOUDFILESCONTRACT_VERSION 0x70000
#endif // defined(WINDOWS_STORAGE_PROVIDER_CLOUDFILESCONTRACT_VERSION)
#if !defined(WINDOWS_SYSTEM_SYSTEMMANAGEMENTCONTRACT_VERSION)
#define WINDOWS_SYSTEM_SYSTEMMANAGEMENTCONTRACT_VERSION 0x70000
#endif // defined(WINDOWS_SYSTEM_SYSTEMMANAGEMENTCONTRACT_VERSION)
#if !defined(WINDOWS_UI_CORE_COREWINDOWDIALOGSCONTRACT_VERSION)
#define WINDOWS_UI_CORE_COREWINDOWDIALOGSCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_UI_CORE_COREWINDOWDIALOGSCONTRACT_VERSION)
#if !defined(WINDOWS_UI_VIEWMANAGEMENT_VIEWMANAGEMENTVIEWSCALINGCONTRACT_VERSION)
#define WINDOWS_UI_VIEWMANAGEMENT_VIEWMANAGEMENTVIEWSCALINGCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_UI_VIEWMANAGEMENT_VIEWMANAGEMENTVIEWSCALINGCONTRACT_VERSION)
#if !defined(WINDOWS_UI_WEBUI_CORE_WEBUICOMMANDBARCONTRACT_VERSION)
#define WINDOWS_UI_WEBUI_CORE_WEBUICOMMANDBARCONTRACT_VERSION 0x10000
#endif // defined(WINDOWS_UI_WEBUI_CORE_WEBUICOMMANDBARCONTRACT_VERSION)
#endif // defined(SPECIFIC_API_CONTRACT_DEFINITIONS)
// Header files for imported files
#include "Windows.Media.h"
#if defined(__cplusplus) && !defined(CINTERFACE)
/* Forward Declarations */
#pragma warning (push)
#pragma warning (disable:4668)
#pragma warning (disable:4001)
#pragma once
#pragma warning (pop)
namespace ABI {
namespace DX12Encoder {
class CDX12EncHMFT;
} /* DX12Encoder */
} /* ABI */
#ifndef ____x_ABI_CWindows_CMedia_CIMediaExtension_FWD_DEFINED__
#define ____x_ABI_CWindows_CMedia_CIMediaExtension_FWD_DEFINED__
namespace ABI {
namespace Windows {
namespace Media {
interface IMediaExtension;
} /* Media */
} /* Windows */
} /* ABI */
#define __x_ABI_CWindows_CMedia_CIMediaExtension ABI::Windows::Media::IMediaExtension
#endif // ____x_ABI_CWindows_CMedia_CIMediaExtension_FWD_DEFINED__
/*
*
* Class DX12Encoder.CDX12EncHMFT
*
* Class implements the following interfaces:
* Windows.Media.IMediaExtension ** Default Interface **
*
*/
#ifndef RUNTIMECLASS_DX12Encoder_CDX12EncHMFT_DEFINED
#define RUNTIMECLASS_DX12Encoder_CDX12EncHMFT_DEFINED
extern const __declspec(selectany) _Null_terminated_ WCHAR RuntimeClass_DX12Encoder_CDX12EncHMFT[] = L"DX12Encoder.CDX12EncHMFT";
#endif
#else // !defined(__cplusplus)
/* Forward Declarations */
#pragma warning (push)
#pragma warning (disable:4668)
#pragma warning (disable:4001)
#pragma once
#pragma warning (pop)
#ifndef ____x_ABI_CWindows_CMedia_CIMediaExtension_FWD_DEFINED__
#define ____x_ABI_CWindows_CMedia_CIMediaExtension_FWD_DEFINED__
typedef interface __x_ABI_CWindows_CMedia_CIMediaExtension __x_ABI_CWindows_CMedia_CIMediaExtension;
#endif // ____x_ABI_CWindows_CMedia_CIMediaExtension_FWD_DEFINED__
/*
*
* Class DX12Encoder.CDX12EncHMFT
*
* Class implements the following interfaces:
* Windows.Media.IMediaExtension ** Default Interface **
*
*/
#ifndef RUNTIMECLASS_DX12Encoder_CDX12EncHMFT_DEFINED
#define RUNTIMECLASS_DX12Encoder_CDX12EncHMFT_DEFINED
extern const __declspec(selectany) _Null_terminated_ WCHAR RuntimeClass_DX12Encoder_CDX12EncHMFT[] = L"DX12Encoder.CDX12EncHMFT";
#endif
#endif // defined(__cplusplus)
#pragma pop_macro("MIDL_CONST_ID")
#endif // __dx12enchmft_p_h__
#endif // __dx12enchmft_h__

View file

@ -0,0 +1,35 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import "Windows.Media.idl";
#include <sdkddkver.h>
namespace DX12Encoder
{
[version(NTDDI_WIN8)]
runtimeclass CDX12EncHMFT
{
[default] interface Windows.Media.IMediaExtension;
}
}

View file

@ -0,0 +1,106 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* this ALWAYS GENERATED file contains the definitions for the interfaces */
/* File created by MIDL compiler version 8.01.0628 */
/* at Mon Jan 18 19:14:07 2038
*/
/* Compiler settings for C:\Users\BRIANK~1\AppData\Local\Temp\dx12enchmft.idl-71af067c:
Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0628
protocol : dce , ms_ext, c_ext, robust
error checks: allocation ref bounds_check enum stub_data
VC __declspec() decoration level:
__declspec(uuid()), __declspec(selectany), __declspec(novtable)
DECLSPEC_UUID(), MIDL_INTERFACE()
*/
/* @@MIDL_FILE_HEADING( ) */
#pragma warning( disable: 4049 ) /* more than 64k source lines */
/* verify that the <rpcndr.h> version is high enough to compile this file*/
#ifndef __REQUIRED_RPCNDR_H_VERSION__
#define __REQUIRED_RPCNDR_H_VERSION__ 475
#endif
#include "rpc.h"
#include "rpcndr.h"
#ifndef __RPCNDR_H_VERSION__
#error this stub requires an updated version of <rpcndr.h>
#endif /* __RPCNDR_H_VERSION__ */
#ifndef __dx12enchmft_p_h__
#define __dx12enchmft_p_h__
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif
#ifndef DECLSPEC_XFGVIRT
#if defined(_CONTROL_FLOW_GUARD_XFG)
#define DECLSPEC_XFGVIRT(base, func) __declspec(xfg_virtual(base, func))
#else
#define DECLSPEC_XFGVIRT(base, func)
#endif
#endif
/* Forward Declarations */
/* header files for imported files */
#include "Windows.Media.h"
#ifdef __cplusplus
extern "C"{
#endif
/* interface __MIDL_itf_dx12enchmft_0000_0000 */
/* [local] */
#pragma warning (push)
#pragma warning (disable:4668)
#pragma warning (disable:4001)
#pragma once
#pragma warning (pop)
extern RPC_IF_HANDLE __MIDL_itf_dx12enchmft_0000_0000_v0_0_c_ifspec;
extern RPC_IF_HANDLE __MIDL_itf_dx12enchmft_0000_0000_v0_0_s_ifspec;
/* Additional Prototypes for ALL interfaces */
/* end of Additional Prototypes */
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,192 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include <Unknwn.h>
#include "vl/vl_winsys.h"
#include "macros.h"
#include "mfpipeinterop.h"
#include "reference_frames_tracker.h"
typedef class DX12EncodeContext
{
public:
ComPtr<IMFSample> spSample;
void *pAsyncCookie = nullptr;
reference_frames_tracker_dpb_async_token *pAsyncDPBToken = nullptr;
struct pipe_fence_handle *pAsyncFence = NULL;
std::vector<struct pipe_resource *> pOutputBitRes;
std::vector<struct pipe_fence_handle *> pSliceFences;
#if (USE_D3D12_PREVIEW_HEADERS && (D3D12_PREVIEW_SDK_VERSION >= 716))
D3D12_VIDEO_ENCODER_COMPRESSED_BITSTREAM_NOTIFICATION_MODE sliceNotificationMode =
D3D12_VIDEO_ENCODER_COMPRESSED_BITSTREAM_NOTIFICATION_MODE_FULL_FRAME;
#endif // (USE_D3D12_PREVIEW_HEADERS && (D3D12_PREVIEW_SDK_VERSION >= 716))
pipe_resource *pPipeResourceQPMapStats = nullptr;
pipe_resource *pPipeResourceSATDMapStats = nullptr;
pipe_resource *pPipeResourceRCBitAllocMapStats = nullptr;
// Keep all the media and sync objects until encode is done
// and then signal EnqueueResourceRelease so the media
// producer (e.g decoder) can reuse the buffer in their pool
pipe_video_buffer *pPipeVideoBuffer = nullptr;
ComPtr<IMFMediaBuffer> spMediaBuffer;
ComPtr<IMFD3D12SynchronizationObjectCommands> spSyncObjectCommands;
ID3D12CommandQueue *pSyncObjectQueue = nullptr; // weakref
UINT textureWidth = 0; // width of input sample
UINT textureHeight = 0; // height of input sample
BOOL bROI = FALSE;
ROI_AREA video_roi_area = {};
UINT longTermReferenceFrameInfo = 0x0000FFFF; // corresponds to MFT attribute MFSampleExtension_LongTermReferenceFrameInfo
struct vl_screen *pVlScreen = nullptr; // weakref
struct pipe_video_codec encoderSettings = {};
union
{
struct pipe_picture_desc base;
struct pipe_h264_enc_picture_desc h264enc;
struct pipe_h265_enc_picture_desc h265enc;
struct pipe_av1_enc_picture_desc av1enc;
} encoderPicInfo = {};
const D3D12_VIDEO_ENCODER_CODEC m_Codec = D3D12_VIDEO_ENCODER_CODEC_H264;
UINT32 GetPictureType()
{
UINT32 result = 0;
switch( m_Codec )
{
case D3D12_VIDEO_ENCODER_CODEC_H264:
result = ConvertPictureTypeToAVEncH264PictureType( encoderPicInfo.h264enc.picture_type );
break;
case D3D12_VIDEO_ENCODER_CODEC_HEVC:
result = ConvertPictureTypeToAVEncH264PictureType( encoderPicInfo.h265enc.picture_type );
break;
case D3D12_VIDEO_ENCODER_CODEC_AV1:
// TODO: ConvertPictureTypeToAVEncAV1PictureType(pDX12EncodeContext->encoderPicInfo.av1enc.picture_type);
result = (UINT32) eAVEncAV1PictureType_Key;
break;
}
return result;
}
BOOL IsPicTypeCleanPoint()
{
BOOL result = FALSE;
switch( m_Codec )
{
case D3D12_VIDEO_ENCODER_CODEC_H264:
if( ( encoderPicInfo.h264enc.picture_type == PIPE_H2645_ENC_PICTURE_TYPE_I ) ||
( encoderPicInfo.h264enc.picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR ) )
result = TRUE;
break;
case D3D12_VIDEO_ENCODER_CODEC_HEVC:
if( ( encoderPicInfo.h265enc.picture_type == PIPE_H2645_ENC_PICTURE_TYPE_I ) ||
( encoderPicInfo.h265enc.picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR ) )
result = TRUE;
break;
case D3D12_VIDEO_ENCODER_CODEC_AV1:
if( encoderPicInfo.av1enc.frame_type == PIPE_AV1_ENC_FRAME_TYPE_KEY )
result = TRUE;
break;
}
return result;
}
UINT32 GetFrameRateNumerator()
{
UINT32 result = 0;
switch( m_Codec )
{
case D3D12_VIDEO_ENCODER_CODEC_H264:
result = encoderPicInfo.h264enc.rate_ctrl[0].frame_rate_num;
break;
case D3D12_VIDEO_ENCODER_CODEC_HEVC:
result = encoderPicInfo.h265enc.rc[0].frame_rate_num;
break;
case D3D12_VIDEO_ENCODER_CODEC_AV1:
result = encoderPicInfo.av1enc.rc[0].frame_rate_num;
break;
}
return result;
}
UINT32 GetFrameRateDenominator()
{
UINT32 result = 0;
switch( m_Codec )
{
case D3D12_VIDEO_ENCODER_CODEC_H264:
result = encoderPicInfo.h264enc.rate_ctrl[0].frame_rate_den;
break;
case D3D12_VIDEO_ENCODER_CODEC_HEVC:
result = encoderPicInfo.h265enc.rc[0].frame_rate_den;
break;
case D3D12_VIDEO_ENCODER_CODEC_AV1:
result = encoderPicInfo.av1enc.rc[0].frame_rate_den;
break;
}
return result;
}
DX12EncodeContext( D3D12_VIDEO_ENCODER_CODEC codec ) : m_Codec( codec ) { };
~DX12EncodeContext()
{
if( spSyncObjectCommands )
spSyncObjectCommands->EnqueueResourceRelease( pSyncObjectQueue );
switch( m_Codec )
{
case D3D12_VIDEO_ENCODER_CODEC_H264:
util_dynarray_foreach ( &encoderPicInfo.h264enc.raw_headers, struct pipe_enc_raw_header, header )
delete header->buffer;
util_dynarray_fini( &encoderPicInfo.h264enc.raw_headers );
break;
case D3D12_VIDEO_ENCODER_CODEC_HEVC:
util_dynarray_foreach ( &encoderPicInfo.h265enc.raw_headers, struct pipe_enc_raw_header, header )
delete header->buffer;
util_dynarray_fini( &encoderPicInfo.h265enc.raw_headers );
break;
case D3D12_VIDEO_ENCODER_CODEC_AV1:
util_dynarray_foreach ( &encoderPicInfo.av1enc.raw_headers, struct pipe_enc_raw_header, header )
delete header->buffer;
util_dynarray_fini( &encoderPicInfo.av1enc.raw_headers );
break;
}
for( uint32_t slice_idx = 0; slice_idx < static_cast<uint32_t>( pOutputBitRes.size() ); slice_idx++ )
if( ( ( slice_idx == 0 ) || pOutputBitRes[slice_idx] != pOutputBitRes[slice_idx - 1] ) && pOutputBitRes[slice_idx] )
pVlScreen->pscreen->resource_destroy( pVlScreen->pscreen, pOutputBitRes[slice_idx] );
if( pPipeResourceQPMapStats )
pVlScreen->pscreen->resource_destroy( pVlScreen->pscreen, pPipeResourceQPMapStats );
if( pPipeResourceSATDMapStats )
pVlScreen->pscreen->resource_destroy( pVlScreen->pscreen, pPipeResourceSATDMapStats );
if( pPipeResourceRCBitAllocMapStats )
pVlScreen->pscreen->resource_destroy( pVlScreen->pscreen, pPipeResourceRCBitAllocMapStats );
if( pPipeVideoBuffer )
pPipeVideoBuffer->destroy( pPipeVideoBuffer );
}
} *LPDX12EncodeContext;

View file

@ -0,0 +1,73 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "dpb_buffer_manager.h"
// retrieve a buffer from the pool
struct pipe_video_buffer *
dpb_buffer_manager::get_fresh_dpb_buffer()
{
for( auto &entry : m_pool )
{
if( !entry.used )
{
entry.used = true;
return entry.buffer;
}
}
assert( false ); // Did not find an unused buffer
return NULL;
}
// release a buffer back to the pool
void
dpb_buffer_manager::release_dpb_buffer( struct pipe_video_buffer *target )
{
for( auto &entry : m_pool )
{
if( entry.buffer == target )
{
entry.used = false;
break;
}
}
}
dpb_buffer_manager::dpb_buffer_manager(
struct pipe_video_codec *codec, unsigned width, unsigned height, enum pipe_format buffer_format, unsigned pool_size )
: m_codec( codec ), m_pool( pool_size, { NULL, false } )
{
m_template.width = width;
m_template.height = height;
m_template.buffer_format = buffer_format;
for( auto &entry : m_pool )
entry.buffer = m_codec->create_dpb_buffer( m_codec, NULL, &m_template );
}
dpb_buffer_manager::~dpb_buffer_manager()
{
for( auto &entry : m_pool )
entry.buffer->destroy( entry.buffer );
}

View file

@ -0,0 +1,53 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include <vector>
#include "pipe_headers.h"
class dpb_buffer_manager
{
public:
dpb_buffer_manager(
struct pipe_video_codec *codec, unsigned width, unsigned height, enum pipe_format buffer_format, unsigned pool_size );
~dpb_buffer_manager();
// retrieve a buffer from the pool
struct pipe_video_buffer *get_fresh_dpb_buffer();
// release a buffer back to the pool
void release_dpb_buffer( struct pipe_video_buffer *target );
private:
struct pipe_video_codec *m_codec = NULL;
struct pipe_video_buffer m_template = {};
struct dpb_buffer_manager_pool_entry
{
struct pipe_video_buffer *buffer;
bool used;
};
std::vector<struct dpb_buffer_manager_pool_entry> m_pool;
};

View file

@ -0,0 +1,467 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "hmft_entrypoints.h"
#include "mfbufferhelp.h"
#include "mfpipeinterop.h"
#include "wpptrace.h"
#include "encode.tmh"
// internal function to prepare the data needed to pass to DX12 encoder via DX12EncoderContext
// this include converting the input sample to a DX12 accepted sample. Note that codec specific
// handling is forward to PrepareForEncodeHelper
HRESULT
CDX12EncHMFT::PrepareForEncode( IMFSample *pSample, LPDX12EncodeContext *ppDX12EncodeContext )
{
HRESULT hr = S_OK;
UINT unDiscontinuity = 0;
LPDX12EncodeContext pDX12EncodeContext;
UINT uiSubresourceIndex = 0;
UINT textureWidth = 0u;
UINT textureHeight = 0u;
bool bReceivedDirtyRectBlob = false;
uint32_t dirtyRectFrameNum = UINT32_MAX;
ComPtr<IMFDXGIBuffer> spDXGIBuffer;
HANDLE hTexture = NULL;
winsys_handle winsysHandle = {};
ROI_AREA video_roi_area = {};
UINT32 uiROIBlobOutSize = 0;
// Get HW Support Surface Alignment to check against input sample
const uint32_t surfaceWidthAlignment = 1 << m_EncoderCapabilities.m_HWSupportSurfaceAlignment.bits.log2_width_alignment;
const uint32_t surfaceHeightAlignment = 1 << m_EncoderCapabilities.m_HWSupportSurfaceAlignment.bits.log2_width_alignment;
// Check for Discontinuity
(void) pSample->GetUINT32( MFSampleExtension_Discontinuity, &unDiscontinuity );
if( unDiscontinuity )
{
MFE_INFO( "[dx12 hmft 0x%p] Discontinuity signaled on input sample", this );
m_bForceKeyFrame = TRUE;
}
CHECKNULL_GOTO( pDX12EncodeContext = new DX12EncodeContext( m_Codec ), E_OUTOFMEMORY, done );
CHECKNULL_GOTO( pDX12EncodeContext->pAsyncDPBToken = new reference_frames_tracker_dpb_async_token(), E_OUTOFMEMORY, done );
CHECKHR_GOTO( pSample->GetBufferByIndex( 0, &pDX12EncodeContext->spMediaBuffer ), done );
// If we can't get a DXGIBuffer out of this incoming buffer, then its a software-based buffer
if( FAILED( pDX12EncodeContext->spMediaBuffer.As( &spDXGIBuffer ) ) )
{
ComPtr<IMFSample> spSample;
ComPtr<IMFMediaBuffer> spBuffer;
// Allocate a video buffer
CHECKHR_GOTO( m_spVideoSampleAllocator->AllocateSample( &spSample ), done );
CHECKHR_GOTO( MFCopySample( spSample.Get(), pSample, m_spInputType.Get() ), done );
CHECKHR_GOTO( spSample->GetBufferByIndex( 0, &pDX12EncodeContext->spMediaBuffer ), done );
CHECKHR_GOTO( pDX12EncodeContext->spMediaBuffer.As( &spDXGIBuffer ), done );
debug_printf( "[dx12 hmft 0x%p] Software input sample\n", this );
}
CHECKHR_GOTO( spDXGIBuffer->GetSubresourceIndex( &uiSubresourceIndex ), done );
if( m_spDevice11 )
{
// D3D11 input sample path
ComPtr<IDXGIResource1> spDXGIResource1;
ComPtr<ID3D11Texture2D> spTexture;
ComPtr<ID3D11DeviceContext3> spDeviceContext3;
ComPtr<ID3D11DeviceContext4> spDeviceContext4;
D3D11_TEXTURE2D_DESC d3d11textureDescSrc;
D3D11_TEXTURE2D_DESC d3d11textureDescDst;
CHECKHR_GOTO( spDXGIBuffer->GetResource( IID_PPV_ARGS( &spTexture ) ), done );
D3D11_TEXTURE2D_DESC desc = {};
spTexture->GetDesc( &desc );
textureWidth = desc.Width;
textureHeight = desc.Height;
CHECKHR_GOTO( spTexture.As( &spDXGIResource1 ), done );
spTexture->GetDesc( &d3d11textureDescSrc );
if( SUCCEEDED( spDXGIResource1->CreateSharedHandle( nullptr, DXGI_SHARED_RESOURCE_READ, nullptr, &hTexture ) ) )
{
// If the CreateSharedHandle() call works, then the DX11 texture being given to us was created with sharing
// ability. This will simplify our live as we can map it more easily into DX12 space on the same device, and
// then use it.
CHECKBOOL_GOTO( uiSubresourceIndex == 0,
MF_E_UNEXPECTED,
done ); // video_buffer_from_handle expects data to be on first subresource (e.g no texture array)
m_spDevice11->GetImmediateContext3( &spDeviceContext3 );
CHECKHR_GOTO( spDeviceContext3.As( &spDeviceContext4 ), done );
// This will signal the staging fence the d3d12 mesa backend is consuming
spDeviceContext4->Signal( m_spStagingFence11.Get(), m_SyncFenceValue );
debug_printf( "[dx12 hmft 0x%p] DX11 *shared* input sample\n", this );
}
else
{
// We need to create a shareable texture and copy into it
ComPtr<ID3D11Texture2D> spSharedTexture;
D3D11_BOX d3d11Box = { 0 };
d3d11textureDescDst = d3d11textureDescSrc;
d3d11textureDescDst.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED;
d3d11textureDescDst.BindFlags = D3D11_BIND_SHADER_RESOURCE;
d3d11textureDescDst.ArraySize = 1;
d3d11textureDescDst.Width = textureWidth;
d3d11textureDescDst.Height = textureHeight;
CHECKHR_GOTO( m_spDevice11->CreateTexture2D( &d3d11textureDescDst, nullptr, &spSharedTexture ), done );
m_spDevice11->GetImmediateContext3( &spDeviceContext3 );
d3d11Box.right = d3d11textureDescSrc.Width;
d3d11Box.bottom = d3d11textureDescSrc.Height;
d3d11Box.back = 1;
spDeviceContext3
->CopySubresourceRegion( spSharedTexture.Get(), 0, 0, 0, 0, spTexture.Get(), uiSubresourceIndex, &d3d11Box );
// This will signal the staging fence the d3d12 mesa backend is consuming
// Since we're signaling from the D3D11 context on a shared fence, the signal
// will happen after the d3d11 context copy is done.
CHECKHR_GOTO( spDeviceContext3.As( &spDeviceContext4 ), done );
spDeviceContext4->Signal( m_spStagingFence11.Get(), m_SyncFenceValue );
CHECKHR_GOTO( spSharedTexture.As( &spDXGIResource1 ), done );
CHECKHR_GOTO( spDXGIResource1->CreateSharedHandle( nullptr, DXGI_SHARED_RESOURCE_READ, nullptr, &hTexture ), done );
debug_printf( "[dx12 hmft 0x%p] DX11 input sample\n", this );
}
// We have an hTexture from one of the two paths above
winsysHandle.handle = hTexture;
winsysHandle.type = WINSYS_HANDLE_TYPE_FD;
CHECKNULL_GOTO(
pDX12EncodeContext->pPipeVideoBuffer = m_pPipeContext->video_buffer_from_handle( m_pPipeContext, NULL, &winsysHandle, 0 ),
MF_E_UNEXPECTED,
done );
}
else
{
// D3D12 input sample path
ComPtr<ID3D12Resource> spResource;
CHECKHR_GOTO( spDXGIBuffer->GetResource( IID_PPV_ARGS( &spResource ) ), done );
const D3D12_RESOURCE_DESC desc = spResource->GetDesc();
textureWidth = static_cast<UINT>( desc.Width );
textureHeight = static_cast<UINT>( desc.Height );
CHECKHR_GOTO(
spDXGIBuffer->GetUnknown( MF_D3D12_SYNCHRONIZATION_OBJECT, IID_PPV_ARGS( &pDX12EncodeContext->spSyncObjectCommands ) ),
done );
CHECKHR_GOTO( pDX12EncodeContext->spSyncObjectCommands->EnqueueResourceReadyWait( m_spStagingQueue.Get() ), done );
pDX12EncodeContext->pSyncObjectQueue = m_spStagingQueue.Get();
// This will signal the staging fence the d3d12 mesa backend is consuming
// Since we have a Wait() on spStagingQueue added by EnqueueResourceReadyWait, this will only happen after MF
// triggered completion on the input
m_spStagingQueue->Signal( m_spStagingFence12.Get(), m_SyncFenceValue );
winsysHandle.com_obj = spResource.Get();
winsysHandle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
CHECKBOOL_GOTO( uiSubresourceIndex == 0,
MF_E_UNEXPECTED,
done ); // video_buffer_from_handle expects data to be on first subresource (e.g no texture array)
CHECKNULL_GOTO(
pDX12EncodeContext->pPipeVideoBuffer = m_pPipeContext->video_buffer_from_handle( m_pPipeContext, NULL, &winsysHandle, 0 ),
MF_E_UNEXPECTED,
done );
debug_printf( "[dx12 hmft 0x%p] DX12 input sample\n", this );
}
// validate texture dimensions with surface alignment here for now, will add handling for non-aligned textures later
if( textureWidth % surfaceWidthAlignment != 0 || textureHeight % surfaceHeightAlignment != 0 )
{
assert( false );
}
pDX12EncodeContext->textureWidth = textureWidth;
pDX12EncodeContext->textureHeight = textureHeight;
if( m_uiDirtyRectEnabled )
{
UINT32 cBlob = 0;
pSample->GetBlobSize( MFSampleExtension_DirtyRects, &cBlob );
if( cBlob >= sizeof( DIRTYRECT_INFO ) )
{
if( m_pDirtyRectBlob.size() < cBlob )
{
m_pDirtyRectBlob.resize( cBlob );
}
if( S_OK == pSample->GetBlob( MFSampleExtension_DirtyRects, m_pDirtyRectBlob.data(), cBlob, &cBlob ) )
{
DIRTYRECT_INFO *pDirtyRectInfo = (DIRTYRECT_INFO *) m_pDirtyRectBlob.data();
dirtyRectFrameNum = pDirtyRectInfo->FrameNumber;
bReceivedDirtyRectBlob = true;
}
}
}
if( m_pGOPTracker == nullptr )
{
CHECKHR_GOTO( CreateGOPTracker( textureWidth, textureHeight ), done );
}
{
bool markLTR = false;
bool useLTR = false;
uint32_t markLTRindex = 0;
uint32_t useLTRbitmap = 0;
if( m_uiMaxLongTermReferences > 0 )
{
if( m_bMarkLTRFrameSet )
{
markLTR = true;
markLTRindex = m_uiMarkLTRFrame;
assert( m_uiMarkLTRFrame < m_uiMaxLongTermReferences ); // TODO: add check at CodecAPI level
m_bMarkLTRFrameSet = FALSE;
}
if( m_bUseLTRFrameSet )
{
useLTR = true;
useLTRbitmap = m_uiUseLTRFrame;
m_bUseLTRFrameSet = FALSE;
}
}
m_pGOPTracker->begin_frame( pDX12EncodeContext->pAsyncDPBToken,
m_bForceKeyFrame,
markLTR,
markLTRindex,
useLTR,
useLTRbitmap,
m_bLayerCountSet,
m_uiLayerCount,
bReceivedDirtyRectBlob,
dirtyRectFrameNum );
if( m_bForceKeyFrame )
{
m_bForceKeyFrame = FALSE;
}
}
//
// TODO: Just to test the backend, needs proper plumbing to CodecAPI
//
#if 0 // TODO: Enable me
{
//
// Create resources for output GPU frame stats
//
struct pipe_resource templ = {};
memset(&templ, 0, sizeof(templ));
templ.target = PIPE_TEXTURE_2D;
// PIPE_USAGE_STAGING allocates resource in L0 (System Memory) heap
// and avoid a bunch of roundtrips for uploading/reading back the bitstream headers
// The GPU writes once the slice data (if dGPU over the PCIe bus) and all the other
// uploads (e.g bitstream headers from CPU) and readbacks to output MFSamples
// happen without moving data between L0/L1 pools
templ.usage = PIPE_USAGE_DEFAULT;
templ.depth0 = 1;
templ.array_size = 1;
// TODO: Only allocate these if CodecAPi requested these stats, since there's a perf impact to request them in DX12 driver
if (m_EncoderCapabilities.m_HWSupportStatsQPMapOutput.bits.supported)
{
uint32_t block_size = (1 << m_EncoderCapabilities.m_HWSupportStatsQPMapOutput.bits.log2_values_block_size);
templ.format = (enum pipe_format) m_EncoderCapabilities.m_HWSupportStatsQPMapOutput.bits.pipe_pixel_format;
templ.width0 = static_cast<uint32_t>(std::ceil(alignedWidth / static_cast<float>(block_size)));
templ.height0 = static_cast<uint32_t>(std::ceil(alignedHeight / static_cast<float>(block_size)));
CHECKNULL_GOTO(
pDX12EncodeContext->pPipeResourceQPMapStats = m_pVlScreen->pscreen->resource_create(m_pVlScreen->pscreen, &templ),
E_OUTOFMEMORY,
done);
}
if (m_EncoderCapabilities.m_HWSupportStatsSATDMapOutput.bits.supported)
{
uint32_t block_size = (1 << m_EncoderCapabilities.m_HWSupportStatsSATDMapOutput.bits.log2_values_block_size);
templ.format = (enum pipe_format) m_EncoderCapabilities.m_HWSupportStatsSATDMapOutput.bits.pipe_pixel_format;
templ.width0 = static_cast<uint32_t>(std::ceil(alignedWidth / static_cast<float>(block_size)));
templ.height0 = static_cast<uint32_t>(std::ceil(alignedHeight / static_cast<float>(block_size)));
CHECKNULL_GOTO(
pDX12EncodeContext->pPipeResourceSATDMapStats = m_pVlScreen->pscreen->resource_create(m_pVlScreen->pscreen, &templ),
E_OUTOFMEMORY,
done);
}
if (m_EncoderCapabilities.m_HWSupportStatsRCBitAllocationMapOutput.bits.supported)
{
uint32_t block_size = (1 << m_EncoderCapabilities.m_HWSupportStatsRCBitAllocationMapOutput.bits.log2_values_block_size);
templ.format = (enum pipe_format) m_EncoderCapabilities.m_HWSupportStatsRCBitAllocationMapOutput.bits.pipe_pixel_format;
templ.width0 = static_cast<uint32_t>(std::ceil(alignedWidth / static_cast<float>(block_size)));
templ.height0 = static_cast<uint32_t>(std::ceil(alignedHeight / static_cast<float>(block_size)));
CHECKNULL_GOTO(
pDX12EncodeContext->pPipeResourceRCBitAllocMapStats = m_pVlScreen->pscreen->resource_create(m_pVlScreen->pscreen, &templ),
E_OUTOFMEMORY,
done);
}
}
#endif
memset( &pDX12EncodeContext->encoderPicInfo, 0, sizeof( pDX12EncodeContext->encoderPicInfo ) );
pDX12EncodeContext->encoderPicInfo.base.profile = m_outputPipeProfile;
// Encode region of interest
// When m_bVideoROIEnabled, app can (or not) set MFSampleExtension_ROIRectangle on separate frames optionally
if( m_bVideoROIEnabled )
{
pSample->GetBlob( MFSampleExtension_ROIRectangle,
(UINT8 *) &pDX12EncodeContext->video_roi_area,
sizeof( ROI_AREA ),
&uiROIBlobOutSize );
if( uiROIBlobOutSize > 0 )
{
// Check the Blob size matches the struct size we expect
CHECKBOOL_GOTO( uiROIBlobOutSize == sizeof( ROI_AREA ), MF_E_UNEXPECTED, done );
// When requested QPDelta == 0, just don't enable roi since it won't have any effect
if( video_roi_area.QPDelta != 0 )
{
// Check for hardware support for delta QP
CHECKBOOL_GOTO( m_EncoderCapabilities.m_HWSupportsVideoEncodeROI.bits.roi_rc_qp_delta_support == 1,
MF_E_UNEXPECTED,
done );
pDX12EncodeContext->bROI = TRUE;
}
}
}
pDX12EncodeContext->pVlScreen = m_pVlScreen; // weakref
// Call the helper for encoder specific work
CHECKHR_GOTO( PrepareForEncodeHelper( pDX12EncodeContext, bReceivedDirtyRectBlob, dirtyRectFrameNum ), done );
{
struct pipe_resource templ = {};
memset( &templ, 0, sizeof( templ ) );
// Prefer using sliced buffers + fence notifications when supported to reduce latency if
// user requested multiple slices
// Otherwise fallback to full frame encoding fence notification using a single output buffer
uint32_t num_output_buffers = 1u;
#if VIDEO_CODEC_H264ENC
num_output_buffers = std::max( 1u, pDX12EncodeContext->encoderPicInfo.h264enc.num_slice_descriptors );
#elif VIDEO_CODEC_H265ENC
num_output_buffers = std::max( 1u, pDX12EncodeContext->encoderPicInfo.h265enc.num_slice_descriptors );
#elif VIDEO_CODEC_AV1ENC
num_output_buffers =
std::max( 1u, pDX12EncodeContext->encoderPicInfo.av1enc.tile_rows * pDX12EncodeContext->encoderPicInfo.av1enc.tile_cols );
#endif
#if (USE_D3D12_PREVIEW_HEADERS && (D3D12_PREVIEW_SDK_VERSION >= 716))
pDX12EncodeContext->sliceNotificationMode = D3D12_VIDEO_ENCODER_COMPRESSED_BITSTREAM_NOTIFICATION_MODE_FULL_FRAME;
if( m_EncoderCapabilities.m_HWSupportSlicedFences.bits.supported && ( num_output_buffers > 1 ) )
{
pDX12EncodeContext->sliceNotificationMode = D3D12_VIDEO_ENCODER_COMPRESSED_BITSTREAM_NOTIFICATION_MODE_SUBREGIONS;
if( m_EncoderCapabilities.m_HWSupportSlicedFences.bits.multiple_buffers_required )
{
// Buffer byte size for sliced buffers + notifications with multiple individual buffers per slice
templ.width0 = ( 1024 /*1K*/ * 1024 /*1MB*/ ) * 8 /*8 MB*/;
}
else
{
// Buffer byte size for sliced buffers + notifications with a single buffer (suballocated by driver for each slice)
templ.width0 = ( 1024 /*1K*/ * 1024 /*1MB*/ ) * 8 /*8 MB*/;
}
}
else
#endif // (USE_D3D12_PREVIEW_HEADERS && (D3D12_PREVIEW_SDK_VERSION >= 716))
{
// Buffer byte size for full frame bitstream (when num_output_buffers == 1)
templ.width0 = ( 1024 /*1K*/ * 1024 /*1MB*/ ) * 8 /*8 MB*/;
}
templ.target = PIPE_BUFFER;
// PIPE_USAGE_STAGING allocates resource in L0 (System Memory) heap
// and avoid a bunch of roundtrips for uploading/reading back the bitstream headers
// The GPU writes once the slice data (if dGPU over the PCIe bus) and all the other
// uploads (e.g bitstream headers from CPU) and readbacks to output MFSamples
// happen without moving data between L0/L1 pools
templ.usage = PIPE_USAGE_STAGING;
templ.format = PIPE_FORMAT_R8_UINT;
templ.height0 = 1;
templ.depth0 = 1;
templ.array_size = 1;
pDX12EncodeContext->pOutputBitRes.resize( num_output_buffers, NULL );
pDX12EncodeContext->pSliceFences.resize( num_output_buffers, NULL );
for( uint32_t slice_idx = 0; slice_idx < num_output_buffers; slice_idx++ )
{
if( ( slice_idx > 0 ) && !m_EncoderCapabilities.m_HWSupportSlicedFences.bits.multiple_buffers_required )
{
// sliced buffers + notifications with a single buffer (suballocated by driver for each slice)
pDX12EncodeContext->pOutputBitRes[slice_idx] = pDX12EncodeContext->pOutputBitRes[0];
}
else
{
// sliced buffers + notifications with multiple individual buffers per slice
// or, full frame bitstream (when num_output_buffers == 1)
CHECKNULL_GOTO(
pDX12EncodeContext->pOutputBitRes[slice_idx] = m_pVlScreen->pscreen->resource_create( m_pVlScreen->pscreen, &templ ),
E_OUTOFMEMORY,
done );
}
}
}
// Set the fence to be waited on m_SyncFenceValue and increment the value for the next frame
m_pVlScreen->pscreen->set_fence_timeline_value( m_pVlScreen->pscreen, m_pPipeFenceHandle, m_SyncFenceValue++ );
done:
if( SUCCEEDED( hr ) )
{
*ppDX12EncodeContext = pDX12EncodeContext;
pDX12EncodeContext = nullptr;
}
SAFE_DELETE( pDX12EncodeContext );
SAFE_CLOSEHANDLE( hTexture );
return hr;
}
// utility function to validate the user passed in dirty rects
HRESULT
CDX12EncHMFT::ValidateDirtyRects( const LPDX12EncodeContext pDX12EncodeContext, const DIRTYRECT_INFO *pDirtyRectInfo )
{
HRESULT hr = S_OK;
const UINT uiNumDirtyRects = pDirtyRectInfo->NumDirtyRects;
const LONG textureWidth = static_cast<LONG>( pDX12EncodeContext->textureWidth );
const LONG textureHeight = static_cast<LONG>( pDX12EncodeContext->textureHeight );
for( UINT i = 0; i < uiNumDirtyRects; i++ )
{
if( pDirtyRectInfo->DirtyRects[i].left < 0 || pDirtyRectInfo->DirtyRects[i].top < 0 ||
pDirtyRectInfo->DirtyRects[i].right < pDirtyRectInfo->DirtyRects[i].left ||
pDirtyRectInfo->DirtyRects[i].bottom < pDirtyRectInfo->DirtyRects[i].top ||
pDirtyRectInfo->DirtyRects[i].right > textureWidth || pDirtyRectInfo->DirtyRects[i].bottom > textureHeight )
{
debug_printf( "MFT: invalid dirty rect %d (%d, %d, %d, %d) received\n",
i,
pDirtyRectInfo->DirtyRects[i].left,
pDirtyRectInfo->DirtyRects[i].top,
pDirtyRectInfo->DirtyRects[i].right,
pDirtyRectInfo->DirtyRects[i].bottom );
CHECKHR_GOTO( E_INVALIDARG, done );
}
}
done:
return hr;
}

View file

@ -0,0 +1,138 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if VIDEO_CODEC_AV1ENC
#include "hmft_entrypoints.h"
#include "mfbufferhelp.h"
#include "mfpipeinterop.h"
extern DWORD
CalculateQualityFromQP( DWORD QP );
HRESULT
CDX12EncHMFT::PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bool dirtyRectFrameNumSet, uint32_t dirtyRectFrameNum )
{
HRESULT hr = S_OK;
// done:
return hr;
}
HRESULT
CDX12EncHMFT::GetCodecPrivateData( LPBYTE pSPSPPSData, DWORD dwSPSPPSDataLen, LPDWORD lpdwSPSPPSDataLen )
{
HRESULT hr = S_OK;
done:
return hr;
}
static HRESULT
ConvertLevelToAVEncAV1VLevel( UINT32 uiLevel, eAVEncAV1VLevel &level )
{
HRESULT hr = S_OK;
level = eAVEncAV1VLevel5;
switch( uiLevel )
{
case(UINT32) -1: // -1 means auto
level = eAVEncAV1VLevel5;
break;
case 0:
level = eAVEncAV1VLevel2;
break;
case 1:
level = eAVEncAV1VLevel2_1;
break;
case 4:
level = eAVEncAV1VLevel3;
break;
case 5:
level = eAVEncAV1VLevel3_1;
break;
case 8:
level = eAVEncAV1VLevel4;
break;
case 9:
level = eAVEncAV1VLevel4_1;
break;
case 12:
level = eAVEncAV1VLevel5;
break;
case 13:
level = eAVEncAV1VLevel5_1;
break;
case 14:
level = eAVEncAV1VLevel5_2;
break;
case 15:
level = eAVEncAV1VLevel5_3;
break;
case 16:
level = eAVEncAV1VLevel6;
break;
case 17:
level = eAVEncAV1VLevel6_1;
break;
case 18:
level = eAVEncAV1VLevel6_2;
break;
case 19:
level = eAVEncAV1VLevel6_3;
break;
default:
hr = MF_E_INVALIDMEDIATYPE;
break;
}
return hr;
}
HRESULT
CDX12EncHMFT::CheckMediaTypeLevel(
IMFMediaType *pmt, int width, int height, const encoder_capabilities &encoderCapabilities, eAVEncAV1VLevel *pLevel ) const
{
HRESULT hr = S_OK;
UINT32 uiLevel = (UINT32) -1;
uiLevel = MFGetAttributeUINT32( pmt, MF_MT_VIDEO_LEVEL, uiLevel );
enum eAVEncAV1VLevel AVEncLevel;
CHECKHR_GOTO( ConvertLevelToAVEncAV1VLevel( uiLevel, AVEncLevel ), done );
if( pLevel )
{
*pLevel = AVEncLevel;
}
done:
return hr;
}
UINT32
CDX12EncHMFT::GetMaxReferences( unsigned int width, unsigned int height )
{
UINT32 uiMaxReferences = PIPE_AV1_REFS_PER_FRAME;
return uiMaxReferences;
}
HRESULT
CDX12EncHMFT::CreateGOPTracker( uint32_t textureWidth, uint32_t textureHeight )
{
HRESULT hr = E_NOTIMPL;
return hr;
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,956 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if VIDEO_CODEC_H265ENC
#include "hmft_entrypoints.h"
#include "mfbufferhelp.h"
#include "mfpipeinterop.h"
#include "reference_frames_tracker_hevc.h"
#include "wpptrace.h"
#include "encode_hevc.tmh"
extern DWORD
CalculateQualityFromQP( DWORD QP );
// utility function to compute the cropping rectangle given texture and output dimensions
static void
ComputeCroppingRect( pipe_h265_enc_picture_desc *pPicInfo,
const UINT32 textureWidth,
const UINT32 textureHeight,
const UINT uiOutputWidth,
const UINT uiOutputHeight,
const enum pipe_video_profile outputPipeProfile )
{
UINT32 iCropRight = textureWidth - uiOutputWidth;
UINT32 iCropBottom = textureHeight - uiOutputHeight;
if( iCropRight || iCropBottom )
{
UINT32 chromaFormatIdc = GetChromaFormatIdc( ConvertProfileToFormat( outputPipeProfile ) );
UINT32 cropUnitX = 1;
UINT32 cropUnitY = 1;
switch( chromaFormatIdc )
{
case 1:
cropUnitX = 2;
cropUnitY = 2;
break;
case 2:
cropUnitX = 2;
cropUnitY = 1;
break;
case 3:
cropUnitX = 1;
cropUnitY = 1;
break;
default:
{
unreachable( "Unsupported chroma format idc" );
}
break;
}
pPicInfo->seq.conformance_window_flag = TRUE;
pPicInfo->seq.conf_win_right_offset = static_cast<uint16_t>( iCropRight / cropUnitX );
pPicInfo->seq.conf_win_bottom_offset = static_cast<uint16_t>( iCropBottom / cropUnitY );
}
}
// utility function to fill in encoder picture descriptor (pPicInfo) which is used to pass information to DX12 encoder
static void
UpdateH265EncPictureDesc( pipe_h265_enc_picture_desc *pPicInfo,
const encoder_capabilities &EncoderCapabilities,
const VUInfo &VUIInfo,
const MFRatio &FrameRate )
{
if( pPicInfo->base.profile == PIPE_VIDEO_PROFILE_HEVC_MAIN10_422 || pPicInfo->base.profile == PIPE_VIDEO_PROFILE_HEVC_MAIN_444 ||
pPicInfo->base.profile == PIPE_VIDEO_PROFILE_HEVC_MAIN10_444 )
{
pPicInfo->seq.sps_range_extension.sps_range_extension_flag = 1;
// SPS Range ext flags
if( EncoderCapabilities.m_HWSupportH265RangeExtensionFlags.bits.supports_transform_skip_rotation_enabled_flag )
pPicInfo->seq.sps_range_extension.transform_skip_rotation_enabled_flag = 1;
if( EncoderCapabilities.m_HWSupportH265RangeExtensionFlags.bits.supports_transform_skip_context_enabled_flag )
pPicInfo->seq.sps_range_extension.transform_skip_context_enabled_flag = 1;
if( EncoderCapabilities.m_HWSupportH265RangeExtensionFlags.bits.supports_implicit_rdpcm_enabled_flag )
pPicInfo->seq.sps_range_extension.implicit_rdpcm_enabled_flag = 1;
if( EncoderCapabilities.m_HWSupportH265RangeExtensionFlags.bits.supports_explicit_rdpcm_enabled_flag )
pPicInfo->seq.sps_range_extension.explicit_rdpcm_enabled_flag = 1;
if( EncoderCapabilities.m_HWSupportH265RangeExtensionFlags.bits.supports_extended_precision_processing_flag )
pPicInfo->seq.sps_range_extension.extended_precision_processing_flag = 1;
if( EncoderCapabilities.m_HWSupportH265RangeExtensionFlags.bits.supports_intra_smoothing_disabled_flag )
pPicInfo->seq.sps_range_extension.intra_smoothing_disabled_flag = 0;
if( EncoderCapabilities.m_HWSupportH265RangeExtensionFlags.bits.supports_high_precision_offsets_enabled_flag )
pPicInfo->seq.sps_range_extension.high_precision_offsets_enabled_flag = 1;
if( EncoderCapabilities.m_HWSupportH265RangeExtensionFlags.bits.supports_persistent_rice_adaptation_enabled_flag )
pPicInfo->seq.sps_range_extension.persistent_rice_adaptation_enabled_flag = 1;
if( EncoderCapabilities.m_HWSupportH265RangeExtensionFlags.bits.supports_cabac_bypass_alignment_enabled_flag )
pPicInfo->seq.sps_range_extension.cabac_bypass_alignment_enabled_flag = 1;
// PPS Range ext flags
pPicInfo->pic.pps_range_extension.pps_range_extension_flag = 1;
if( EncoderCapabilities.m_HWSupportH265RangeExtensionFlags.bits.supports_cross_component_prediction_enabled_flag )
pPicInfo->pic.pps_range_extension.cross_component_prediction_enabled_flag = 1;
// Codec valid range for support for log2_max_transform_skip_block_size_minus2 is [0, 3]
for( unsigned i = 0; i < 4; i++ )
{
if( ( EncoderCapabilities.m_HWSupportH265RangeExtension.bits.supported_log2_max_transform_skip_block_size_minus2_values &
( 1 << i ) ) != 0 )
{
pPicInfo->pic.pps_range_extension.log2_max_transform_skip_block_size_minus2 = i;
break;
}
}
if( EncoderCapabilities.m_HWSupportH265RangeExtensionFlags.bits.supports_chroma_qp_offset_list_enabled_flag )
pPicInfo->pic.pps_range_extension.chroma_qp_offset_list_enabled_flag = 1;
if( pPicInfo->pic.pps_range_extension.chroma_qp_offset_list_enabled_flag )
{
// Codec valid range for support for diff_cu_chroma_qp_offset_depth is [0, 3].
for( unsigned i = 0; i < 4; i++ )
{
if( ( EncoderCapabilities.m_HWSupportH265RangeExtension.bits.supported_diff_cu_chroma_qp_offset_depth_values &
( 1 << i ) ) != 0 )
{
pPicInfo->pic.pps_range_extension.diff_cu_chroma_qp_offset_depth = i;
break;
}
}
pPicInfo->pic.pps_range_extension.chroma_qp_offset_list_len_minus1 =
EncoderCapabilities.m_HWSupportH265RangeExtension.bits.min_chroma_qp_offset_list_len_minus1_values;
for( unsigned i = 0; i < pPicInfo->pic.pps_range_extension.chroma_qp_offset_list_len_minus1 + 1; i++ )
{
pPicInfo->pic.pps_range_extension.cb_qp_offset_list[i] = 0;
pPicInfo->pic.pps_range_extension.cr_qp_offset_list[i] = 0;
}
}
// Codec valid range for support for log2_sao_offset_scale_luma is [0, 6].
for( unsigned i = 0; i < 7; i++ )
{
if( ( EncoderCapabilities.m_HWSupportH265RangeExtension.bits.supported_log2_sao_offset_scale_luma_values & ( 1 << i ) ) !=
0 )
{
pPicInfo->pic.pps_range_extension.log2_sao_offset_scale_luma = i;
break;
}
}
// Codec valid range for support for log2_sao_offset_scale_chroma is [0, 6].
for( unsigned i = 0; i < 7; i++ )
{
if( ( EncoderCapabilities.m_HWSupportH265RangeExtension.bits.supported_log2_sao_offset_scale_chroma_values &
( 1 << i ) ) != 0 )
{
pPicInfo->pic.pps_range_extension.log2_sao_offset_scale_chroma = i;
break;
}
}
}
pPicInfo->seq.log2_min_luma_coding_block_size_minus3 =
EncoderCapabilities.m_HWSupportH265BlockSizes.bits.log2_min_luma_coding_block_size_minus3;
pPicInfo->seq.log2_diff_max_min_luma_coding_block_size =
static_cast<uint8_t>( ( EncoderCapabilities.m_HWSupportH265BlockSizes.bits.log2_max_coding_tree_block_size_minus3 + 3 ) -
( EncoderCapabilities.m_HWSupportH265BlockSizes.bits.log2_min_luma_coding_block_size_minus3 + 3 ) );
pPicInfo->seq.log2_min_transform_block_size_minus2 =
EncoderCapabilities.m_HWSupportH265BlockSizes.bits.log2_min_luma_transform_block_size_minus2;
pPicInfo->seq.log2_diff_max_min_transform_block_size =
static_cast<uint8_t>( ( EncoderCapabilities.m_HWSupportH265BlockSizes.bits.log2_max_luma_transform_block_size_minus2 + 2 ) -
( EncoderCapabilities.m_HWSupportH265BlockSizes.bits.log2_min_luma_transform_block_size_minus2 + 2 ) );
pPicInfo->seq.max_transform_hierarchy_depth_inter =
EncoderCapabilities.m_HWSupportH265BlockSizes.bits.min_max_transform_hierarchy_depth_inter;
pPicInfo->seq.max_transform_hierarchy_depth_intra =
EncoderCapabilities.m_HWSupportH265BlockSizes.bits.min_max_transform_hierarchy_depth_intra;
// VUI Data - always true because we have timing_info_present_flag = 1
pPicInfo->seq.vui_parameters_present_flag = 1;
// SAR - aspect ratio
pPicInfo->seq.vui_flags.aspect_ratio_info_present_flag = VUIInfo.bEnableSAR;
pPicInfo->seq.aspect_ratio_idc = 255 /* EXTENDED_SAR */;
pPicInfo->seq.sar_width = VUIInfo.stSARInfo.usWidth;
pPicInfo->seq.sar_height = VUIInfo.stSARInfo.usHeight;
// VST - video signal type
pPicInfo->seq.vui_flags.video_signal_type_present_flag = VUIInfo.bEnableVST;
pPicInfo->seq.video_format = VUIInfo.stVidSigType.eVideoFormat;
pPicInfo->seq.video_full_range_flag = VUIInfo.stVidSigType.bVideoFullRangeFlag;
pPicInfo->seq.vui_flags.colour_description_present_flag = VUIInfo.stVidSigType.bColorInfoPresent;
pPicInfo->seq.colour_primaries = VUIInfo.stVidSigType.eColorPrimary;
pPicInfo->seq.transfer_characteristics = VUIInfo.stVidSigType.eColorTransfer;
pPicInfo->seq.matrix_coefficients = VUIInfo.stVidSigType.eColorMatrix;
pPicInfo->seq.vui_flags.timing_info_present_flag = 1;
pPicInfo->seq.num_units_in_tick = FrameRate.Denominator;
pPicInfo->seq.time_scale = FrameRate.Numerator * 2;
pPicInfo->seq.vui_flags.chroma_loc_info_present_flag = 0;
pPicInfo->seq.chroma_sample_loc_type_top_field = 0;
pPicInfo->seq.chroma_sample_loc_type_bottom_field = 0;
pPicInfo->seq.vui_flags.overscan_info_present_flag = 0;
pPicInfo->seq.vui_flags.overscan_appropriate_flag = 0;
pPicInfo->seq.vui_flags.bitstream_restriction_flag = 1;
if( pPicInfo->seq.vui_flags.bitstream_restriction_flag )
{
pPicInfo->seq.vui_flags.motion_vectors_over_pic_boundaries_flag = 0;
pPicInfo->seq.max_bytes_per_pic_denom = 0;
pPicInfo->seq.log2_max_mv_length_horizontal = 0;
pPicInfo->seq.log2_max_mv_length_vertical = 0;
}
}
// internal function which contains the codec specific portion of PrepareForEncode
HRESULT
CDX12EncHMFT::PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bool dirtyRectFrameNumSet, uint32_t dirtyRectFrameNum )
{
HRESULT hr = S_OK;
pipe_h265_enc_picture_desc *pPicInfo = &pDX12EncodeContext->encoderPicInfo.h265enc;
// Initialize raw headers array
util_dynarray_init( &pPicInfo->raw_headers, NULL );
const reference_frames_tracker_frame_descriptor_hevc *cur_frame_desc = nullptr;
uint32_t height_in_blocks = 0;
uint32_t width_in_blocks = 0;
uint32_t rate_ctrl_active_layer_index = 0;
pPicInfo->requested_metadata = m_EncoderCapabilities.m_HWSupportedMetadataFlags;
pPicInfo->base.fence = &m_pPipeFenceHandle;
pPicInfo->base.input_format = pDX12EncodeContext->pPipeVideoBuffer->buffer_format;
if( pDX12EncodeContext->bROI )
{
// Convert to pipe roi params semantics
pPicInfo->roi.num = 1;
pPicInfo->roi.region[0].valid = true;
pPicInfo->roi.region[0].qp_value = pDX12EncodeContext->video_roi_area.QPDelta;
pPicInfo->roi.region[0].x = pDX12EncodeContext->video_roi_area.rect.left;
pPicInfo->roi.region[0].y = pDX12EncodeContext->video_roi_area.rect.top;
pPicInfo->roi.region[0].width =
( pDX12EncodeContext->video_roi_area.rect.right - pDX12EncodeContext->video_roi_area.rect.left );
pPicInfo->roi.region[0].height =
( pDX12EncodeContext->video_roi_area.rect.bottom - pDX12EncodeContext->video_roi_area.rect.top );
}
cur_frame_desc = (const reference_frames_tracker_frame_descriptor_hevc *) m_pGOPTracker->get_frame_descriptor();
// Currently frame_descriptor_h26x decides which temporal layer the current frame is on (e.g temporal_id)
// and reference_frames_tracker_h264 uses a well known L0 list reference topology to generate the expected reference
// pattern for temporal patterns like L1T1, L1T2, L1T3, etc
pPicInfo->pic.temporal_id = cur_frame_desc->gop_info->temporal_id;
pPicInfo->picture_type = cur_frame_desc->gop_info->frame_type;
pPicInfo->pic_order_cnt = cur_frame_desc->gop_info->picture_order_count;
pPicInfo->pic_order_cnt_type = cur_frame_desc->gop_info->pic_order_cnt_type;
// Insert new headers on IDR
if( pPicInfo->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR )
{
struct pipe_enc_raw_header header_vps = { /* type */ PIPE_H265_NAL_VPS };
util_dynarray_append( &pPicInfo->raw_headers, struct pipe_enc_raw_header, header_vps );
struct pipe_enc_raw_header header_sps = { /* type */ PIPE_H265_NAL_SPS };
util_dynarray_append( &pPicInfo->raw_headers, struct pipe_enc_raw_header, header_sps );
struct pipe_enc_raw_header header_pps = { /* type */ PIPE_H265_NAL_PPS };
util_dynarray_append( &pPicInfo->raw_headers, struct pipe_enc_raw_header, header_pps );
}
// Always insert AUD
struct pipe_enc_raw_header header_aud = { /* type */ PIPE_H265_NAL_AUD };
util_dynarray_append( &pPicInfo->raw_headers, struct pipe_enc_raw_header, header_aud );
pPicInfo->not_referenced = !cur_frame_desc->gop_info->is_used_as_future_reference;
assert( ( cur_frame_desc->gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_B ) == pPicInfo->not_referenced );
// Pass valid DPB entries on all frames (even for I/IDR contains curr recon pic buffer)
pPicInfo->dpb_size = static_cast<uint8_t>( cur_frame_desc->dpb_snapshot.size() );
assert( pPicInfo->dpb_size <= PIPE_H264_MAX_DPB_SIZE );
for( unsigned i = 0; i < pPicInfo->dpb_size; i++ )
{
pPicInfo->dpb[i].id = cur_frame_desc->dpb_snapshot[i].id;
pPicInfo->dpb[i].pic_order_cnt = cur_frame_desc->dpb_snapshot[i].pic_order_cnt;
pPicInfo->dpb[i].is_ltr = cur_frame_desc->dpb_snapshot[i].is_ltr;
pPicInfo->dpb[i].buffer = cur_frame_desc->dpb_snapshot[i].buffer;
}
pDX12EncodeContext->longTermReferenceFrameInfo = cur_frame_desc->gop_info->long_term_reference_frame_info;
pPicInfo->num_ref_idx_l0_active_minus1 = 0;
if( ( pPicInfo->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_P ) || ( pPicInfo->picture_type == PIPE_H2645_ENC_PICTURE_TYPE_B ) )
{
pPicInfo->num_ref_idx_l0_active_minus1 =
static_cast<uint32_t>( std::max( 0, static_cast<int32_t>( cur_frame_desc->l0_reference_list.size() - 1 ) ) );
for( uint32_t i = 0; i <= pPicInfo->num_ref_idx_l0_active_minus1; i++ )
pPicInfo->ref_list0[i] = cur_frame_desc->l0_reference_list[i];
}
if( m_uiDirtyRectEnabled )
{
if( m_EncoderCapabilities.m_HWSupportDirtyRects.bits.supports_require_auto_slice_mode )
{
pPicInfo->slice_mode = PIPE_VIDEO_SLICE_MODE_AUTO;
}
if( dirtyRectFrameNumSet )
{
DIRTYRECT_INFO *pDirtyRectInfo = (DIRTYRECT_INFO *) m_pDirtyRectBlob.data();
UINT uiNumDirtyRects = min( pDirtyRectInfo->NumDirtyRects, (UINT) PIPE_ENC_DIRTY_RECTS_NUM_MAX );
if( uiNumDirtyRects > 0 )
{
bool foundSurfaceIndex = false;
uint8_t surfaceIndex = UINT8_MAX;
uint32_t search = dirtyRectFrameNum - 1;
CHECKHR_GOTO( ValidateDirtyRects( pDX12EncodeContext, pDirtyRectInfo ), done );
assert( cur_frame_desc->dirty_rect_frame_num.size() == cur_frame_desc->dpb_snapshot.size() );
uint8_t dpbIndex = pPicInfo->ref_list0[0];
if( search == cur_frame_desc->dirty_rect_frame_num[dpbIndex] )
{
foundSurfaceIndex = true;
surfaceIndex = dpbIndex;
}
else
{
if( m_uiDirtyRectEnabled == DIRTY_RECT_MODE_IGNORE_FRAME_NUM )
{
debug_printf( "[dx12 hmft 0x%p] dirty rect frame num doesn't match, continue use\n", this );
foundSurfaceIndex = true;
surfaceIndex = dpbIndex;
}
else
{
debug_printf( "[dx12 hmft 0x%p] dirty rect frame num doesn't match, ignore dirty rect\n", this );
}
}
if( foundSurfaceIndex )
{
pPicInfo->dirty_info.input_mode = PIPE_ENC_DIRTY_INFO_INPUT_MODE_RECTS;
pPicInfo->dirty_info.dpb_reference_index = surfaceIndex;
pPicInfo->dirty_info.full_frame_skip = false;
pPicInfo->dirty_info.num_rects = uiNumDirtyRects;
for( UINT i = 0; i < uiNumDirtyRects; i++ )
{
pPicInfo->dirty_info.rects[i].top = pDirtyRectInfo->DirtyRects[i].top;
pPicInfo->dirty_info.rects[i].bottom = pDirtyRectInfo->DirtyRects[i].bottom;
pPicInfo->dirty_info.rects[i].left = pDirtyRectInfo->DirtyRects[i].left;
pPicInfo->dirty_info.rects[i].right = pDirtyRectInfo->DirtyRects[i].right;
}
}
}
}
}
// Quality vs speed
// PIPE: The quality level range is [1..m_uiMaxHWSupportedQualityVsSpeedLevel]
// A lower value means higher quality (slower encoding speed), and a value of 1 represents the highest quality
// (slowest encoding speed). MF Range: 0 Lower quality, faster encoding. - 100 Higher quality, slower encoding.
pPicInfo->quality_modes.level = std::max(
1u,
static_cast<uint32_t>( std::ceil( ( static_cast<float>( 100 - m_uiQualityVsSpeed ) / 100.0f ) *
static_cast<double>( m_EncoderCapabilities.m_uiMaxHWSupportedQualityVsSpeedLevel ) ) ) );
// Setup Level, not sure why this is represented twice on the codec?
pPicInfo->seq.general_level_idc = static_cast<uint8_t>( m_pPipeVideoCodec->level );
pPicInfo->seq.intra_period = cur_frame_desc->gop_info->intra_period;
pPicInfo->seq.ip_period = cur_frame_desc->gop_info->ip_period;
pPicInfo->seq.log2_max_pic_order_cnt_lsb_minus4 = cur_frame_desc->gop_info->log2_max_pic_order_cnt_lsb_minus4;
UpdateH265EncPictureDesc( pPicInfo, m_EncoderCapabilities, m_VUIInfo, m_FrameRate );
ComputeCroppingRect( pPicInfo,
pDX12EncodeContext->textureWidth,
pDX12EncodeContext->textureHeight,
m_uiOutputWidth,
m_uiOutputHeight,
m_outputPipeProfile );
pPicInfo->seq.pic_width_in_luma_samples = static_cast<uint16_t>( pDX12EncodeContext->pPipeVideoBuffer->width );
pPicInfo->seq.pic_height_in_luma_samples = static_cast<uint16_t>( pDX12EncodeContext->pPipeVideoBuffer->height );
// Slices data
height_in_blocks = ( ( pDX12EncodeContext->pPipeVideoBuffer->height + 15 ) >> 4 );
width_in_blocks = ( ( pDX12EncodeContext->pPipeVideoBuffer->width + 15 ) >> 4 );
if( m_bSliceControlModeSet && m_bSliceControlSizeSet )
{
// dirty rect is incompatible with Slice Mode, when auto mode is on
if( !( m_uiDirtyRectEnabled && !m_EncoderCapabilities.m_HWSupportDirtyRects.bits.supports_require_auto_slice_mode ) )
{
if( SLICE_CONTROL_MODE_MB == m_uiSliceControlMode )
{
pPicInfo->slice_mode = PIPE_VIDEO_SLICE_MODE_BLOCKS;
uint32_t blocks_per_slice = m_uiSliceControlSize;
pPicInfo->num_slice_descriptors = ( height_in_blocks * width_in_blocks ) / blocks_per_slice;
uint32_t slice_starting_mb = 0;
CHECKBOOL_GOTO( pPicInfo->num_slice_descriptors <= m_EncoderCapabilities.m_uiMaxHWSupportedMaxSlices,
MF_E_UNEXPECTED,
done );
for( uint32_t i = 0; i < pPicInfo->num_slice_descriptors; i++ )
{
pPicInfo->slices_descriptors[i].slice_segment_address = slice_starting_mb;
pPicInfo->slices_descriptors[i].num_ctu_in_slice = blocks_per_slice;
pPicInfo->slices_descriptors[i].slice_type = PIPE_H265_SLICE_TYPE_P; // %%%TODO%%%
slice_starting_mb += blocks_per_slice;
}
}
else if( SLICE_CONTROL_MODE_BITS == m_uiSliceControlMode )
{
pPicInfo->slice_mode = PIPE_VIDEO_SLICE_MODE_MAX_SLICE_SIZE;
pPicInfo->max_slice_bytes = m_uiSliceControlSize / 8; /* bits to bytes */
}
}
else
{
debug_printf( "[dx12 hmft 0x%p] ignore slice control because dirty rect require auto slice mode is on", this );
}
}
// Intra refresh (needs to be set after slices are set above)
if( m_uiIntraRefreshMode > 0 )
{
// dirty rect is incompatible with Intra Refresh when auto mode is on
if( !( m_uiDirtyRectEnabled && !m_EncoderCapabilities.m_HWSupportDirtyRects.bits.supports_require_auto_slice_mode ) )
{
// Use current encoder slice config for when NOT doing an intra-refresh wave
intra_refresh_slices_config non_ir_wave_slices_config = {};
CHECKBOOL_GOTO( m_EncoderCapabilities.m_uiHWSupportsIntraRefreshModes, MF_E_UNEXPECTED, done );
non_ir_wave_slices_config.slice_mode = pPicInfo->slice_mode;
non_ir_wave_slices_config.num_slice_descriptors = pPicInfo->num_slice_descriptors;
memcpy( non_ir_wave_slices_config.slices_descriptors,
pPicInfo->slices_descriptors,
sizeof( non_ir_wave_slices_config.slices_descriptors ) );
non_ir_wave_slices_config.max_slice_bytes = pPicInfo->max_slice_bytes;
// Initialize IR tracker
if( !dynamic_cast<intra_refresh_tracker_row_hevc *>( m_pGOPTracker ) )
{
if( m_uiIntraRefreshSize > m_uiGopSize && m_uiGopSize != 0 )
{ // Infinite
m_uiIntraRefreshSize = m_uiGopSize;
}
CHECKBOOL_GOTO( m_uiIntraRefreshSize <= m_EncoderCapabilities.m_uiMaxHWSupportedIntraRefreshSize,
MF_E_UNEXPECTED,
done );
m_pGOPTracker = new intra_refresh_tracker_row_hevc( m_pGOPTracker /* inject current pic tracker */,
m_uiIntraRefreshSize,
non_ir_wave_slices_config,
height_in_blocks * width_in_blocks );
CHECKNULL_GOTO( m_pGOPTracker, E_OUTOFMEMORY, done );
}
// Set pipe IR params
const intra_refresh_tracker_frame_descriptor_hevc *intra_refresh_frame_desc =
(const intra_refresh_tracker_frame_descriptor_hevc *) m_pGOPTracker->get_frame_descriptor();
pPicInfo->intra_refresh = intra_refresh_frame_desc->intra_refresh_params;
// Override slice params (as per DX12 spec for IR)
pPicInfo->slice_mode = intra_refresh_frame_desc->slices_config.slice_mode;
pPicInfo->num_slice_descriptors = intra_refresh_frame_desc->slices_config.num_slice_descriptors;
memcpy( pPicInfo->slices_descriptors,
intra_refresh_frame_desc->slices_config.slices_descriptors,
sizeof( intra_refresh_frame_desc->slices_config.slices_descriptors ) );
pPicInfo->max_slice_bytes = intra_refresh_frame_desc->slices_config.max_slice_bytes;
}
else
{
debug_printf( "[dx12 hmft 0x%p] ignore intra refresh because dirty rect require auto slice mode is on", this );
}
}
// Rate control
// Currently frame_descriptor_h26x decides which temporal layer the current frame is on (e.g temporal_id)
// which is also used to select the active rate control state index.
rate_ctrl_active_layer_index = cur_frame_desc->gop_info->temporal_id;
pPicInfo->rc[rate_ctrl_active_layer_index].fill_data_enable = true;
pPicInfo->rc[rate_ctrl_active_layer_index].skip_frame_enable = false;
if( m_uiRateControlMode == eAVEncCommonRateControlMode_CBR )
{
pPicInfo->rc[rate_ctrl_active_layer_index].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT;
pPicInfo->rc[rate_ctrl_active_layer_index].target_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
pPicInfo->rc[rate_ctrl_active_layer_index].peak_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
}
else if( m_uiRateControlMode == eAVEncCommonRateControlMode_Quality )
{
#ifdef MF_MAP_QUALITY_CONTROL_MODE_TO_QVBR
// NOTE: MF CodecAPI doesn't currently have a rate-control mode that maps well to DX12 QVBR
/* Attempt using DX12 QVBR */
if( encoder_caps.m_bHWSupportsQualityVBRRateControlMode )
{
pPicInfo->rc[rate_ctrl_active_layer_index].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_QUALITY_VARIABLE;
pPicInfo->rc[rate_ctrl_active_layer_index].target_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
pPicInfo->rc[rate_ctrl_active_layer_index].peak_bitrate = m_bPeakBitRateSet ? m_uiPeakBitRate : m_uiOutputBitrate;
pPicInfo->rc[rate_ctrl_active_layer_index].vbr_quality_factor = ( ( ( 100 - m_uiQuality[0] ) / 100.0 ) * 50 ) + 1;
pPicInfo->rc[rate_ctrl_active_layer_index].app_requested_hrd_buffer = 1;
pPicInfo->rc[rate_ctrl_active_layer_index].vbv_buffer_size = pPicInfo->rc[rate_ctrl_active_layer_index].target_bitrate /
( ( m_FrameRate.Numerator / m_FrameRate.Denominator ) * 5.5 );
pPicInfo->rc[rate_ctrl_active_layer_index].vbv_buf_initial_size =
pPicInfo->rc[rate_ctrl_active_layer_index].vbv_buffer_size;
}
else
#endif // MF_MAP_QUALITY_CONTROL_MODE_TO_QVBR
{
/* Emulate with CQP mode if QVBR not available in HW */
pPicInfo->rc[rate_ctrl_active_layer_index].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_DISABLE;
if( m_bEncodeQPSet )
{
pPicInfo->rc[0].quant_i_frames = m_uiEncodeFrameTypeIQP[rate_ctrl_active_layer_index];
pPicInfo->rc[0].quant_p_frames = m_uiEncodeFrameTypePQP[rate_ctrl_active_layer_index];
pPicInfo->rc[0].quant_b_frames = m_uiEncodeFrameTypeBQP[rate_ctrl_active_layer_index];
}
else
{
pPicInfo->rc[0].quant_i_frames = m_uiEncodeFrameTypeIQP[0];
pPicInfo->rc[0].quant_p_frames = m_uiEncodeFrameTypePQP[0];
pPicInfo->rc[0].quant_b_frames = m_uiEncodeFrameTypeBQP[0];
}
}
}
else if( m_uiRateControlMode == eAVEncCommonRateControlMode_UnconstrainedVBR )
{
pPicInfo->rc[rate_ctrl_active_layer_index].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_VARIABLE;
pPicInfo->rc[rate_ctrl_active_layer_index].target_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
pPicInfo->rc[rate_ctrl_active_layer_index].peak_bitrate =
/* emulate "unconstrained" with 5x the target bitrate*/
m_bPeakBitRateSet ? m_uiPeakBitRate : ( 5 * pPicInfo->rc[rate_ctrl_active_layer_index].target_bitrate );
}
else if( m_uiRateControlMode == eAVEncCommonRateControlMode_PeakConstrainedVBR && m_bPeakBitRateSet )
{
pPicInfo->rc[rate_ctrl_active_layer_index].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_VARIABLE;
pPicInfo->rc[rate_ctrl_active_layer_index].target_bitrate = m_bMeanBitRateSet ? m_uiMeanBitRate : m_uiOutputBitrate;
pPicInfo->rc[rate_ctrl_active_layer_index].peak_bitrate =
m_bPeakBitRateSet ? m_uiPeakBitRate : pPicInfo->rc[rate_ctrl_active_layer_index].target_bitrate;
}
pPicInfo->rc[rate_ctrl_active_layer_index].vbv_buffer_size = pPicInfo->rc[rate_ctrl_active_layer_index].target_bitrate;
if( ( pPicInfo->rc[rate_ctrl_active_layer_index].rate_ctrl_method != PIPE_H2645_ENC_RATE_CONTROL_METHOD_CONSTANT ) &&
( pPicInfo->rc[rate_ctrl_active_layer_index].target_bitrate < 2000000u ) )
pPicInfo->rc[rate_ctrl_active_layer_index].vbv_buffer_size =
(unsigned) std::min( 2000000.0, pPicInfo->rc[rate_ctrl_active_layer_index].target_bitrate * 2.75 );
pPicInfo->seq.sps_max_sub_layers_minus1 = static_cast<uint8_t>( m_uiLayerCount - 1 );
// Optional Rate control params for all RC modes
pPicInfo->rc[rate_ctrl_active_layer_index].app_requested_qp_range = m_bMinQPSet || m_bMaxQPSet;
pPicInfo->rc[rate_ctrl_active_layer_index].min_qp = m_uiMinQP;
pPicInfo->rc[rate_ctrl_active_layer_index].max_qp = m_uiMaxQP;
if( m_bBufferSizeSet )
{
pPicInfo->rc[rate_ctrl_active_layer_index].app_requested_hrd_buffer = true;
pPicInfo->rc[rate_ctrl_active_layer_index].vbv_buffer_size = m_uiBufferSize;
pPicInfo->rc[rate_ctrl_active_layer_index].vbv_buf_initial_size = m_uiBufferSize;
}
if( m_bBufferInLevelSet )
{
pPicInfo->rc[rate_ctrl_active_layer_index].app_requested_hrd_buffer = true;
pPicInfo->rc[rate_ctrl_active_layer_index].vbv_buf_initial_size = m_uiBufferInLevel;
}
// Frame Rate
pPicInfo->rc[rate_ctrl_active_layer_index].frame_rate_num = m_FrameRate.Numerator;
pPicInfo->rc[rate_ctrl_active_layer_index].frame_rate_den = m_FrameRate.Denominator;
// VPS
pPicInfo->vid.vps_sub_layer_ordering_info_present_flag = 0;
pPicInfo->vid.vps_max_sub_layers_minus1 = 0;
for( int i = ( pPicInfo->vid.vps_sub_layer_ordering_info_present_flag ? 0 : pPicInfo->vid.vps_max_sub_layers_minus1 );
i <= pPicInfo->vid.vps_max_sub_layers_minus1;
i++ )
{
pPicInfo->vid.vps_max_dec_pic_buffering_minus1[i] = static_cast<uint8_t>( m_pPipeVideoCodec->max_references );
pPicInfo->vid.vps_max_num_reorder_pics[i] = 0; // TODO: B-frames / reordering
pPicInfo->vid.vps_max_latency_increase_plus1[i] = 0 + 1; // TODO: B-frames
}
// sanity checks for future, currently these two values are all zeros.
if( m_uiDirtyRectEnabled )
{
if( m_EncoderCapabilities.m_HWSupportDirtyRects.bits.supports_require_loop_filter_disabled )
{
if( pPicInfo->pic.pps_loop_filter_across_slices_enabled_flag )
{
debug_printf( "[dx12 hmft 0x%p] override pps_loop_filter_across_slices_enabled_flag to 0 because dirty rect "
"supports_require_loop_filter_disabled is enable\n",
this );
assert( false );
pPicInfo->pic.pps_loop_filter_across_slices_enabled_flag = 0;
}
}
if( m_EncoderCapabilities.m_HWSupportDirtyRects.bits.supports_require_sao_filter_disabled )
{
if( pPicInfo->seq.sample_adaptive_offset_enabled_flag )
{
debug_printf( "[dx12 hmft 0x%p] override sample_adaptive_offset_enabled_flag to 0 because dirty rect "
"supports_require_sao_filter_disabled is enable\n",
this );
assert( false );
pPicInfo->seq.sample_adaptive_offset_enabled_flag = 0;
}
}
}
debug_printf( "[dx12 hmft 0x%p] MFT frontend submission - POC %d picture_type %s num_slice_descriptors %d\n",
this,
pPicInfo->pic_order_cnt,
ConvertPipeH2645FrameTypeToString( pPicInfo->picture_type ),
pPicInfo->num_slice_descriptors );
done:
return hr;
}
// generate SPS and PPS headers for codec private data (MF_MT_MPEG_SEQUENCE_HEADER)
HRESULT
CDX12EncHMFT::GetCodecPrivateData( LPBYTE pSPSPPSData, DWORD dwSPSPPSDataLen, LPDWORD lpdwSPSPPSDataLen )
{
HRESULT hr = S_OK;
UINT alignedWidth = static_cast<UINT>( std::ceil( m_uiOutputWidth / 16.0 ) ) * 16;
UINT alignedHeight = static_cast<UINT>( std::ceil( m_uiOutputHeight / 16.0 ) ) * 16;
int ret = EINVAL;
unsigned buf_size = dwSPSPPSDataLen;
pipe_h265_enc_picture_desc h265_pic_desc = {};
memset( &h265_pic_desc, 0, sizeof( h265_pic_desc ) );
uint32_t gop_length = m_uiGopSize;
uint32_t p_picture_period = m_uiBFrameCount + 1;
h265_pic_desc.base.profile = m_outputPipeProfile;
// TODO: might not be needed for 265, check later, for now dup logic
h265_pic_desc.pic_order_cnt_type = ( p_picture_period > 2 ) ? 0u : 2u;
h265_pic_desc.pic_order_cnt = 0; // cur_frame_desc->gop_info->picture_order_count;
h265_pic_desc.picture_type = PIPE_H2645_ENC_PICTURE_TYPE_IDR; // cur_frame_desc->gop_info->frame_type;
h265_pic_desc.seq.ip_period = p_picture_period; // cur_frame_desc->gop_info->base.ip_period;
h265_pic_desc.seq.intra_period = gop_length; // cur_frame_desc->gop_info->base.intra_period;
h265_pic_desc.seq.general_profile_idc = static_cast<uint8_t>( m_pPipeVideoCodec->profile );
h265_pic_desc.seq.general_level_idc = static_cast<uint8_t>( m_pPipeVideoCodec->level );
h265_pic_desc.seq.chroma_format_idc = GetChromaFormatIdc( ConvertProfileToFormat( m_outputPipeProfile ) );
h265_pic_desc.seq.log2_max_pic_order_cnt_lsb_minus4 = 4;
UpdateH265EncPictureDesc( &h265_pic_desc, m_EncoderCapabilities, m_VUIInfo, m_FrameRate );
ComputeCroppingRect( &h265_pic_desc, alignedWidth, alignedHeight, m_uiOutputWidth, m_uiOutputHeight, m_outputPipeProfile );
h265_pic_desc.seq.pic_width_in_luma_samples = static_cast<uint16_t>( alignedWidth );
h265_pic_desc.seq.pic_height_in_luma_samples = static_cast<uint16_t>( alignedHeight );
// Rate Control
h265_pic_desc.rc[0].rate_ctrl_method = PIPE_H2645_ENC_RATE_CONTROL_METHOD_DISABLE;
h265_pic_desc.rc[0].frame_rate_num = m_FrameRate.Numerator;
h265_pic_desc.rc[0].frame_rate_den = m_FrameRate.Denominator;
h265_pic_desc.rc[0].vbr_quality_factor = static_cast<unsigned int>( ( ( ( 100 - m_uiQuality[0] ) / 100.0 ) * 50 ) + 1 );
// Set default valid CQP 26 with 30 fps, doesn't affect header building
// but needs to be valid, otherwise some drivers segfault
h265_pic_desc.rc[0].quant_i_frames = m_uiEncodeFrameTypeIQP[0];
h265_pic_desc.rc[0].quant_p_frames = m_uiEncodeFrameTypeIQP[0];
h265_pic_desc.rc[0].quant_b_frames = m_uiEncodeFrameTypeIQP[0];
h265_pic_desc.vid.vps_sub_layer_ordering_info_present_flag = 0;
h265_pic_desc.vid.vps_max_sub_layers_minus1 = 0;
for( int i = ( h265_pic_desc.vid.vps_sub_layer_ordering_info_present_flag ? 0 : h265_pic_desc.vid.vps_max_sub_layers_minus1 );
i <= h265_pic_desc.vid.vps_max_sub_layers_minus1;
i++ )
{
h265_pic_desc.vid.vps_max_dec_pic_buffering_minus1[i] = static_cast<uint8_t>( m_pPipeVideoCodec->max_references - 1 );
h265_pic_desc.vid.vps_max_num_reorder_pics[i] = 0; // TODO: B-frames / reordering
h265_pic_desc.vid.vps_max_latency_increase_plus1[i] = 0 + 1; // TODO: B-frames
}
ret = m_pPipeVideoCodec->get_encode_headers( m_pPipeVideoCodec, &h265_pic_desc.base, pSPSPPSData, &buf_size );
CHECKHR_GOTO( ConvertErrnoRetToHR( ret ), done );
*lpdwSPSPPSDataLen = (DWORD) buf_size;
done:
return hr;
}
// utility function to convert level to eAVEncH265VLevel
static HRESULT
ConvertLevelToAVEncH265VLevel( UINT32 uiLevel, eAVEncH265VLevel &level )
{
HRESULT hr = S_OK;
level = eAVEncH265VLevel5;
switch( uiLevel )
{
case 0: // possibly HLK is using 0 as auto.
case(UINT32) -1: // auto
level = eAVEncH265VLevel5;
break;
case 30:
level = eAVEncH265VLevel1;
break;
case 60:
level = eAVEncH265VLevel2;
break;
case 63:
level = eAVEncH265VLevel2_1;
break;
case 90:
level = eAVEncH265VLevel3;
break;
case 93:
level = eAVEncH265VLevel3_1;
break;
case 120:
level = eAVEncH265VLevel4;
break;
case 123:
level = eAVEncH265VLevel4_1;
break;
case 150:
level = eAVEncH265VLevel5;
break;
case 153:
level = eAVEncH265VLevel5_1;
break;
case 156:
level = eAVEncH265VLevel5_2;
break;
case 180:
level = eAVEncH265VLevel6;
break;
case 183:
level = eAVEncH265VLevel6_1;
break;
case 186:
level = eAVEncH265VLevel6_2;
break;
default:
hr = MF_E_INVALIDMEDIATYPE;
break;
}
return hr;
}
/* get max luma picture size from level (see Table A.8) */
static int
LevelToLumaPS( eAVEncH265VLevel level_idc )
{
int maxLumaPs = 0;
switch( level_idc )
{
case eAVEncH265VLevel1:
maxLumaPs = 36864;
break;
case eAVEncH265VLevel2:
maxLumaPs = 122880;
break;
case eAVEncH265VLevel2_1:
maxLumaPs = 245760;
break;
case eAVEncH265VLevel3:
maxLumaPs = 552960;
break;
case eAVEncH265VLevel3_1:
maxLumaPs = 983040;
break;
case eAVEncH265VLevel4:
maxLumaPs = 2228224;
break;
case eAVEncH265VLevel4_1:
maxLumaPs = 2228224;
break;
case eAVEncH265VLevel5:
maxLumaPs = 8912896;
break;
case eAVEncH265VLevel5_1:
maxLumaPs = 8912896;
break;
case eAVEncH265VLevel5_2:
maxLumaPs = 8912896;
break;
case eAVEncH265VLevel6:
maxLumaPs = 35651584;
break;
case eAVEncH265VLevel6_1:
maxLumaPs = 35651584;
break;
case eAVEncH265VLevel6_2:
maxLumaPs = 35651584;
break;
default:
unreachable( "unexpected level_idc" );
break;
}
return maxLumaPs;
}
// utility function to check the level retrieved from the media type
HRESULT
CDX12EncHMFT::CheckMediaTypeLevel(
IMFMediaType *pmt, int width, int height, const encoder_capabilities &encoderCapabilities, eAVEncH265VLevel *pLevel ) const
{
HRESULT hr = S_OK;
UINT32 uiLevel = (UINT32) -1;
int maxLumaPs = 0;
const int minCbSizeY = 1 << ( encoderCapabilities.m_HWSupportH265BlockSizes.bits.log2_min_luma_coding_block_size_minus3 + 3 );
const int alignedWidth = static_cast<UINT>( std::ceil( width / static_cast<double>( minCbSizeY ) ) * minCbSizeY );
const int alignedHeight = static_cast<UINT>( std::ceil( height / static_cast<double>( minCbSizeY ) ) * minCbSizeY );
uiLevel = MFGetAttributeUINT32( pmt, MF_MT_VIDEO_LEVEL, uiLevel );
enum eAVEncH265VLevel AVEncLevel;
CHECKHR_GOTO( ConvertLevelToAVEncH265VLevel( uiLevel, AVEncLevel ), done );
maxLumaPs = LevelToLumaPS( AVEncLevel );
// TODO: add more checks according to A.1
if( ( alignedHeight * alignedWidth > maxLumaPs ) || ( (double) alignedWidth > sqrt( (double) maxLumaPs * 8 ) ) ||
( (double) alignedHeight > sqrt( (double) maxLumaPs * 8 ) ) )
{
debug_printf( "[dx12 hmft 0x%p] CheckMediaTypeLevel failed: alignedWidth, alignedHeight combination exceeded max luma "
"sample constraints "
"(maxLumaPS). (alignedWidth = %d, alignedHeight = %d, maxLumaPS = %d)\n",
this,
alignedHeight,
alignedWidth,
maxLumaPs );
CHECKHR_GOTO( E_INVALIDARG, done );
}
if( pLevel )
{
*pLevel = AVEncLevel;
}
done:
return hr;
}
// utility function to get max dpb size from the level and image dimensions
static int
GetMaxDPBSize( int width, int height, eAVEncH265VLevel level_idc, int minCBSizeY )
{
const int alignedWidth = static_cast<UINT>( std::ceil( width / static_cast<double>( minCBSizeY ) ) * minCBSizeY );
const int alignedHeight = static_cast<UINT>( std::ceil( height / static_cast<double>( minCBSizeY ) ) * minCBSizeY );
const int PicSizeInSamplesY = ( alignedWidth ) * ( alignedHeight );
int maxLumaPs = LevelToLumaPS( level_idc );
int maxDpbSize = 0;
const int maxDpbPicBuf = 6; // TODO: in spec it is 6 or 7 depending on sps_curr_pic_ref_enabled_flag (scc profile), need to
// check if this is something we support
if( PicSizeInSamplesY <= ( maxLumaPs >> 2 ) )
{
maxDpbSize = 4 * maxDpbPicBuf;
}
else if( PicSizeInSamplesY <= ( maxLumaPs >> 1 ) )
{
maxDpbSize = 2 * maxDpbPicBuf;
}
else if( PicSizeInSamplesY <= ( ( 3 * maxLumaPs ) >> 2 ) )
{
maxDpbSize = 4 * maxDpbPicBuf / 3;
}
else
{
maxDpbSize = maxDpbPicBuf;
}
return maxDpbSize;
}
// utility function to get max reference frames from hardware capabilities given image dimensions
UINT32
CDX12EncHMFT::GetMaxReferences( unsigned int width, unsigned int height )
{
const int minCbSizeY = 1 << ( m_EncoderCapabilities.m_HWSupportH265BlockSizes.bits.log2_min_luma_coding_block_size_minus3 + 3 );
int maxDPBSize = GetMaxDPBSize( width, height, m_uiLevel, minCbSizeY );
UINT32 uiMaxReferences = std::min( (int) m_EncoderCapabilities.m_uiMaxHWSupportedDPBCapacity, maxDPBSize );
return uiMaxReferences;
}
// utility function to create reference frame tracker which manages the DPB and operations involving it, e.g. frame type, LTR,
// temporal layers, etc.
HRESULT
CDX12EncHMFT::CreateGOPTracker( uint32_t textureWidth, uint32_t textureHeight )
{
HRESULT hr = S_OK;
uint32_t MaxHWL0Ref = m_EncoderCapabilities.m_uiMaxHWSupportedL0References;
uint32_t MaxHWL1Ref = m_EncoderCapabilities.m_uiMaxHWSupportedL1References;
MaxHWL0Ref = std::min( 1u, MaxHWL0Ref ); // we only support 1
MaxHWL1Ref = 0;
SAFE_DELETE( m_pGOPTracker );
// B Frame not supported by HW
CHECKBOOL_GOTO( ( m_uiBFrameCount == 0 ) || ( MaxHWL1Ref > 0 ), E_INVALIDARG, done );
// Requested number of temporal layers higher than max supported by HW
CHECKBOOL_GOTO( m_uiLayerCount <= m_EncoderCapabilities.m_uiMaxTemporalLayers, MF_E_OUT_OF_RANGE, done );
// Validate logic expression (m_uiLayerCount > 1) => (m_uiBFrameCount == 0)
CHECKBOOL_GOTO( ( m_uiLayerCount <= 1 ) || ( m_uiBFrameCount == 0 ),
E_INVALIDARG,
done ); // B frame with temporal layers not implemented
// Validate logic expression (m_uiMaxLongTermReferences != 0) => (m_uiBFrameCount == 0)
CHECKBOOL_GOTO( ( m_uiMaxLongTermReferences == 0 ) || ( m_uiBFrameCount == 0 ), MF_E_OUT_OF_RANGE, done );
// Ensure that the number of long term references is <= than the max supported by HW
// TODO: This check should be added at CodecAPI_AVEncVideoLTRBufferControl level and fail there too, but would need to setup
// global encoder cap first.
CHECKBOOL_GOTO( ( m_uiMaxLongTermReferences <= m_EncoderCapabilities.m_uiMaxHWSupportedLongTermReferences ),
MF_E_OUT_OF_RANGE,
done );
assert( m_uiBFrameCount == 0 );
assert( m_uiMaxNumRefFrame == m_pPipeVideoCodec->max_references );
assert( 1 + m_uiMaxLongTermReferences <= m_uiMaxNumRefFrame );
assert( MaxHWL0Ref <= m_uiMaxNumRefFrame );
assert( MaxHWL1Ref <= m_uiMaxNumRefFrame );
m_pGOPTracker = new reference_frames_tracker_hevc( m_pPipeVideoCodec,
textureWidth,
textureHeight,
m_uiGopSize,
m_uiBFrameCount,
m_bLayerCountSet,
m_uiLayerCount,
m_bLowLatency,
MaxHWL0Ref,
MaxHWL1Ref,
m_pPipeVideoCodec->max_references,
m_uiMaxLongTermReferences );
CHECKNULL_GOTO( m_pGOPTracker, MF_E_INVALIDMEDIATYPE, done );
done:
return hr;
}
#endif

View file

@ -0,0 +1,149 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <utility>
#include <encoder_capabilities.h>
// Initializes encoder capabilities by querying hardware-specific parameters from pipe given the video profile.
void
encoder_capabilities::initialize( pipe_screen *pScreen, pipe_video_profile videoProfile )
{
m_deviceVendor = pScreen->get_device_vendor( pScreen );
m_uiMaxWidth = pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_MAX_WIDTH );
m_uiMaxHeight = pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_MAX_HEIGHT );
m_uiMinWidth = pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_MIN_WIDTH );
m_uiMinHeight = pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_MIN_HEIGHT );
m_uiMaxTemporalLayers =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_MAX_TEMPORAL_LAYERS );
// On some systems this is coming back as zero? Set it to 1 slice per frame in that case
m_uiMaxHWSupportedMaxSlices = std::max(
1,
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_MAX_SLICES_PER_FRAME ) );
UINT uiMaxHWSupportedL0L1References =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_MAX_REFERENCES_PER_FRAME );
m_uiMaxHWSupportedL0References = ( uiMaxHWSupportedL0L1References & 0xffff ); // lower 16 bits
m_uiMaxHWSupportedL1References = ( ( uiMaxHWSupportedL0L1References >> 16 ) & 0xffff ); // upper 16 bits
m_uiMaxHWSupportedLongTermReferences = pScreen->get_video_param( pScreen,
videoProfile,
PIPE_VIDEO_ENTRYPOINT_ENCODE,
PIPE_VIDEO_CAP_ENC_MAX_LONG_TERM_REFERENCES_PER_FRAME );
m_uiMaxHWSupportedDPBCapacity =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_MAX_DPB_CAPACITY );
m_uiMaxHWSupportedQualityVsSpeedLevel =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_QUALITY_LEVEL );
m_bHWSupportsMaxFrameSize =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_SUPPORTS_MAX_FRAME_SIZE );
m_bHWSupportsQualityVBRRateControlMode =
( pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_RATE_CONTROL_QVBR ) ==
1 );
m_uiHWSupportsIntraRefreshModes =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_INTRA_REFRESH );
m_HWSupportedMetadataFlags =
(enum pipe_video_feedback_metadata_type) pScreen->get_video_param( pScreen,
videoProfile,
PIPE_VIDEO_ENTRYPOINT_ENCODE,
PIPE_VIDEO_CAP_ENC_SUPPORTS_FEEDBACK_METADATA );
m_HWSupportedDisableDBKH264ModeFlags = (enum pipe_video_h264_enc_dbk_filter_mode_flags) pScreen->get_video_param(
pScreen,
videoProfile,
PIPE_VIDEO_ENTRYPOINT_ENCODE,
PIPE_VIDEO_CAP_ENC_H264_DISABLE_DBK_FILTER_MODES_SUPPORTED );
if( m_uiHWSupportsIntraRefreshModes )
{
m_uiMaxHWSupportedIntraRefreshSize = pScreen->get_video_param( pScreen,
videoProfile,
PIPE_VIDEO_ENTRYPOINT_ENCODE,
PIPE_VIDEO_CAP_ENC_INTRA_REFRESH_MAX_DURATION );
}
m_bHWSupportsH264CABACEncode =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_H264_SUPPORTS_CABAC_ENCODE );
m_HWSupportsVideoEncodeROI.value = static_cast<uint32_t>(
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_ROI ) );
m_HWSupportH265BlockSizes.value =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_HEVC_BLOCK_SIZES );
m_HWSupportH265RangeExtension.value = pScreen->get_video_param( pScreen,
videoProfile,
PIPE_VIDEO_ENTRYPOINT_ENCODE,
PIPE_VIDEO_CAP_ENC_HEVC_RANGE_EXTENSION_SUPPORT );
m_HWSupportH265RangeExtensionFlags.value = pScreen->get_video_param( pScreen,
videoProfile,
PIPE_VIDEO_ENTRYPOINT_ENCODE,
PIPE_VIDEO_CAP_ENC_HEVC_RANGE_EXTENSION_FLAGS_SUPPORT );
m_HWSupportSurfaceAlignment.value =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_SURFACE_ALIGNMENT );
m_HWSupportDirtyRects.value =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_DIRTY_RECTS );
m_HWSupportMoveRects.value =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_MOVE_RECTS );
m_HWSupportStatsQPMapOutput.value =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_GPU_STATS_QP_MAP );
m_HWSupportStatsSATDMapOutput.value =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_GPU_STATS_SATD_MAP );
m_HWSupportStatsRCBitAllocationMapOutput.value = pScreen->get_video_param( pScreen,
videoProfile,
PIPE_VIDEO_ENTRYPOINT_ENCODE,
PIPE_VIDEO_CAP_ENC_GPU_STATS_RATE_CONTROL_BITS_MAP );
m_HWSupportSlicedFences.value =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_SLICED_NOTIFICATIONS );
m_HWSupportDirtyGPUMaps.value =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_DIRTY_MAPS );
m_HWSupportQPGPUMaps.value =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_QP_MAPS );
m_HWSupportMotionGPUMaps.value =
pScreen->get_video_param( pScreen, videoProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_ENC_MOTION_VECTOR_MAPS );
// TODO: We should get the supported slice mode from pipe, but currently, it doesn't support.
// Currently, dx12MFT only support mode_blocks, so we initialize it like this.
m_HWSupportedSliceModes = EnumMask<pipe_video_slice_mode> { PIPE_VIDEO_SLICE_MODE_BLOCKS };
}

View file

@ -0,0 +1,132 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#define UNICODE
#include <string>
#include <wtypes.h>
#include "enum_mask.h"
#include "pipe_headers.h"
class encoder_capabilities
{
public:
encoder_capabilities() { };
~encoder_capabilities() { };
void initialize( pipe_screen *pScreen, pipe_video_profile profile );
// Cached underlying backend pipe caps (avoid querying on each frame)
std::string m_deviceVendor {};
// PIPE_VIDEO_CAP_MAX_WIDTH
UINT m_uiMaxWidth = 0;
// PIPE_VIDEO_CAP_MAX_HEIGHT
UINT m_uiMaxHeight = 0;
// PIPE_VIDEO_CAP_MIN_WIDTH
UINT m_uiMinWidth = 0;
// PIPE_VIDEO_CAP_MIN_HEIGHT
UINT m_uiMinHeight = 0;
// PIPE_VIDEO_CAP_MAX_TEMPORAL_LAYERS
UINT m_uiMaxTemporalLayers = 0;
// PIPE_VIDEO_CAP_ENC_MAX_SLICES_PER_FRAME
UINT m_uiMaxHWSupportedMaxSlices = 0;
// PIPE_VIDEO_CAP_ENC_MAX_REFERENCES_PER_FRAME
UINT m_uiMaxHWSupportedL0References = 0;
UINT m_uiMaxHWSupportedL1References = 0;
// PIPE_VIDEO_CAP_ENC_MAX_LONG_TERM_REFERENCES_PER_FRAME
UINT m_uiMaxHWSupportedLongTermReferences = 0;
// PIPE_VIDEO_CAP_ENC_MAX_DPB_CAPACITY
UINT m_uiMaxHWSupportedDPBCapacity = 0;
// PIPE_VIDEO_CAP_ENC_QUALITY_LEVEL
UINT m_uiMaxHWSupportedQualityVsSpeedLevel = 0;
// PIPE_VIDEO_CAP_ENC_SUPPORTS_MAX_FRAME_SIZE
BOOL m_bHWSupportsMaxFrameSize = FALSE;
// PIPE_VIDEO_CAP_ENC_RATE_CONTROL_QVBR
BOOL m_bHWSupportsQualityVBRRateControlMode = FALSE;
// PIPE_VIDEO_CAP_ENC_INTRA_REFRESH
BOOL m_uiHWSupportsIntraRefreshModes = FALSE;
// PIPE_VIDEO_CAP_ENC_SUPPORTS_FEEDBACK_METADATA
enum pipe_video_feedback_metadata_type m_HWSupportedMetadataFlags = PIPE_VIDEO_FEEDBACK_METADATA_TYPE_BITSTREAM_SIZE;
// PIPE_VIDEO_CAP_ENC_H264_DISABLE_DBK_FILTER_MODES_SUPPORTED
enum pipe_video_h264_enc_dbk_filter_mode_flags m_HWSupportedDisableDBKH264ModeFlags = {};
// PIPE_VIDEO_CAP_ENC_INTRA_REFRESH_MAX_DURATION
UINT m_uiMaxHWSupportedIntraRefreshSize = 0;
// PIPE_VIDEO_CAP_ENC_H264_SUPPORTS_CABAC_ENCODE
UINT m_bHWSupportsH264CABACEncode = 0;
// PIPE_VIDEO_CAP_ENC_ROI
union pipe_enc_cap_roi m_HWSupportsVideoEncodeROI = {};
// PIPE_VIDEO_CAP_ENC_HEVC_BLOCK_SIZES
union pipe_h265_enc_cap_block_sizes m_HWSupportH265BlockSizes = {};
union pipe_h265_enc_cap_range_extension m_HWSupportH265RangeExtension = {};
union pipe_h265_enc_cap_range_extension_flags m_HWSupportH265RangeExtensionFlags = {};
// PIPE_VIDEO_CAP_ENC_SURFACE_ALIGNMENT
union pipe_enc_cap_surface_alignment m_HWSupportSurfaceAlignment = {};
// CPU dirty rects array
union pipe_enc_cap_dirty_info m_HWSupportDirtyRects = {};
union pipe_enc_cap_move_rect m_HWSupportMoveRects = {};
union pipe_enc_cap_gpu_stats_map m_HWSupportStatsQPMapOutput = {};
union pipe_enc_cap_gpu_stats_map m_HWSupportStatsSATDMapOutput = {};
union pipe_enc_cap_gpu_stats_map m_HWSupportStatsRCBitAllocationMapOutput = {};
union pipe_enc_cap_sliced_notifications m_HWSupportSlicedFences = {};
// GPU dirty map texture
union pipe_enc_cap_dirty_info m_HWSupportDirtyGPUMaps = {};
// GPU QPMap texture input
union pipe_enc_cap_qpmap m_HWSupportQPGPUMaps = {};
// GPU Motion vectors texture input
union pipe_enc_cap_motion_vector_map m_HWSupportMotionGPUMaps = {};
// Supported slice mode
EnumMask<pipe_video_slice_mode> m_HWSupportedSliceModes {};
};

View file

@ -0,0 +1,64 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include <initializer_list>
#include <type_traits>
template <typename T>
concept EnumType = std::is_enum_v<T>;
template <EnumType Enum>
class EnumMask
{
using UnderlyingType = std::underlying_type_t<Enum>;
public:
explicit constexpr EnumMask( std::initializer_list<Enum> values )
{
for( auto v : values )
{
m_mask |= MakeValue( v );
}
}
constexpr bool HasAll( Enum v )
{
return m_mask & MakeValue( v );
}
template <EnumType... Enums>
constexpr bool HasAll( Enum v, Enums... values )
{
return HasAll( v ) && HasAll( values... );
}
private:
constexpr UnderlyingType MakeValue( Enum v )
{
return 1 << (UnderlyingType) v;
}
UnderlyingType m_mask {};
};

View file

@ -0,0 +1,128 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "hmft_entrypoints.h"
/*
- This file should implement all the necessary public HMFT interface functions and export them using
- PUBLIC keyword in the method definition on this file
- Adding the DLL export entry in:
- mesa\src\gallium\targets\mediafoundation\mediafoundation.def.in
- mesa\src\gallium\targets\mediafoundation\mediafoundation.sym
- The actual implementation of the methods defined here should be delegated
to implementations in other C files in the same directory, but keeping
all the entrypoints/stubs in this file would help organize the DLL public interface
*/
CDX12EncHMFT::CDX12EncHMFT()
{ }
CDX12EncHMFT::~CDX12EncHMFT()
{
Shutdown();
CMFD3DManager::Shutdown();
}
HRESULT
CDX12EncHMFT::Initialize()
{
HRESULT hr = S_OK;
CHECKHR_GOTO( CMFD3DManager::Initialize( m_Codec ), done );
done:
return hr;
}
#if VIDEO_CODEC_H264ENC
MFT_REGISTER_TYPE_INFO rgOutputInfo = { MFMediaType_Video, MFVideoFormat_H264 };
#elif VIDEO_CODEC_H265ENC
MFT_REGISTER_TYPE_INFO rgOutputInfo = { MFMediaType_Video, MFVideoFormat_HEVC };
#elif VIDEO_CODEC_AV1ENC
MFT_REGISTER_TYPE_INFO rgOutputInfo = { MFMediaType_Video, MFVideoFormat_AV1 };
#endif
MFT_REGISTER_TYPE_INFO rgInputInfo[NUM_INPUT_TYPES] = { { MFMediaType_Video, MFVideoFormat_NV12 },
{ MFMediaType_Video, MFVideoFormat_P010 },
{ MFMediaType_Video, MFVideoFormat_AYUV } };
// Internal function to initialize available input/output types and their associated MF attributes
HRESULT
CDX12EncHMFT::RuntimeClassInitialize()
{
HRESULT hr = S_OK;
ComPtr<IMFMediaType> spVideoType = NULL;
static_assert( VIDEO_CODEC_H264ENC ^ VIDEO_CODEC_H265ENC ^ VIDEO_CODEC_AV1ENC,
"VIDEO_CODEC_H264ENC or VIDEO_CODEC_H265ENC or VIDEO_CODEC_AV1ENC must be defined but only one at a time" );
// Start by configuring for 4:2:0 NV12 as the only possible input type.
// Once the SetOutputType() happens with a profile, we'll reconfigure the available input type
// accordingly. For example, specifying an output profile that indicates 4:4:4 would mean we
// expose an input-type of AYUV.
CHECKHR_GOTO( MFCreateMediaType( &spVideoType ), done );
CHECKHR_GOTO( spVideoType->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Video ), done );
CHECKHR_GOTO( spVideoType->SetGUID( MF_MT_SUBTYPE, MFVideoFormat_NV12 ), done );
m_spAvailableInputType.Attach( spVideoType.Detach() );
CHECKHR_GOTO( MFCreateMediaType( &spVideoType ), done );
CHECKHR_GOTO( spVideoType->SetGUID( MF_MT_MAJOR_TYPE, rgOutputInfo.guidMajorType ), done );
CHECKHR_GOTO( spVideoType->SetGUID( MF_MT_SUBTYPE, rgOutputInfo.guidSubtype ), done );
CHECKHR_GOTO( spVideoType->SetUINT32( MF_MT_IN_BAND_PARAMETER_SET, TRUE ), done );
CHECKHR_GOTO( spVideoType->SetUINT32( MF_NALU_LENGTH_SET, 1 ), done );
m_spAvailableOutputType.Attach( spVideoType.Detach() );
CHECKHR_GOTO( MFCreateAttributes( &m_spMFAttributes, 7 ), done );
CHECKHR_GOTO( m_spMFAttributes->SetUINT32( MFT_ENCODER_SUPPORTS_CONFIG_EVENT, TRUE ), done );
// These are required to indicate we are an Async MFT (like all HMFTs are)
CHECKHR_GOTO( m_spMFAttributes->SetUINT32( MF_TRANSFORM_ASYNC, TRUE ), done );
CHECKHR_GOTO( m_spMFAttributes->SetUINT32( MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, TRUE ), done );
// This is required to indicate we can handle an IMFDXGIDeviceManager (which is either 11 or 12)
// NOTE: Ignore the poor naming of MF_SA_***D3D11***_AWARE here
CHECKHR_GOTO( m_spMFAttributes->SetUINT32( MF_SA_D3D11_AWARE, TRUE ), done );
CHECKHR_GOTO( m_spMFAttributes->SetUINT32( MF_SA_D3D12_AWARE, TRUE ), done );
CHECKHR_GOTO( m_spMFAttributes->SetString( MFT_ENUM_HARDWARE_VENDOR_ID_Attribute, L"VEN_1414" ), done );
CHECKHR_GOTO( m_spMFAttributes->SetString( MFT_ENUM_HARDWARE_URL_Attribute, g_pMFTFriendlyName ), done );
CHECKHR_GOTO( m_spMFAttributes->SetString( MFT_FRIENDLY_NAME_Attribute, g_pMFTFriendlyName ), done );
// Set up IMFMediaEventQueue
CHECKHR_GOTO( MFCreateEventQueue( &m_spEventQueue ), done );
CHECKHR_GOTO( Initialize(), done );
done:
return hr;
}
// factory function
HRESULT
CDX12EncHMFT::CreateInstance( __deref_out CDX12EncHMFT **ppDX12EncHMFT )
{
HRESULT hr = S_OK;
ComPtr<CDX12EncHMFT> spDX12EncHMFT = Microsoft::WRL::Make<CDX12EncHMFT>();
CHECKNULL_GOTO( ppDX12EncHMFT, E_INVALIDARG, done );
CHECKNULL_GOTO( spDX12EncHMFT, E_OUTOFMEMORY, done );
CHECKHR_GOTO( spDX12EncHMFT->RuntimeClassInitialize(), done );
*ppDX12EncHMFT = spDX12EncHMFT.Detach();
done:
return hr;
}

View file

@ -0,0 +1,556 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#define UNICODE
#include "util/u_video.h"
#include "vl/vl_winsys.h"
#include "pipe_headers.h"
#include <directx/d3d12.h>
#include <directx/d3d12video.h>
#include "idl/dx12enchmft.h"
#include <Unknwn.h>
#include <agents.h>
#include <codecapi.h>
#include <combaseapi.h>
#include <concrt.h>
#include <initguid.h>
#include <mfapi.h>
#include <mfd3d12.h> // For IMFD3D12SynchronizationObjectCommands
#include <mferror.h>
#include <mfidl.h> // For IMFRealTimeClientEx, IMFShutdown
#include <mfobjects.h> // For IMFActivate, IMFObjectInformation, IMFMediaEventGenerator
#include <mftransform.h> // For IMFTransform
#include <mutex>
#include <strmif.h> // For ICodecAPI
#include <wrl.h>
#include "wil/com.h"
#include "wil/resource.h"
#include <wrl/client.h>
#include <wrl/implements.h>
#include "macros.h"
#include "mfd3dmanager.h"
#include <d3d11_1.h>
#include <d3d11_3.h>
#include <d3d11_4.h>
#include <dxgi1_2.h>
#include "context.h"
#include "encoder_capabilities.h"
#include "reference_frames_tracker.h"
using namespace concurrency;
using namespace Microsoft::WRL;
using Microsoft::WRL::ComPtr;
#define NUM_INPUT_TYPES 3
extern MFT_REGISTER_TYPE_INFO rgOutputInfo;
extern MFT_REGISTER_TYPE_INFO rgInputInfo[NUM_INPUT_TYPES];
extern const wchar_t *g_pMFTFriendlyName;
#ifndef FOURCC_H264
#define FOURCC_H264 MAKEFOURCC( 'H', '2', '6', '4' )
#endif
#ifndef FOURCC_H265
#define FOURCC_H265 MAKEFOURCC( 'H', '2', '6', '5' )
#endif
#ifndef FOURCC_HEVC
#define FOURCC_HEVC MAKEFOURCC( 'H', 'E', 'V', 'C' )
#endif
#ifndef FOURCC_avc1
#define FOURCC_avc1 MAKEFOURCC( 'a', 'v', 'c', '1' )
#endif
#ifndef FOURCC_AV01
#define FOURCC_AV01 MAKEFOURCC( 'A', 'V', '0', '1' )
#endif
#ifndef FOURCC_NV12
#define FOURCC_NV12 MAKEFOURCC( 'N', 'V', '1', '2' )
#endif
#ifndef FOURCC_P010
#define FOURCC_P010 MAKEFOURCC( 'P', '0', '1', '0' )
#endif
#ifndef FOURCC_AYUV
#define FOURCC_AYUV MAKEFOURCC( 'A', 'Y', 'U', 'V' )
#endif
#ifndef FOURCC_Y210
#define FOURCC_Y210 MAKEFOURCC( 'Y', '2', '1', '0' )
#endif
#ifndef FOURCC_Y410
#define FOURCC_Y410 MAKEFOURCC( 'Y', '4', '1', '0' )
#endif
#ifndef FOURCC_YUY2
#define FOURCC_YUY2 MAKEFOURCC( 'Y', 'U', 'Y', '2' )
#endif
#ifdef SUPPORT_BFRAMES
#define HMFT_MAX_BFRAMES 1
#else
#define HMFT_MAX_BFRAMES 0
#endif
#define HMFT_MIN_WIDTH 34
#define HMFT_MIN_HEIGHT 34
#define HMFT_MIN_BITS_PER_SLICE 256
#define AVC_MAX_QP 51
#define AVC_DEFAULT_QP 26
#define HMFT_MAX_TEMPORAL_LAYERS 2
constexpr const eAVEncH265VProfile eAVEncH265VProfile_Main_422_8 = (eAVEncH265VProfile) 23;
typedef enum tVideoFormat
{
VIDFMT_COMPONENT = 0,
VIDFMT_PAL,
VIDFMT_NTSC,
VIDFMT_SECAM,
VIDFMT_MAC,
VIDFMT_UNSPECIFIED,
VIDFMT_MAX
} VideoFormat;
typedef enum tColorPrimary
{
COLORPRIM_BT709_5 = 0,
COLORPRIM_UNSPECIFIED,
COLORPRIM_BT470_6M,
COLORPRIM_BT470_6BG,
COLORPRIM_SMPTE_170M,
COLORPRIM_SMPTE_240M,
COLORPRIM_FILM,
COLORPRIM_MAX
} ColorPrimary;
typedef enum tColorTransfer
{
COLORXFER_BT709_5 = 0,
COLORXFER_UNSPECIFIED,
COLORXFER_BT470_6M,
COLORXFER_BT470_6BG,
COLORXFER_SMPTE_170M,
COLORXFER_SMPTE_240M,
COLORXFER_LINEAR,
COLORXFER_LOG100,
COLORXFER_LOG316,
COLORXFER_IEC,
COLORXFER_BT1361,
COLORXFER_MAX
} ColorTransfer;
typedef enum tColorMatrix
{
COLORMATRIX_GBR = 0,
COLORMATRIX_BT709_5,
COLORMATRIX_UNSPECIFIED,
COLORMATRIX_FCC47,
COLORMATRIX_BT470_6BG,
COLORMATRIX_SMPTE170M,
COLORMATRIX_SMPTE240M,
COLORMATRIX_YCgCo,
COLORMATRIX_MAX
} ColorMatrix;
typedef struct tSampleAspectRatio
{
unsigned short usWidth;
unsigned short usHeight;
} SampleAspectRatio;
typedef struct tVideoSignalType
{
VideoFormat eVideoFormat;
BOOL bVideoFullRangeFlag;
BOOL bColorInfoPresent;
ColorPrimary eColorPrimary;
ColorTransfer eColorTransfer;
ColorMatrix eColorMatrix;
} VideoSignalType;
// VUI
typedef struct tVUInfo
{
BOOL bEnableSAR;
SampleAspectRatio stSARInfo;
BOOL bEnableVST;
VideoSignalType stVidSigType;
// TODO: This seems incomplete, more VUI params to fill and then plumb to DX12 backend VUI header writer
} VUInfo;
// Slice control modes supported by the encoder.
typedef enum tSliceControlMode
{
SLICE_CONTROL_MODE_MB = 0,
SLICE_CONTROL_MODE_BITS = 1,
SLICE_CONTROL_MODE_MB_ROW = 2,
SLICE_CONTROL_MODE_MAX
} SliceControlMode;
// DirtyRect modes supported by the encoder.
typedef enum tDirtyRectMode
{
DIRTY_RECT_MODE_OFF = 0,
DIRTY_RECT_MODE_USE_FRAME_NUM = 1,
DIRTY_RECT_MODE_IGNORE_FRAME_NUM = 2,
DIRTY_RECT_MODE_MAX
} DirtyRectMode;
// Gradual intra refresh modes supported by the encoder.
typedef enum IntraRefreshMode
{
HMFT_INTRA_REFRESH_MODE_NONE = 0,
HMFT_INTRA_REFRESH_MODE_PERIODIC = 1,
HMFT_INTRA_REFRESH_MODE_CONTINUAL = 2,
HMFT_INTRA_REFRESH_MODE_MAX
} IntraRefreshMode;
// MFSampleExtension_VideoEncodeQPMap {2C68A331-B712-49CA-860A-3A1D58237D88}
// Type: PTR
// Used by to return the QP map of the current frame
DEFINE_GUID( MFSampleExtension_VideoEncodeQPMap, 0x2c68a331, 0xb712, 0x49ca, 0x86, 0xa, 0x3a, 0x1d, 0x58, 0x23, 0x7d, 0x88 );
#if VIDEO_CODEC_H264ENC
#define HMFT_GUID "8994db7c-288a-4c62-a136-a3c3c2a208a8"
#elif VIDEO_CODEC_H265ENC
#define HMFT_GUID "e7ffb8eb-fa0b-4fb0-acdf-1202f663cde5"
#elif VIDEO_CODEC_AV1ENC
#define HMFT_GUID "1a6f3150-b121-4ce9-9497-50fedb3dcb70"
#endif
#define MFT_INPUT_QUEUE_DEPTH 8
class __declspec( uuid( HMFT_GUID ) ) CDX12EncHMFT : CMFD3DManager,
public RuntimeClass<RuntimeClassFlags<RuntimeClassType::WinRtClassicComMix>,
IMFTransform,
IMFRealTimeClientEx,
ICodecAPI,
IMFMediaEventGenerator,
IMFShutdown>
{
InspectableClass( RuntimeClass_DX12Encoder_CDX12EncHMFT, BaseTrust )
protected : enum {
EVENT_QUIT,
EVENT_INPUT,
MAX_EVENTS
};
static void WINAPI xThreadProc( void *pCtx );
HANDLE m_hThread = NULL;
DWORD m_dwThreadId = 0;
private:
~CDX12EncHMFT();
HRESULT InitializeEncoder( pipe_video_profile VideoProfile, UINT32 Width, UINT32 Height );
void CleanupEncoder();
HRESULT CreateGOPTracker( uint32_t textureWidth, uint32_t textureHeight );
event m_eventHaveInput;
// signal that the queue has data via m_eventHaveInput
concurrent_queue<LPDX12EncodeContext> m_EncodingQueue; // (MFT_INPUT_QUEUE_DEPTH)
concurrent_queue<IMFSample *> m_OutputQueue;
wil::critical_section m_OutputQueueLock;
HRESULT SetEncodingParameters( IMFAttributes *pMFAttributes );
HRESULT GetCodecPrivateData( LPBYTE pSPSPPSData, DWORD dwSPSPPSDataLen, LPDWORD lpdwSPSPPSDataLen );
// ProcessMessage Event Handlers
HRESULT OnDrain();
HRESULT OnFlush();
HRESULT ConfigureSampleAllocator();
HRESULT UpdateAvailableInputType();
HRESULT InternalCheckInputType( IMFMediaType *pType );
HRESULT InternalCheckOutputType( IMFMediaType *pType );
HRESULT CheckMediaType( IMFMediaType *pmt, bool bInputType );
#if VIDEO_CODEC_H264ENC
HRESULT CheckMediaTypeLevel(
IMFMediaType *pmt, int width, int height, const encoder_capabilities &encoderCapabilities, eAVEncH264VLevel *pLevel ) const;
#elif VIDEO_CODEC_H265ENC
HRESULT CheckMediaTypeLevel(
IMFMediaType *pmt, int width, int height, const encoder_capabilities &encoderCapabilities, eAVEncH265VLevel *pLevel ) const;
#elif VIDEO_CODEC_AV1ENC
HRESULT CheckMediaTypeLevel(
IMFMediaType *pmt, int width, int height, const encoder_capabilities &encoderCapabilities, eAVEncAV1VLevel *pLevel ) const;
#endif
HRESULT ValidateDirtyRects( const LPDX12EncodeContext pDX12EncodeContext, const DIRTYRECT_INFO *pDirtyRectInfo );
UINT32 GetMaxReferences( unsigned int width, unsigned int height );
HRESULT CheckShutdown();
// MFT Attributes
ComPtr<IMFAttributes> m_spMFAttributes;
// MFT event-queue
ComPtr<IMFMediaEventQueue> m_spEventQueue;
// input stream
ComPtr<IMFMediaType> m_spAvailableInputType;
ComPtr<IMFMediaType> m_spInputType;
DWORD m_dwInputTypeStride;
DWORD m_dwInputOffsetX;
DWORD m_dwInputOffsetY;
BOOL m_bEncodingStarted = FALSE;
GUID m_InputSubType;
VUInfo m_VUIInfo = {};
// output stream
ComPtr<IMFMediaType> m_spAvailableOutputType;
ComPtr<IMFMediaType> m_spOutputType;
UINT32 m_uiOutputWidth = 0;
UINT32 m_uiOutputHeight = 0;
UINT32 m_uiOutputBitrate = 0;
MFRatio m_FrameRate = { 30, 1 }; // default to 30fps
MFRatio m_PixelAspectRatio = { 1, 1 }; // default to 1:1
MFNominalRange m_eNominalRange = MFNominalRange_16_235;
// TODO%%% Convert this to a map based on CODECAPI_guidname...
BOOL m_bForceKeyFrame = FALSE;
UINT32 m_uiRateControlMode = eAVEncCommonRateControlMode_CBR;
BOOL m_bRateControlModeSet = FALSE;
UINT32 m_uiMaxLongTermReferences = 0;
UINT32 m_uiTrustModeLongTermReferences = 0;
BOOL m_bLayerCountSet = FALSE;
UINT32 m_uiLayerCount = 1;
UINT32 m_uiSelectedLayer = 0;
UINT32 m_uiQualityVsSpeed = 33;
UINT32 m_uiMeanBitRate;
BOOL m_bMeanBitRateSet = FALSE;
UINT32 m_uiPeakBitRate = 0;
BOOL m_bPeakBitRateSet = FALSE;
UINT32 m_uiBufferSize = 0;
BOOL m_bBufferSizeSet = FALSE;
UINT32 m_uiBufferInLevel = 0;
BOOL m_bBufferInLevelSet = FALSE;
UINT32 m_uiGopSize = 30; // ~1s worth as a default
BOOL m_bGopSizeSet = FALSE;
UINT32 m_uiBFrameCount = 0;
UINT32 m_uiContentType = eAVEncVideoContentType_Unknown;
BOOL m_bContentTypeSet = FALSE;
UINT32 m_uiMinQP = 0;
BOOL m_bMinQPSet = FALSE;
UINT32 m_uiMaxQP = AVC_MAX_QP;
BOOL m_bMaxQPSet = FALSE;
UINT32 m_uiSPSID = 0;
BOOL m_bSPSIDSet = FALSE;
UINT32 m_uiPPSID = 0;
BOOL m_bPPSIDSet = FALSE;
UINT32 m_uiLTRBufferControl = 0;
BOOL m_bLTRBufferControlSet = FALSE;
UINT32 m_uiMarkLTRFrame;
BOOL m_bMarkLTRFrameSet = FALSE;
UINT32 m_uiUseLTRFrame;
BOOL m_bUseLTRFrameSet = FALSE;
UINT32 m_uiSliceControlMode = SLICE_CONTROL_MODE_MB;
BOOL m_bSliceControlModeSet = FALSE;
UINT32 m_uiSliceControlSize = 0;
BOOL m_bSliceControlSizeSet = FALSE;
BOOL m_bMaxNumRefFrameSet = FALSE;
#if VIDEO_CODEC_H264ENC
UINT32 m_uiMaxNumRefFrame = PIPE_H264_MAX_REFERENCES;
#elif VIDEO_CODEC_H265ENC
UINT32 m_uiMaxNumRefFrame = PIPE_H265_MAX_REFERENCES;
#elif VIDEO_CODEC_AV1ENC
UINT32 m_uiMaxNumRefFrame = PIPE_AV1_MAX_REFERENCES;
#endif
#if VIDEO_CODEC_H264ENC
eAVEncH264VProfile m_uiProfile = eAVEncH264VProfile_Main;
eAVEncH264VLevel m_uiLevel = eAVEncH264VLevel5;
const D3D12_VIDEO_ENCODER_CODEC m_Codec = D3D12_VIDEO_ENCODER_CODEC_H264;
enum pipe_video_profile m_outputPipeProfile = PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN;
#elif VIDEO_CODEC_H265ENC
eAVEncH265VProfile m_uiProfile = eAVEncH265VProfile_Main_420_8;
eAVEncH265VLevel m_uiLevel = eAVEncH265VLevel5;
const D3D12_VIDEO_ENCODER_CODEC m_Codec = D3D12_VIDEO_ENCODER_CODEC_HEVC;
enum pipe_video_profile m_outputPipeProfile = PIPE_VIDEO_PROFILE_HEVC_MAIN;
#elif VIDEO_CODEC_AV1ENC
eAVEncAV1VProfile m_uiProfile = eAVEncAV1VProfile_Main_420_8;
eAVEncAV1VLevel m_uiLevel = eAVEncAV1VLevel5;
const D3D12_VIDEO_ENCODER_CODEC m_Codec = D3D12_VIDEO_ENCODER_CODEC_AV1;
enum pipe_video_profile m_outputPipeProfile = PIPE_VIDEO_PROFILE_AV1_MAIN;
#endif
UINT32 m_uiMeanAbsoluteDifference = 0;
UINT32 m_uiIntraRefreshMode = 0;
UINT32 m_uiIntraRefreshSize = 0;
eAVScenarioInfo m_eScenarioInfo = eAVScenarioInfo_Unknown;
UINT32 m_uiEnableInLoopBlockFilter = 0;
BOOL m_bVideoROIEnabled = FALSE;
UINT32 m_uiDirtyRectEnabled = 0;
UINT32 m_uiQuality[3] = { 65, 65, 65 }; // Default value for AVEncCommonQuality is 65
uint32_t m_uiEncodeFrameTypeIQP[3] = { AVC_DEFAULT_QP, AVC_DEFAULT_QP, AVC_DEFAULT_QP };
uint32_t m_uiEncodeFrameTypePQP[3] = { AVC_DEFAULT_QP, AVC_DEFAULT_QP, AVC_DEFAULT_QP };
uint32_t m_uiEncodeFrameTypeBQP[3] = { AVC_DEFAULT_QP, AVC_DEFAULT_QP, AVC_DEFAULT_QP };
BOOL m_bEncodeQPSet = FALSE;
BOOL m_bLowLatency = FALSE;
BOOL m_bCabacEnable = TRUE;
struct pipe_video_codec *m_pPipeVideoCodec = nullptr;
reference_frames_tracker *m_pGOPTracker = nullptr;
enum pipe_format m_inputPipeFormat = PIPE_FORMAT_NV12;
// Fences used to synchronize different upstream textures
// types (e.g DX12, DX11, CPU buffer) with the pipe interface
ComPtr<ID3D11Fence> m_spStagingFence11;
ComPtr<ID3D12Fence> m_spStagingFence12;
struct pipe_fence_handle *m_pPipeFenceHandle = nullptr;
HANDLE m_hSharedFenceHandle = nullptr;
uint64_t m_SyncFenceValue = 1;
// Cached encoder capabilities
class encoder_capabilities m_EncoderCapabilities = {};
// state management
bool m_bShutdown = false;
bool m_bInitialized = false;
bool m_bStreaming = false;
bool m_bDraining = false;
bool m_bFlushing = false;
event m_eventInputDrained;
DWORD m_dwNeedInputCount = 0;
DWORD m_dwProcessInputCount = 0;
DWORD m_dwHaveOutputCount = 0;
DWORD m_dwProcessOutputCount = 0;
class wil::critical_section m_lock;
class wil::critical_section m_lockShutdown;
class wil::critical_section m_encoderLock;
bool m_bExitThread = false;
bool m_bUnlocked = false;
HRESULT IsUnlocked( void );
HRESULT PrepareForEncodeHelper( LPDX12EncodeContext pDX12EncodeContext, bool dirtyRectFrameNumSet, uint32_t dirtyRectFrameNum );
HRESULT PrepareForEncode( IMFSample *pSample, LPDX12EncodeContext *ppDX12EncodeContext );
std::vector<BYTE> m_pDirtyRectBlob = std::vector<BYTE>( sizeof( DIRTYRECT_INFO ) );
public:
CDX12EncHMFT();
CDX12EncHMFT( LPUNKNOWN pUnk, HRESULT *phr );
STDMETHOD( RuntimeClassInitialize )();
HRESULT Initialize();
HRESULT OnInputTypeChanged();
HRESULT OnOutputTypeChanged();
public:
static HRESULT CreateInstance( __deref_out CDX12EncHMFT **ppDX12EncHMFT );
// ---------------------------------------------------------------------------------------------------------
// IMFTransform (https://learn.microsoft.com/en-us/windows/win32/api/mftransform/nn-mftransform-imftransform)
// ---------------------------------------------------------------------------------------------------------
STDMETHOD( GetAttributes )( IMFAttributes **ppAttributes );
STDMETHOD( GetOutputStreamAttributes )( DWORD dwOutputStreamID, IMFAttributes **ppAttributes );
STDMETHOD( GetOutputStreamInfo )( DWORD dwOutputStreamIndex, MFT_OUTPUT_STREAM_INFO *pStreamInfo );
STDMETHOD( GetInputStreamAttributes )( DWORD dwInputStreamID, IMFAttributes **ppAttributes );
STDMETHOD( GetInputStreamInfo )( DWORD dwInputStreamIndex, MFT_INPUT_STREAM_INFO *pStreamInfo );
STDMETHOD( GetStreamCount )( DWORD *pcInputStreams, DWORD *pcOutputStreams );
STDMETHOD( GetStreamIDs )( DWORD dwInputIDArraySize, DWORD *pdwInputIDs, DWORD dwOutputIDArraySize, DWORD *pdwOutputIDs );
STDMETHOD( GetStreamLimits )( DWORD *pdwInputMinimum, DWORD *pdwInputMaximum, DWORD *pdwOutputMinimum, DWORD *pdwOutputMaximum );
STDMETHOD( DeleteInputStream )( DWORD dwStreamIndex );
STDMETHOD( AddInputStreams )( DWORD cStreams, DWORD *adwStreamIDs );
STDMETHOD( GetInputAvailableType )( DWORD dwInputStreamIndex, DWORD dwTypeIndex, IMFMediaType **ppType );
STDMETHOD( GetOutputAvailableType )( DWORD dwOutputStreamIndex, DWORD dwTypeIndex, IMFMediaType **ppType );
STDMETHOD( SetInputType )( DWORD dwInputStreamIndex, IN IMFMediaType *pType, DWORD dwFlags );
STDMETHOD( SetOutputType )( DWORD dwOutputStreamIndex, IN IMFMediaType *pType, DWORD dwFlags );
STDMETHOD( GetInputCurrentType )( DWORD dwInputStreamIndex, IMFMediaType **ppType );
STDMETHOD( GetOutputCurrentType )( DWORD dwOutputStreamIndex, IMFMediaType **ppType );
STDMETHOD( SetOutputBounds )( LONGLONG hnsLowerBound, LONGLONG hnsUpperBound );
STDMETHOD( GetInputStatus )( DWORD dwInputStreamIndex, DWORD *pdwFlags );
STDMETHOD( GetOutputStatus )( DWORD *pdwFlags );
STDMETHOD( ProcessEvent )( DWORD dwInputStreamIndex, IMFMediaEvent *pEvent );
STDMETHOD( ProcessMessage )( MFT_MESSAGE_TYPE eMessage, ULONG_PTR ulParam );
STDMETHOD( ProcessInput )( DWORD dwInputStreamIndex, IMFSample *pSample, DWORD dwFlags );
STDMETHOD( ProcessOutput )( DWORD dwFlags, DWORD cOutputBufferCount, MFT_OUTPUT_DATA_BUFFER *pOutputSamples, DWORD *pdwStatus );
// --------------------------------------------------------------------------------------------------------------------------
// IMFMediaEventGenerator (https://learn.microsoft.com/en-us/windows/win32/api/mfobjects/nn-mfobjects-imfmediaeventgenerator)
// --------------------------------------------------------------------------------------------------------------------------
STDMETHOD( BeginGetEvent )( IMFAsyncCallback *pCallback, IUnknown *punkState );
STDMETHOD( EndGetEvent )( IMFAsyncResult *pResult, IMFMediaEvent **ppEvent );
STDMETHOD( GetEvent )( DWORD dwFlags, IMFMediaEvent **ppEvent );
STDMETHOD( QueueEvent )( MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT *pvValue );
// --------------------------------------------------------------------------------------------
// IMFShutdown (https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nn-mfidl-imfshutdown)
// --------------------------------------------------------------------------------------------
STDMETHOD( GetShutdownStatus )( MFSHUTDOWN_STATUS *pStatus );
STDMETHOD( Shutdown )( void );
// --------------------------------------------------------------------------------------------
// ICodecAPI (https://learn.microsoft.com/en-us/windows/win32/api/strmif/nn-strmif-icodecapi)
// --------------------------------------------------------------------------------------------
STDMETHOD( IsSupported )( const GUID *Api );
STDMETHOD( IsModifiable )( const GUID *Api );
STDMETHOD( GetParameterRange )( const GUID *Api, VARIANT *ValueMin, VARIANT *ValueMax, VARIANT *SteppingDelta );
STDMETHOD( GetParameterValues )( const GUID *Api, VARIANT **Values, ULONG *ValuesCount );
STDMETHOD( GetValue )( const GUID *Api, VARIANT *Value );
STDMETHOD( SetValue )( const GUID *Api, VARIANT *Value );
STDMETHOD( GetDefaultValue )( const GUID *Api, VARIANT *Value );
STDMETHOD( RegisterForEvent )( const GUID *Api, LONG_PTR userData );
STDMETHOD( UnregisterForEvent )( const GUID *Api );
STDMETHOD( SetAllDefaults )( void );
STDMETHOD( SetValueWithNotify )( const GUID *Api, VARIANT *Value, GUID **ChangedParam, ULONG *ChangedParamCount );
STDMETHOD( SetAllDefaultsWithNotify )( GUID **ChangedParam, ULONG *ChangedParamCount );
STDMETHOD( GetAllSettings )( IStream *pStream );
STDMETHOD( SetAllSettings )( IStream *pStream );
STDMETHOD( SetAllSettingsWithNotify )( IStream *pStream, GUID **ChangedParam, ULONG *ChangedParamCount );
// ------------------------------------------------------------------------------------------------------------
// IMFRealTimeClientEx (https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nn-mfidl-imfrealtimeclientex)
// ------------------------------------------------------------------------------------------------------------
STDMETHOD( RegisterThreadsEx )( DWORD *pdwTaskIndex, LPCWSTR wszClassName, LONG lBasePriority );
STDMETHOD( UnregisterThreads )( void );
STDMETHOD( SetWorkQueueEx )( DWORD dwMultithreadedWorkQueueId, LONG lWorkItemBasePriority );
};
ActivatableClass( CDX12EncHMFT );
CoCreatableClass( CDX12EncHMFT );

View file

@ -0,0 +1,60 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#define CHECKHR_GOTO( hresult, label ) \
{ \
hr = hresult; \
if( FAILED( hr ) ) \
{ \
debug_printf( "\nerror in %s, line=%u in %s hr=0x%08x\n", __FUNCTION__, __LINE__, __FILE__, hr ); \
goto label; \
} \
}
#define CHECKHR_HRGOTO( hresult, newhresult, label ) \
{ \
hr = hresult; \
if( FAILED( hr ) ) \
{ \
CHECKHR_GOTO( newhresult, label ); \
} \
}
#define CHECKBOOL_GOTO( exp, err, label ) \
if( !( exp ) ) \
{ \
CHECKHR_GOTO( err, label ); \
}
#define CHECKNULL_GOTO( exp, err, label ) CHECKBOOL_GOTO( ( exp ), err, label )
#define SAFE_RELEASE( x ) \
if( ( x ) ) \
( x )->Release();
#define SAFE_DELETE( x ) \
if( ( x ) ) \
{ \
delete ( x ); \
( x ) = nullptr; \
}
#define SAFE_CLOSEHANDLE( x ) \
if( ( x ) ) \
{ \
CloseHandle( ( x ) ); \
( x ) = nullptr; \
}

View file

@ -0,0 +1,108 @@
# Copyright © Microsoft Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
fs = import('fs')
mf_cpp_args = cc.get_supported_arguments([
'/we4267',
'/we4996',
'/we4146',
'/we4244',
'/we4389',
'/we4838',
'/we4302',
'/we4018',
'/we4056',
'/we4305',
'/we4351',
'/we4756',
'/we4800',
'/we4291',
'/we4624',
'/we4309',
'/we5105',
'/we4020',
'/we4024',
'/we4189',
])
if get_option('mediafoundation-store-dll')
mf_cpp_args += '-DBUILD_FOR_MSDK' # Can use #if BUILD_FOR_MSDK in .cpp code
endif
mediafoundation_files = files(
'codecapi.cpp',
'encode.cpp',
'encode_av1.cpp',
'encode_h264.cpp',
'encode_hevc.cpp',
'encoder_capabilities.cpp',
'hmft_entrypoints.cpp',
'mfbufferhelp.cpp',
'mfd3dmanager.cpp',
'mfmediaeventgenerator.cpp',
'mfpipeinterop.cpp',
'mfrealtimeclientex.cpp',
'mfshutdown.cpp',
'mftransform.cpp',
'dpb_buffer_manager.cpp',
'reference_frames_tracker_av1.cpp',
'reference_frames_tracker_h264.cpp',
'reference_frames_tracker_hevc.cpp',
'videobufferlock.cpp',
'wpptrace.cpp',
)
mediafoundation_wpp_args = ''
foreach f : mediafoundation_files
mediafoundation_wpp_args += fs.relative_to(f, 'a:\\').replace('/', '\\') + '\r\n'
endforeach
mediafoundation_wpp_rsp = configure_file(
input : meson.current_source_dir() + '/wppconfig/wpp_args.rsp.template',
output : 'mediafoundation_wpp_args.rsp',
configuration : {'WPP_ARGS' : mediafoundation_wpp_args}
)
mediafoundation_wpp_preprocess = custom_target(
'mediafoundation_wpp_preprocess',
capture : true,
build_always: true,
input : mediafoundation_files,
output : 'mediafoundation_wpp_preprocess.dummy.h',
command : ['tracewpp', '-cfgdir:@CURRENT_SOURCE_DIR@/wppconfig/rev1', '-scan:@CURRENT_SOURCE_DIR@/wpptrace.h', '-odir:@OUTDIR@', '@' + fs.relative_to(mediafoundation_wpp_rsp, 'a:\\')]
)
gallium_mf_name = get_option('mediafoundation-windows-dll-name')
mediafoundation_st = static_library(
'mediafoundation_st',
mediafoundation_files,
mediafoundation_wpp_preprocess,
gnu_symbol_visibility : 'hidden',
cpp_args : mf_cpp_args,
override_options: ['cpp_std=c++20'],
include_directories : [inc_include, inc_src, inc_mesa, inc_gallium, inc_gallium_aux],
dependencies : [idep_mesautil, dep_dxheaders],
)
if with_gallium_mediafoundation_test
subdir('test')
endif

View file

@ -0,0 +1,264 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "hmft_entrypoints.h"
#include "videobufferlock.h"
// utility functions to retrieve plane information from IMFMediaType
HRESULT
MFTypeToBitmapInfo(
IMFMediaType *pType, UINT32 *pcbActualBytesPerLine, UINT32 *punLines, UINT32 *pcbSBytesPerLine, UINT32 *punSLines )
{
HRESULT hr = S_OK;
LONG lMinPitch;
UINT32 unWidth, unHeight;
UINT32 cbImageSize, cbActualBytesPerLine, unLines;
GUID format;
CHECKHR_GOTO( pType->GetGUID( MF_MT_SUBTYPE, &format ), done );
CHECKHR_GOTO( MFGetAttributeSize( pType, MF_MT_FRAME_SIZE, &unWidth, &unHeight ), done );
CHECKHR_GOTO( MFCalculateImageSize( format, unWidth, unHeight, &cbImageSize ), done );
CHECKHR_GOTO( MFGetStrideForBitmapInfoHeader( format.Data1, unWidth, &lMinPitch ), done );
cbActualBytesPerLine = abs( lMinPitch );
// dual half-planar formats
if( format == MFVideoFormat_YV12 || format == MFVideoFormat_IYUV || format == MFVideoFormat_I420 )
{
unLines = cbImageSize / ( cbActualBytesPerLine + cbActualBytesPerLine / 2 );
if( nullptr != pcbSBytesPerLine )
*pcbSBytesPerLine = cbActualBytesPerLine / 2;
// processing 2 half planes with half lines, should process double lines
if( nullptr != punSLines )
*punSLines = unLines;
}
else
{
unLines = cbImageSize / cbActualBytesPerLine;
if( nullptr != pcbSBytesPerLine )
*pcbSBytesPerLine = 0;
if( nullptr != punSLines )
*punSLines = 0;
}
if( nullptr != pcbActualBytesPerLine )
*pcbActualBytesPerLine = cbActualBytesPerLine;
if( nullptr != punLines )
*punLines = unLines;
done:
return hr;
}
// utility function to retrieve default image size from IMFMediaType
HRESULT
MFTypeToImageSize( IMFMediaType *pType, UINT32 *pcbSize )
{
HRESULT hr = S_OK;
UINT32 unWidth, unHeight;
UINT32 cbImageSize;
GUID format;
CHECKHR_GOTO( pType->GetGUID( MF_MT_SUBTYPE, &format ), done );
CHECKHR_GOTO( MFGetAttributeSize( pType, MF_MT_FRAME_SIZE, &unWidth, &unHeight ), done );
CHECKHR_GOTO( MFCalculateImageSize( format, unWidth, unHeight, &cbImageSize ), done );
*pcbSize = cbImageSize;
done:
return hr;
}
// utility function to retrieve D3D11 texture from IMFMediaBuffer
HRESULT
MFBufferToDXType( _In_ IMFMediaBuffer *pBuffer, _Outptr_ ID3D11Texture2D **ppTexture, _Out_ UINT *uiViewIndex )
{
HRESULT hr = S_OK;
ComPtr<IMFDXGIBuffer> spDXGIBuffer;
hr = pBuffer->QueryInterface( __uuidof( IMFDXGIBuffer ), (LPVOID *) ( &spDXGIBuffer ) );
if( SUCCEEDED( hr ) )
{
hr = spDXGIBuffer->GetResource( __uuidof( ID3D11Texture2D ), (LPVOID *) ppTexture );
}
if( SUCCEEDED( hr ) && uiViewIndex != nullptr )
{
hr = spDXGIBuffer->GetSubresourceIndex( uiViewIndex );
}
return hr;
}
// utility function to retrieve D3D12 texture from IMFMediaBuffer
HRESULT
MFBufferToDXType( _In_ IMFMediaBuffer *pBuffer, _Outptr_ ID3D12Resource **ppTexture, _Out_ UINT *uiViewIndex )
{
HRESULT hr = S_OK;
ComPtr<IMFDXGIBuffer> spDXGIBuffer;
hr = pBuffer->QueryInterface( __uuidof( IMFDXGIBuffer ), (LPVOID *) ( &spDXGIBuffer ) );
if( SUCCEEDED( hr ) )
{
hr = spDXGIBuffer->GetResource( __uuidof( ID3D12Resource ), (LPVOID *) ppTexture );
}
if( SUCCEEDED( hr ) && uiViewIndex != nullptr )
{
hr = spDXGIBuffer->GetSubresourceIndex( uiViewIndex );
}
return hr;
}
// utility function to copy MFSample
HRESULT
MFCopySample( IMFSample *dest, IMFSample *src, IMFMediaType *pmt )
{
HRESULT hr = S_OK;
ComPtr<IMFMediaBuffer> pOutput, pInput;
ComPtr<ID3D11Texture2D> srcTexture, dstTexture;
ComPtr<ID3D11Device> pSrcDevice, pDstDevice;
ComPtr<ID3D11DeviceContext> pCtx;
UINT uiSrcIndex, uiDstIndex;
DWORD dwBufferCount;
UINT32 dwSize = 0;
BOOL bUsedDX = FALSE;
UINT32 cbActualBytesPerLine, unLines, cbSActualBytes, unSLines;
CHECKHR_GOTO( MFTypeToBitmapInfo( pmt, &cbActualBytesPerLine, &unLines, &cbSActualBytes, &unSLines ), done );
if( !dest || !src )
{
return E_POINTER;
}
(void) src->CopyAllItems( dest );
LONGLONG hnsTime;
hr = src->GetSampleTime( &hnsTime );
if( SUCCEEDED( hr ) )
{
hr = dest->SetSampleTime( hnsTime );
}
hr = S_OK;
LONGLONG hnsDuration;
hr = src->GetSampleDuration( &hnsDuration );
if( SUCCEEDED( hr ) )
{
hr = dest->SetSampleDuration( hnsDuration );
}
hr = S_OK;
hr = src->GetBufferCount( &dwBufferCount );
if( SUCCEEDED( hr ) )
for( DWORD x = 0; x < dwBufferCount; x++ )
{
hr = src->GetBufferByIndex( x, &pInput );
if( FAILED( hr ) )
{
break;
}
hr = dest->GetBufferByIndex( x, &pOutput );
if( FAILED( hr ) )
{
hr = MFCreateMediaBufferFromMediaType( pmt, hnsDuration, 0, 0, &pOutput );
if( FAILED( hr ) )
{
break;
}
hr = dest->AddBuffer( pOutput.Get() );
if( FAILED( hr ) )
{
break;
}
}
// Try to use DX if available
if( SUCCEEDED( MFBufferToDXType( pOutput.Get(), &srcTexture, &uiSrcIndex ) ) &&
SUCCEEDED( MFBufferToDXType( pInput.Get(), &dstTexture, &uiDstIndex ) ) )
{
srcTexture->GetDevice( &pSrcDevice );
dstTexture->GetDevice( &pDstDevice );
if( pSrcDevice != pDstDevice )
{
// TODO: if both are shared we can keyed mutex them across
}
else
{
pSrcDevice->GetImmediateContext( &pCtx );
pCtx->CopySubresourceRegion( dstTexture.Get(), uiDstIndex, 0, 0, 0, srcTexture.Get(), uiSrcIndex, NULL );
bUsedDX = TRUE;
}
hr = MFTypeToImageSize( pmt, &dwSize );
if( FAILED( hr ) )
{
break;
}
}
if( !bUsedDX )
{
VideoBufferLock inputLock( pInput.Get(), pmt );
VideoBufferLock outputLock( pOutput.Get(), pmt );
hr = inputLock.lock( MF2DBuffer_LockFlags_Read );
if( FAILED( hr ) )
{
break;
}
hr = outputLock.lock( MF2DBuffer_LockFlags_Write );
if( FAILED( hr ) )
{
break;
}
hr = MFCopyImage( outputLock.data(),
outputLock.stride(),
inputLock.data(),
inputLock.stride(),
cbActualBytesPerLine,
unLines );
if( FAILED( hr ) )
{
break;
}
if( unSLines )
{
hr = MFCopyImage( outputLock.data(),
outputLock.stride(),
inputLock.data(),
inputLock.stride(),
cbSActualBytes,
unSLines );
}
dwSize = inputLock.size();
}
hr = pOutput->SetCurrentLength( dwSize );
if( FAILED( hr ) )
{
break;
}
}
done:
return hr;
}

View file

@ -0,0 +1,46 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include <mfidl.h>
// Given a media type report plane information
// pType - type to get info for
// pcbActualBytesPerLine - line byte count for first plane
// punLines - line count for first plane
// pcbSBytesPerLine - line byte count for second plane (may be null)
// punSLines - line count for second plane (may be null)
HRESULT
MFTypeToBitmapInfo( IMFMediaType *pType,
UINT32 *pcbActualBytesPerLine,
UINT32 *punLines,
UINT32 *pcbSBytesPerLine = nullptr,
UINT32 *punSLines = nullptr );
// Gets the default size of an image for pmt
HRESULT
MFTypeToImageSize( IMFMediaType *pType, UINT32 *pcbSize );
// Copy a sample from src to dst for the given media type (copies buffer contents)
HRESULT
MFCopySample( IMFSample *dest, IMFSample *src, IMFMediaType *pmt );

View file

@ -0,0 +1,374 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include "mfd3dmanager.h"
#include <algorithm>
#include <iterator>
#include <map>
#include <vector>
#include "wpptrace.h"
#include "mfd3dmanager.tmh"
static IDXCoreAdapter *
choose_dxcore_adapter( void )
{
HRESULT hr = S_OK;
std::vector<MFTAdapterInfo> adapter_infos;
ComPtr<IDXCoreAdapterFactory> spFactory;
ComPtr<IDXCoreAdapterList> spAdapterList;
adapter_infos.reserve( 2 );
hr = DXCoreCreateAdapterFactory( IID_IDXCoreAdapterFactory, (void **) spFactory.GetAddressOf() );
if( FAILED( hr ) )
{
debug_printf( "CMFD3DManager: DXCoreCreateAdapterFactory failed: %08x\n", hr );
return NULL;
}
#ifdef NTDDI_WIN11_GA
// Get all media adapters (e.g including MCDM using latest DXCore APIs)
ComPtr<IDXCoreAdapterFactory1> spFactory1;
if( SUCCEEDED( spFactory.As( &spFactory1 ) ) &&
SUCCEEDED( spFactory1->CreateAdapterListByWorkload( DXCoreWorkload::Media,
DXCoreRuntimeFilterFlags::D3D12,
DXCoreHardwareTypeFilterFlags::None,
spAdapterList.GetAddressOf() ) ) )
{
debug_printf( "CMFD3DManager: Using IDXCoreAdapterFactory1::CreateAdapterListByWorkload\n" );
}
#endif // NTDDI_WIN11_GA
// Fallback to older DXCore enumeration APIs
if( !spAdapterList &&
SUCCEEDED( spFactory->CreateAdapterList( 1, &DXCORE_ADAPTER_ATTRIBUTE_D3D12_CORE_COMPUTE, spAdapterList.GetAddressOf() ) ) )
{
debug_printf( "CMFD3DManager: Fallback to IDXCoreAdapterFactory::CreateAdapterList since "
"IDXCoreAdapterFactory1::CreateAdapterListByWorkload was not available\n" );
}
// Validate we enumerated one way or another
if( !spAdapterList )
{
debug_printf( "CMFD3DManager: Couldn't create an adapter list\n" );
return NULL;
}
adapter_infos.clear();
for( unsigned i = 0; i < spAdapterList->GetAdapterCount(); i++ )
{
ComPtr<IDXCoreAdapter> spAdapter;
MFTAdapterInfo adapter_info = {};
uint64_t driver_version = 0;
if( SUCCEEDED( spAdapterList->GetAdapter( i, spAdapter.GetAddressOf() ) ) )
{
if( FAILED( spAdapter->GetProperty( DXCoreAdapterProperty::IsIntegrated, &adapter_info.is_integrated ) ) )
continue;
if( FAILED( spAdapter->GetProperty( DXCoreAdapterProperty::HardwareID, &adapter_info.hardware_id ) ) )
continue;
if( FAILED( spAdapter->GetProperty( DXCoreAdapterProperty::InstanceLuid, &adapter_info.adapter_luid ) ) )
continue;
if( FAILED( spAdapter->GetProperty( DXCoreAdapterProperty::DriverVersion, &driver_version ) ) )
continue;
// Read into driver_version variable first since the parts in the struct are in high to low bits order
adapter_info.driver_version.part1 = ( ( driver_version >> 48 ) & 0xFFFF );
adapter_info.driver_version.part2 = ( ( driver_version >> 32 ) & 0xFFFF );
adapter_info.driver_version.part3 = ( ( driver_version >> 16 ) & 0xFFFF );
adapter_info.driver_version.part4 = ( driver_version & 0xFFFF );
adapter_infos.push_back( adapter_info );
}
}
std::map<uint32_t, std::string> vendor_id_friendly_names = {
{ 0x1002, "AMD" },
{ 0x1414, "Microsoft" },
{ 0x10DE, "NVidia" },
{ 0x8086, "Intel" },
};
const std::vector<uint32_t> vendor_preference_order = {
0x10DE, // NVidia
0x1002, // AMD
0x8086, // Intel
0x1414, // Microsoft
};
std::map<uint32_t, MFAdapterDriverVersion> driver_min_versions;
{
// Min driver versions
driver_min_versions[0x1002 /* AMD */] = { 31, 0, 0, 0 };
driver_min_versions[0x1414 /* Microsoft */] = { 10, 0, 26000, 0 }; // OS version for MSFT SW driver
driver_min_versions[0x10DE /* NVidia */] = { 31, 0, 0, 0 };
driver_min_versions[0x8086 /* Intel */] = { 31, 0, 0, 0 };
}
adapter_infos.erase(
std::remove_if( adapter_infos.begin(),
adapter_infos.end(),
[&driver_min_versions]( const MFTAdapterInfo &adapter ) {
return ( adapter.driver_version.part1 < driver_min_versions[adapter.hardware_id.vendorID].part1 ) ||
( adapter.driver_version.part2 < driver_min_versions[adapter.hardware_id.vendorID].part2 ) ||
( adapter.driver_version.part3 < driver_min_versions[adapter.hardware_id.vendorID].part3 ) ||
( adapter.driver_version.part4 < driver_min_versions[adapter.hardware_id.vendorID].part4 );
} ),
adapter_infos.end() );
std::map<uint32_t, std::vector<MFAdapterDriverVersion>> driver_denylist;
{
// Blocked driver versions
driver_denylist[0x1002 /* AMD */] = { { 31, 0, 0, 0 } };
driver_denylist[0x1414 /* Microsoft */] = { { 10, 0, 26000, 0 } }; // OS version for MSFT SW driver
driver_denylist[0x10DE /* NVidia */] = { { 31, 0, 0, 0 } };
driver_denylist[0x8086 /* Intel */] = { { 31, 0, 0, 0 } };
}
adapter_infos.erase( std::remove_if( adapter_infos.begin(),
adapter_infos.end(),
[&driver_denylist]( const MFTAdapterInfo &adapter ) {
return std::find_if( driver_denylist[adapter.hardware_id.vendorID].begin(),
driver_denylist[adapter.hardware_id.vendorID].end(),
[&adapter]( const MFAdapterDriverVersion &x ) {
return x.version == adapter.driver_version.version;
} ) != driver_denylist[adapter.hardware_id.vendorID].end();
} ),
adapter_infos.end() );
std::sort( adapter_infos.begin(), adapter_infos.end(), [vendor_preference_order]( MFTAdapterInfo a, MFTAdapterInfo b ) {
// First criteria: iGPU first
if( a.is_integrated > b.is_integrated )
return true;
if( a.is_integrated < b.is_integrated )
return false;
// Second criteria: IHV preference
size_t preferenceOrderA =
std::distance( vendor_preference_order.begin(),
std::find( vendor_preference_order.begin(), vendor_preference_order.end(), a.hardware_id.vendorID ) );
size_t preferenceOrderB =
std::distance( vendor_preference_order.begin(),
std::find( vendor_preference_order.begin(), vendor_preference_order.end(), b.hardware_id.vendorID ) );
if( preferenceOrderA < preferenceOrderB )
return true;
if( preferenceOrderA > preferenceOrderB )
return false;
// Third criteria: driver version
if( a.driver_version.version > b.driver_version.version )
return true;
if( a.driver_version.version < b.driver_version.version )
return false;
return false;
} );
debug_printf( "CMFD3DManager: Selecting adapter from adapter list...\n" );
for( size_t i = 0; i < adapter_infos.size(); i++ )
{
debug_printf( "CMFD3DManager: %s Adapter LUID (%d %d) - is_integrated %d - vendor_id 0x%x (%s) - driver_version "
"%d.%d.%d.%d \n",
( i == 0 ) ? "[SELECTED]" : "",
adapter_infos[i].adapter_luid.LowPart,
adapter_infos[i].adapter_luid.HighPart,
adapter_infos[i].is_integrated,
adapter_infos[i].hardware_id.vendorID,
vendor_id_friendly_names.count( adapter_infos[i].hardware_id.vendorID ) > 0 ?
vendor_id_friendly_names[adapter_infos[i].hardware_id.vendorID].c_str() :
"Unknown",
adapter_infos[i].driver_version.part1,
adapter_infos[i].driver_version.part2,
adapter_infos[i].driver_version.part3,
adapter_infos[i].driver_version.part4 );
}
IDXCoreAdapter *selected_adapter = NULL;
if( ( adapter_infos.size() == 0 ) || FAILED( spFactory->GetAdapterByLuid( adapter_infos[0].adapter_luid, &selected_adapter ) ) )
debug_printf( "CMFD3DManager: Error, no adapters found.\n" );
return selected_adapter;
}
CMFD3DManager::CMFD3DManager()
{ }
CMFD3DManager::~CMFD3DManager()
{
Shutdown();
}
// #define ENABLE_D3D12_DEBUG_LAYER
HRESULT
CMFD3DManager::Initialize( D3D12_VIDEO_ENCODER_CODEC codec )
{
#ifdef ENABLE_D3D12_DEBUG_LAYER
ComPtr<ID3D12Debug> spDebugController;
if( SUCCEEDED( D3D12GetDebugInterface( IID_PPV_ARGS( &spDebugController ) ) ) )
{
spDebugController->EnableDebugLayer();
}
#endif
m_codec = codec;
return S_OK;
}
HRESULT
CMFD3DManager::Shutdown( bool bReleaseDeviceManager )
{
HRESULT hr = S_OK;
m_spDevice = nullptr;
m_spVideoDevice = nullptr;
m_spDevice11 = nullptr;
m_spStagingQueue = nullptr;
if( m_spVideoSampleAllocator )
{
m_spVideoSampleAllocator->UninitializeSampleAllocator();
m_spVideoSampleAllocator = nullptr;
}
if( m_spDeviceManager != nullptr )
{
if( m_hDevice != NULL )
{
m_spDeviceManager->CloseDeviceHandle( m_hDevice );
m_hDevice = NULL;
}
if( bReleaseDeviceManager )
m_spDeviceManager = nullptr;
}
if( m_pPipeContext )
{
m_pPipeContext->destroy( m_pPipeContext );
m_pPipeContext = nullptr;
}
if( m_pVlScreen )
{
m_pVlScreen->destroy( this->m_pVlScreen );
m_pVlScreen = nullptr;
}
if( m_pWinsys )
{
m_pWinsys->destroy( this->m_pWinsys );
m_pWinsys = nullptr;
}
return hr;
}
static inline HRESULT
CreateD3D12DeviceWithMinimumSupportedFeatureLevel( IUnknown *pAdapter, ComPtr<ID3D12Device> &spDevice )
{
static const D3D_FEATURE_LEVEL levels[] = {
#if ( D3D12_SDK_VERSION >= 611 )
D3D_FEATURE_LEVEL_1_0_GENERIC,
#endif
D3D_FEATURE_LEVEL_1_0_CORE,
D3D_FEATURE_LEVEL_11_0,
};
for( uint32_t i = 0; i < ARRAYSIZE( levels ); i++ )
if( SUCCEEDED( D3D12CreateDevice( pAdapter, levels[i], IID_PPV_ARGS( spDevice.ReleaseAndGetAddressOf() ) ) ) )
return S_OK;
return E_FAIL;
}
HRESULT
CMFD3DManager::xReopenDeviceManager( bool bNewDevice )
{
HRESULT hr = S_OK;
D3D12_COMMAND_QUEUE_DESC commandQueueDesc = { D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE };
D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC capCodecData = { 0U, m_codec, false };
Shutdown( false );
CHECKHR_GOTO( m_spDeviceManager->OpenDeviceHandle( &m_hDevice ), done );
if( FAILED( m_spDeviceManager->GetVideoService( m_hDevice, IID_ID3D12Device, &m_spDevice ) ) )
{
ComPtr<IDXGIDevice> spDXGIDevice;
ComPtr<IDXGIAdapter> spDXGIAdapter;
ComPtr<IUnknown> spAdapter;
CHECKHR_GOTO( m_spDeviceManager->GetVideoService( m_hDevice, IID_ID3D11Device, &m_spDevice11 ), done );
CHECKHR_GOTO( m_spDevice11.As( &spDXGIDevice ), done );
CHECKHR_GOTO( spDXGIDevice->GetAdapter( &spDXGIAdapter ), done );
CHECKHR_GOTO( spDXGIAdapter.As( &spAdapter ), done );
// Create a D3D12 device off of the same adapter this 11 device is on
CHECKHR_GOTO( CreateD3D12DeviceWithMinimumSupportedFeatureLevel( spAdapter.Get(), m_spDevice ), done );
}
// Create a staging queue for MF to signal on input texture GPU completion
CHECKHR_GOTO( m_spDevice->CreateCommandQueue( &commandQueueDesc, IID_PPV_ARGS( &m_spStagingQueue ) ), done );
CHECKHR_GOTO( m_spDevice.As( &m_spVideoDevice ), done );
CHECKHR_GOTO( m_spVideoDevice->CheckFeatureSupport( D3D12_FEATURE_VIDEO_ENCODER_CODEC, &capCodecData, sizeof( capCodecData ) ),
done );
CHECKBOOL_GOTO( capCodecData.IsSupported, MF_E_UNSUPPORTED_D3D_TYPE, done );
done:
return hr;
}
HRESULT
CMFD3DManager::xOnSetD3DManager( ULONG_PTR ulParam )
{
HRESULT hr = S_OK;
Shutdown();
if( ulParam == 0 )
{
#if 0
// Treat 0 as "pick a default"
ComPtr<ID3D12Device> spD3D12Device;
ComPtr<IDXCoreAdapter> dxcore_adapter = choose_dxcore_adapter();
CHECKNULL_GOTO(dxcore_adapter, MF_E_DXGI_DEVICE_NOT_INITIALIZED, done);
CHECKHR_GOTO(MFCreateDXGIDeviceManager(&m_uiResetToken, &m_spDeviceManager), done);
CHECKHR_GOTO(CreateD3D12DeviceWithMinimumSupportedFeatureLevel(dxcore_adapter.Get(), spD3D12Device), done);
CHECKHR_GOTO(m_spDeviceManager->ResetDevice(spD3D12Device.Get(), m_uiResetToken), done);
#else
return hr;
#endif
}
else
{
// We've been given an IUnknown, make sure it is an IMFDXGIDeviceManager
CHECKHR_GOTO( reinterpret_cast<IUnknown *>( ulParam )->QueryInterface( IID_PPV_ARGS( &m_spDeviceManager ) ), done );
}
CHECKHR_GOTO( xReopenDeviceManager( true ), done );
CHECKNULL_GOTO( m_pWinsys = null_sw_create(), MF_E_DXGI_DEVICE_NOT_INITIALIZED, done );
CHECKNULL_GOTO( m_pVlScreen = vl_win32_screen_create_from_d3d12_device( m_spDevice.Get(), m_pWinsys ),
MF_E_DXGI_DEVICE_NOT_INITIALIZED,
done );
CHECKNULL_GOTO( m_pPipeContext = pipe_create_multimedia_context( m_pVlScreen->pscreen, false ),
MF_E_DXGI_DEVICE_NOT_INITIALIZED,
done );
CHECKHR_GOTO( MFCreateVideoSampleAllocatorEx( IID_PPV_ARGS( &m_spVideoSampleAllocator ) ), done );
done:
if( FAILED( hr ) )
{
Shutdown();
}
return hr;
}

View file

@ -0,0 +1,110 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include "pipe_headers.h"
#include "gallium/include/frontend/sw_winsys.h"
#include "gallium/winsys/sw/null/null_sw_winsys.h"
#include "util/u_video.h"
#include "vl/vl_winsys.h"
// directx/xxx has the latest headers from DirectX-Headers dependency
// and must be included _before_ any Windows SDK headers (e.g d3d11.h, etc)
#include <d3d11.h>
#include <directx/d3d12.h>
#include <directx/d3d12video.h>
#include <d3d11_4.h>
#include <mfapi.h>
#include <mferror.h>
#include <mfidl.h> // for IMFVideoSampleAllocatorEx
#include <mutex>
#include <wrl/client.h>
#include <wrl/implements.h>
#include "macros.h"
// Use the Windows SDK dxcore include (e.g directx/dxcore uses DirectX-Headers)
#include <dxcore.h>
using namespace Microsoft::WRL;
using Microsoft::WRL::ComPtr;
using namespace std;
typedef union
{
struct
{
// Driver version format is part1.part2.part3.part4 (e.g 31.0.15.5019)
uint16_t part1 : 16;
uint16_t part2 : 16;
uint16_t part3 : 16;
uint16_t part4 : 16;
};
uint64_t version; // bits field
} MFAdapterDriverVersion;
typedef struct MFTAdapterInfo
{
// DXCoreAdapterProperty::InstanceLuid
LUID adapter_luid;
// DXCoreAdapterProperty::IsIntegrated
uint32_t is_integrated;
// DXCoreAdapterProperty::HardwareID
DXCoreHardwareID hardware_id;
// DXCoreAdapterProperty::DriverVersion
MFAdapterDriverVersion driver_version;
} MFTAdapterInfo;
class CMFD3DManager
{
public:
CMFD3DManager();
~CMFD3DManager();
HRESULT Initialize( D3D12_VIDEO_ENCODER_CODEC codec );
HRESULT Shutdown( bool bReleaseDeviceManager = true );
// Set D3D manager, use in ProcessMessage
HRESULT xOnSetD3DManager( ULONG_PTR ulParam );
protected:
HRESULT xReopenDeviceManager( bool bNewDevice );
ComPtr<IMFDXGIDeviceManager> m_spDeviceManager;
ComPtr<ID3D11Device5> m_spDevice11;
ComPtr<ID3D12Device> m_spDevice;
ComPtr<ID3D12VideoDevice> m_spVideoDevice;
ComPtr<ID3D12CommandQueue> m_spStagingQueue;
ComPtr<IMFVideoSampleAllocatorEx> m_spVideoSampleAllocator; // Used for software input samples that need to be copied
UINT32 m_uiResetToken = 0;
HANDLE m_hDevice = NULL;
LUID m_currentDXAdapterLuid = { 0 };
struct vl_screen *m_pVlScreen = nullptr;
struct sw_winsys *m_pWinsys = nullptr;
struct pipe_context *m_pPipeContext = nullptr;
private:
D3D12_VIDEO_ENCODER_CODEC m_codec;
};

View file

@ -0,0 +1,93 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "hmft_entrypoints.h"
// IMFMediaEventGenerator::BeginGetEvent
// https://learn.microsoft.com/en-us/windows/win32/api/mfobjects/nf-mfobjects-imfmediaeventgenerator-begingetevent
HRESULT
CDX12EncHMFT::BeginGetEvent( IMFAsyncCallback *pCallback, IUnknown *punkState )
{
HRESULT hr = S_OK;
auto lock = m_lock.lock();
CHECKHR_GOTO( CheckShutdown(), done );
CHECKHR_GOTO( m_spEventQueue->BeginGetEvent( pCallback, punkState ), done );
done:
return hr;
}
// IMFMediaEventGenerator::EndGetEvent
// https://learn.microsoft.com/en-us/windows/win32/api/mfobjects/nf-mfobjects-imfmediaeventgenerator-endgetevent
HRESULT
CDX12EncHMFT::EndGetEvent( IMFAsyncResult *pResult, IMFMediaEvent **ppEvent )
{
HRESULT hr = S_OK;
MediaEventType met = MEUnknown;
auto lock = m_lock.lock();
CHECKHR_GOTO( CheckShutdown(), done );
CHECKHR_GOTO( m_spEventQueue->EndGetEvent( pResult, ppEvent ), done );
( *ppEvent )->GetType( &met );
debug_printf( "[dx12 hmft 0x%p] EndGetEvent - SUCCESS, type = 0x%x\n", this, met );
return hr;
done:
debug_printf( "[dx12 hmft 0x%p] EndGetEvent - FAILED 0x%x\n", this, hr );
return hr;
}
// IMFMediaEventGenerator::GetEvent
// https://learn.microsoft.com/en-us/windows/win32/api/mfobjects/nf-mfobjects-imfmediaeventgenerator-getevent
HRESULT
CDX12EncHMFT::GetEvent( DWORD dwFlags, IMFMediaEvent **ppEvent )
{
HRESULT hr = S_OK;
CHECKHR_GOTO( CheckShutdown(), done );
CHECKHR_GOTO( m_spEventQueue->GetEvent( dwFlags, ppEvent ), done );
done:
return hr;
}
// IMFMediaEventGenerator::QueueEvent
// https://learn.microsoft.com/en-us/windows/win32/api/mfobjects/nf-mfobjects-imfmediaeventgenerator-queueevent
HRESULT
CDX12EncHMFT::QueueEvent( MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT *pvValue )
{
HRESULT hr = S_OK;
ComPtr<IMFMediaEvent> spEvent;
CHECKHR_GOTO( CheckShutdown(), done );
CHECKHR_GOTO( MFCreateMediaEvent( met, guidExtendedType, hrStatus, pvValue, &spEvent ), done );
switch( met )
{
case METransformDrainComplete:
spEvent->SetUINT32( MF_EVENT_MFT_INPUT_STREAM_ID, 0 );
break;
}
CHECKHR_GOTO( m_spEventQueue->QueueEvent( spEvent.Get() ), done );
done:
return hr;
}

View file

@ -0,0 +1,441 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "hmft_entrypoints.h"
#include "mfpipeinterop.h"
// utility to convert from pipe_video_profile to AVEncVProfile
uint32_t
ConvertPipeProfileToSpecProfile( pipe_video_profile profile )
{
switch( profile )
{
case PIPE_VIDEO_PROFILE_MPEG4_AVC_CONSTRAINED_BASELINE:
return eAVEncH264VProfile_Base;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN:
return eAVEncH264VProfile_Main;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH:
return eAVEncH264VProfile_High;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH10:
return eAVEncH264VProfile_High10;
case PIPE_VIDEO_PROFILE_HEVC_MAIN:
return eAVEncH265VProfile_Main_420_8;
case PIPE_VIDEO_PROFILE_HEVC_MAIN_10:
return eAVEncH265VProfile_Main_420_10;
case PIPE_VIDEO_PROFILE_HEVC_MAIN_444:
return eAVEncH265VProfile_Main_444_8;
default:
return 0;
}
}
// utility to convert from AVEncVProfile to pipe_video_profile
enum pipe_video_profile
ConvertAVEncVProfileToPipeVideoProfile( struct vl_screen *vlScreen, UINT32 profile, D3D12_VIDEO_ENCODER_CODEC codec )
{
enum pipe_video_profile pipeProfile = PIPE_VIDEO_PROFILE_UNKNOWN;
switch( codec )
{
case D3D12_VIDEO_ENCODER_CODEC_H264:
switch( (eAVEncH264VProfile) profile )
{
case eAVEncH264VProfile_Base: // %%%TODO - Revisit, up-promoting this isn't always valid
case eAVEncH264VProfile_ConstrainedBase:
pipeProfile = PIPE_VIDEO_PROFILE_MPEG4_AVC_CONSTRAINED_BASELINE;
break;
case eAVEncH264VProfile_Main:
pipeProfile = PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN;
break;
case eAVEncH264VProfile_Extended: // We shouldn't get this, SetOutputType() should've already failed
pipeProfile = PIPE_VIDEO_PROFILE_MPEG4_AVC_EXTENDED;
break;
case eAVEncH264VProfile_ConstrainedHigh: // strict subset, so promote to high
case eAVEncH264VProfile_High:
pipeProfile = PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH;
break;
case eAVEncH264VProfile_High10:
pipeProfile = PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH10;
break;
case eAVEncH264VProfile_422:
pipeProfile = PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH422;
break;
case eAVEncH264VProfile_444:
pipeProfile = PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH444;
break;
}
break;
case D3D12_VIDEO_ENCODER_CODEC_HEVC:
switch( (eAVEncH265VProfile) profile )
{
case eAVEncH265VProfile_Main_420_8:
pipeProfile = PIPE_VIDEO_PROFILE_HEVC_MAIN;
break;
case eAVEncH265VProfile_Main_420_10:
pipeProfile = PIPE_VIDEO_PROFILE_HEVC_MAIN_10;
break;
case eAVEncH265VProfile_Main_422_8:
pipeProfile = PIPE_VIDEO_PROFILE_HEVC_MAIN_422;
break;
case eAVEncH265VProfile_Main_422_10:
pipeProfile = PIPE_VIDEO_PROFILE_HEVC_MAIN10_422;
break;
case eAVEncH265VProfile_Main_444_8:
pipeProfile = PIPE_VIDEO_PROFILE_HEVC_MAIN_444;
break;
case eAVEncH265VProfile_Main_444_10:
pipeProfile = PIPE_VIDEO_PROFILE_HEVC_MAIN10_444;
break;
}
break;
case D3D12_VIDEO_ENCODER_CODEC_AV1:
switch( (eAVEncAV1VProfile) profile )
{
case eAVEncAV1VProfile_Main_420_8:
pipeProfile = PIPE_VIDEO_PROFILE_AV1_MAIN;
break;
}
break;
}
if( !vlScreen->pscreen->get_video_param( vlScreen->pscreen, pipeProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_SUPPORTED ) )
{
return PIPE_VIDEO_PROFILE_UNKNOWN;
}
return pipeProfile;
}
// utility to convert from AVEncVProfile to pipe_video_chroma_format
enum pipe_video_chroma_format
ConvertAVEncVProfileToPipeVideoChromaFormat( UINT32 profile, D3D12_VIDEO_ENCODER_CODEC codec )
{
// default to 420
pipe_video_chroma_format chromaFormat = PIPE_VIDEO_CHROMA_FORMAT_420;
switch( codec )
{
case D3D12_VIDEO_ENCODER_CODEC_H264:
switch( profile )
{
case eAVEncH264VProfile_422:
chromaFormat = PIPE_VIDEO_CHROMA_FORMAT_422;
break;
case eAVEncH264VProfile_444:
chromaFormat = PIPE_VIDEO_CHROMA_FORMAT_444;
break;
}
break;
case D3D12_VIDEO_ENCODER_CODEC_HEVC:
switch( profile )
{
case eAVEncH265VProfile_Main_422_10:
case eAVEncH265VProfile_Main_422_12:
chromaFormat = PIPE_VIDEO_CHROMA_FORMAT_422;
break;
case eAVEncH265VProfile_Main_444_8:
case eAVEncH265VProfile_Main_444_10:
case eAVEncH265VProfile_Main_444_12:
chromaFormat = PIPE_VIDEO_CHROMA_FORMAT_444;
break;
}
break;
case D3D12_VIDEO_ENCODER_CODEC_AV1:
switch( profile )
{
case eAVEncAV1VProfile_High_444_10:
case eAVEncAV1VProfile_High_444_8:
case eAVEncAV1VProfile_Professional_444_12:
chromaFormat = PIPE_VIDEO_CHROMA_FORMAT_444;
break;
case eAVEncAV1VProfile_Professional_422_8:
case eAVEncAV1VProfile_Professional_422_10:
case eAVEncAV1VProfile_Professional_422_12:
chromaFormat = PIPE_VIDEO_CHROMA_FORMAT_422;
break;
}
break;
}
return chromaFormat;
}
// utility to convert from pipe_h2645_enc_picture_type to eAVEncH264PictureType
// There is no eAVEncH265PictureType, so this is used for both
enum eAVEncH264PictureType
ConvertPictureTypeToAVEncH264PictureType( enum pipe_h2645_enc_picture_type picType )
{
switch( picType )
{
case PIPE_H2645_ENC_PICTURE_TYPE_P:
return eAVEncH264PictureType_P;
case PIPE_H2645_ENC_PICTURE_TYPE_B:
return eAVEncH264PictureType_B;
case PIPE_H2645_ENC_PICTURE_TYPE_IDR:
case PIPE_H2645_ENC_PICTURE_TYPE_I:
default:
return eAVEncH264PictureType_IDR;
}
}
// utility to convert from eAVEncH264PictureType to pipe_h2645_enc_picture_type
enum pipe_video_profile
ConvertAVEncH265VProfileToPipeVideoProfile( struct vl_screen *vlScreen, eAVEncH265VProfile profile )
{
enum pipe_video_profile pipeProfile;
switch( profile )
{
case eAVEncH265VProfile_Main_420_8:
case eAVEncH265VProfile_MainIntra_420_8:
pipeProfile = PIPE_VIDEO_PROFILE_HEVC_MAIN;
break;
case eAVEncH265VProfile_Main_420_10:
case eAVEncH265VProfile_MainIntra_420_10:
pipeProfile = PIPE_VIDEO_PROFILE_HEVC_MAIN_10;
break;
case eAVEncH265VProfile_Main_444_8:
pipeProfile = PIPE_VIDEO_PROFILE_HEVC_MAIN_444;
default:
pipeProfile = PIPE_VIDEO_PROFILE_UNKNOWN;
break;
}
if( !vlScreen->pscreen->get_video_param( vlScreen->pscreen, pipeProfile, PIPE_VIDEO_ENTRYPOINT_ENCODE, PIPE_VIDEO_CAP_SUPPORTED ) )
{
return PIPE_VIDEO_PROFILE_UNKNOWN;
}
return pipeProfile;
}
// utility to convert from eAVEncH265VProfile to pipe_video_chroma_format
enum pipe_video_chroma_format
ConvertAVEncH265VProfileToPipeVideoChromaFormat( eAVEncH265VProfile profile )
{
switch( profile )
{
case eAVEncH265VProfile_Main_422_10:
case eAVEncH265VProfile_Main_422_12:
case eAVEncH265VProfile_MainIntra_422_10:
case eAVEncH265VProfile_MainIntra_422_12:
return PIPE_VIDEO_CHROMA_FORMAT_422;
break;
case eAVEncH265VProfile_Main_444_8:
case eAVEncH265VProfile_Main_444_12:
case eAVEncH265VProfile_Main_444_10:
case eAVEncH265VProfile_MainIntra_444_8:
case eAVEncH265VProfile_MainIntra_444_10:
case eAVEncH265VProfile_MainIntra_444_12:
return PIPE_VIDEO_CHROMA_FORMAT_444;
break;
default:
return PIPE_VIDEO_CHROMA_FORMAT_420;
break;
}
}
// utility to convert from FourCC to pipe_format
enum pipe_format
ConvertFourCCToPipeFormat( DWORD dwFourCC )
{
if( dwFourCC == FOURCC_NV12 )
{
return PIPE_FORMAT_NV12;
}
else if( dwFourCC == FOURCC_P010 )
{
return PIPE_FORMAT_P010;
}
else if( dwFourCC == FOURCC_AYUV )
{
return PIPE_FORMAT_AYUV;
}
else if( dwFourCC == FOURCC_YUY2 )
{
return PIPE_FORMAT_YUYV;
}
return PIPE_FORMAT_NONE;
}
// utility to convert from pipe_format to image stride
UINT32
AdjustStrideForPipeFormatAndWidth( enum pipe_format pipeFormat, UINT32 width )
{
UINT32 stride = 0;
switch( pipeFormat )
{
case PIPE_FORMAT_NV12:
stride = width;
break;
case PIPE_FORMAT_P010:
stride = 2 * width;
break;
case PIPE_FORMAT_AYUV:
stride = width;
break;
}
return stride;
}
// utility to convert from pipe_format to chroma format idc
UINT32
GetChromaFormatIdc( enum pipe_format pipeFormat )
{
switch( pipeFormat )
{
case PIPE_FORMAT_NV12:
case PIPE_FORMAT_P010:
return 1;
case PIPE_FORMAT_YUYV:
case PIPE_FORMAT_Y210:
return 2;
case PIPE_FORMAT_AYUV:
case PIPE_FORMAT_Y410:
return 3;
default:
{
unreachable( "Unsupported pipe video format" );
}
break;
}
}
// utility to convert from pipe_video_profile to pipe_format
enum pipe_format
ConvertProfileToFormat( enum pipe_video_profile profile )
{
switch( profile )
{
case PIPE_VIDEO_PROFILE_MPEG4_AVC_BASELINE:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_CONSTRAINED_BASELINE:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_EXTENDED:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH:
case PIPE_VIDEO_PROFILE_HEVC_MAIN:
case PIPE_VIDEO_PROFILE_AV1_MAIN:
case PIPE_VIDEO_PROFILE_VP9_PROFILE0:
return PIPE_FORMAT_NV12;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH10:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_10:
case PIPE_VIDEO_PROFILE_VP9_PROFILE2:
return PIPE_FORMAT_P010;
case PIPE_VIDEO_PROFILE_HEVC_MAIN_422:
return PIPE_FORMAT_YUYV;
case PIPE_VIDEO_PROFILE_HEVC_MAIN10_422:
return PIPE_FORMAT_Y210;
case PIPE_VIDEO_PROFILE_HEVC_MAIN_444:
return PIPE_FORMAT_AYUV;
case PIPE_VIDEO_PROFILE_HEVC_MAIN10_444:
return PIPE_FORMAT_Y410;
default:
{
unreachable( "Unsupported pipe video profile" );
}
break;
}
}
// utility to convert from pipe_video_profile to MFVideoFormat subtype
GUID
ConvertProfileToSubtype( enum pipe_video_profile profile )
{
switch( profile )
{
case PIPE_VIDEO_PROFILE_MPEG4_AVC_BASELINE:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_CONSTRAINED_BASELINE:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_EXTENDED:
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH:
case PIPE_VIDEO_PROFILE_HEVC_MAIN:
case PIPE_VIDEO_PROFILE_AV1_MAIN:
return MFVideoFormat_NV12;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH10:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_10:
return MFVideoFormat_P010;
case PIPE_VIDEO_PROFILE_HEVC_MAIN_422:
return MFVideoFormat_YUY2;
case PIPE_VIDEO_PROFILE_HEVC_MAIN10_422:
return MFVideoFormat_Y210;
case PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH444:
case PIPE_VIDEO_PROFILE_HEVC_MAIN_444:
return MFVideoFormat_AYUV;
case PIPE_VIDEO_PROFILE_HEVC_MAIN10_444:
return MFVideoFormat_Y410;
default:
{
unreachable( "Unsupported pipe video profile" );
}
break;
}
}
// utility to convert from errno to HRESULT
HRESULT
ConvertErrnoRetToHR( int ret )
{
switch( ret )
{
case 0:
return S_OK;
case ENOMEM:
return MF_E_INSUFFICIENT_BUFFER;
case EINVAL:
return E_INVALIDARG;
default:
return E_FAIL;
}
}
// utility to convert from pipe_h2645_enc_picture_type to string description
const char *
ConvertPipeH2645FrameTypeToString( pipe_h2645_enc_picture_type picType )
{
switch( picType )
{
case PIPE_H2645_ENC_PICTURE_TYPE_P:
{
return "H264_P_FRAME";
}
break;
case PIPE_H2645_ENC_PICTURE_TYPE_B:
{
return "H264_B_FRAME";
}
break;
case PIPE_H2645_ENC_PICTURE_TYPE_I:
{
return "H264_I_FRAME";
}
break;
case PIPE_H2645_ENC_PICTURE_TYPE_IDR:
{
return "H264_IDR_FRAME";
}
break;
default:
{
unreachable( "Unsupported pipe_h2645_enc_picture_type" );
}
break;
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include "util/u_video.h"
#include "vl/vl_defines.h"
#include "vl/vl_video_buffer.h"
#include "vl/vl_winsys.h"
#include "pipe_headers.h"
#include <codecapi.h>
#include <mfobjects.h>
enum pipe_video_profile
ConvertAVEncVProfileToPipeVideoProfile( struct vl_screen *vlScreen, UINT32 profile, D3D12_VIDEO_ENCODER_CODEC codec );
enum pipe_video_chroma_format
ConvertAVEncVProfileToPipeVideoChromaFormat( UINT32 profile, D3D12_VIDEO_ENCODER_CODEC codec );
// H264
enum eAVEncH264PictureType
ConvertPictureTypeToAVEncH264PictureType( enum pipe_h2645_enc_picture_type picType );
uint32_t
ConvertPipeProfileToSpecProfile( pipe_video_profile profile );
enum pipe_format
ConvertFourCCToPipeFormat( DWORD dwFourCC );
UINT32
AdjustStrideForPipeFormatAndWidth( enum pipe_format pipeFormat, UINT32 width );
UINT32
GetChromaFormatIdc( enum pipe_format pipeFormat );
enum pipe_format
ConvertProfileToFormat( enum pipe_video_profile profile );
GUID ConvertProfileToSubtype( enum pipe_video_profile );
HRESULT
ConvertErrnoRetToHR( int ret );
const char *
ConvertPipeH2645FrameTypeToString( pipe_h2645_enc_picture_type picType );

View file

@ -0,0 +1,76 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "hmft_entrypoints.h"
#include "wpptrace.h"
#include "mfrealtimeclientex.tmh"
// IMFRealTimeClientEx::RegisterThreadsEx
// https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-imfrealtimeclientex-registerthreadsex
HRESULT
CDX12EncHMFT::RegisterThreadsEx( DWORD *pdwTaskIndex, LPCWSTR wszClassName, LONG lBasePriority )
{
auto lock = m_lock.lock();
if( !pdwTaskIndex )
{
return E_POINTER;
}
if( *pdwTaskIndex == 0 )
{
return E_INVALIDARG;
}
/* %%%TODO
return RegisterMmcssThreads (*pdwTaskIndex, wszClassName, lBasePriority);
--or--
return MF_E_TRANSFORM_TYPE_NOT_SET;
*/
return E_NOTIMPL;
}
// IMFRealTimeClientEx::UnregisterThreads
// https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-imfrealtimeclientex-unregisterthreads
HRESULT
CDX12EncHMFT::UnregisterThreads()
{
auto lock = m_lock.lock();
/* %%%TODO
return UnregisterMmcssThreads ();
--or--
return MF_E_TRANSFORM_TYPE_NOT_SET;
*/
return E_NOTIMPL;
}
// IMFRealTimeClientEx::SetWorkQueueEx
// https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-imfrealtimeclientex-setworkqueueex
HRESULT
CDX12EncHMFT::SetWorkQueueEx( DWORD dwMultithreadedWorkQueueId, LONG lWorkItemBasePriority )
{
return E_NOTIMPL;
}

View file

@ -0,0 +1,73 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "hmft_entrypoints.h"
#include "wpptrace.h"
#include "mfshutdown.tmh"
HRESULT
CDX12EncHMFT::CheckShutdown( void )
{
auto lock = m_lockShutdown.lock();
if( m_bShutdown )
{
return MF_E_SHUTDOWN;
}
return S_OK;
}
// IMFShutdown::GetShutdownStatus
// https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-imfshutdown-getshutdownstatus
HRESULT
CDX12EncHMFT::GetShutdownStatus( MFSHUTDOWN_STATUS *pStatus )
{
HRESULT hr = S_OK;
CHECKNULL_GOTO( pStatus, E_POINTER, done );
CHECKBOOL_GOTO( CheckShutdown() == MF_E_SHUTDOWN, MF_E_INVALIDREQUEST, done );
*pStatus = MFSHUTDOWN_COMPLETED;
done:
MFE_INFO( "[dx12 hmft 0x%p] GetShutdownStatus - hr=0x%x", this, hr );
return hr;
}
// IMFShutdown::Shutdown
// https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-imfshutdown-shutdown
HRESULT
CDX12EncHMFT::Shutdown( void )
{
HRESULT hr = S_OK;
auto lock = m_lockShutdown.lock();
debug_printf( "[dx12 hmft 0x%p] Shutdown called\n", this );
if( !m_bShutdown )
{
m_bShutdown = true;
OnFlush();
CleanupEncoder();
}
MFE_INFO( "[dx12 hmft 0x%p] Shutdown - hr=0x%x", this, hr );
return hr;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,27 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include "pipe/p_video_codec.h"
#include "pipe/p_video_enums.h"
#include "pipe/p_video_state.h"

View file

@ -0,0 +1,66 @@
# DX12 Video Encode Media Foundation Transform (Microsoft Corporation (C) 2025)
## Overview
This folder contains the implementation of an async media foundation transform (MFT) that uses DX12 video encode API via the backend D3D12 pipe objects.
## Files
| Files | Description |
| ----- | ---- |
| codecapi.cpp | implements ICodecAPI interface |
| dpb_buffer_manager.cpp | utility for dbp buffer creation and deletion |
| encode.cpp | contains codec agnostic encode related code |
| encoder_capabilities.cpp | utility for querying hardware capability from pipe |
| encode_av1.cpp | encode specific to av1 (currently stub only) |
| encode_h264.cpp | encode specific to h264 |
| encode_hevc.cpp | encode specific to hevc |
| hmft_entrypoints.cpp | entry point to hmft |
| mfbufferhelp.cpp | utility for mf buffer |
| mfd3dmanager.cpp | utility for managing d3d device |
| mfmediaeventgenerator.cpp | implements the IMFMediaEventGenerator interface |
| mfpipeinterop.cpp | utility for various type translation function between MF and pipe |
| mfrealtimeclientex.cpp | implements the IMFRealTimeClientEx interface|
| mfshutdown.cpp | implements the IMFShutdown interface |
| mftransform.cpp | implements the IMFTransform interface |
| reference_frames_tracker_av1.cpp | managing dbp and gop state for av1 (currently stub only)|
| reference_frames_tracker_h264.cpp | managing dbp and gop state for h264 |
| reference_frames_tracker_hevc.cpp | managing dbp and gop state for hevc |
| videobufferlock.cpp | utility for video buffer lock |
| wpptrace.cpp | wpp trace |
| wppconfig/ | folder containing wpp trace config |
| wil/ | folder containing the Windows Implementation Library (https://github.com/microsoft/wil) |
| IDL/ | folder containing the IDL files for the MFT|
| test/ | folder containing the test code for the MFT|
Other files related to the MFT:
| Files | Description |
| ----- | ---- |
| src/gallium/targets/mediafoundation | dllmain and resourc files for MFT, builds the dll |
## Build
Current implementation is designed to build three differt codec (AV1, H264, HEVC) with the same code (note AV1 is stub right now). To build each codec, we run setup with different codec name.
To build the COM based H264 codec, you can use the following command to setup the build (please run the setup under the mesa folder, and by default the output will go to c:\bin)
```
meson setup build/ --pkg-config-path="C:\lib\pkgconfig" -Dgallium-d3d12-graphics=disabled -Dintel-elk=false -Dmicrosoft-clc=disabled -Dllvm=disabled -Dvalgrind=disabled -Dlmsensors=disabled -Dzlib=disabled -Dzstd=disabled -Dxmlconfig=disabled -Dgles1=disabled -Dgles2=disabled -Degl=disabled -Dgbm=disabled -Dglx-direct=false -Denable-glcpp-tests=false -Dopengl=false -Dgallium-drivers=d3d12 -Dgallium-xa=disabled -Dgallium-vdpau=disabled -Dgallium-va=disabled -Dgallium-mediafoundation=enabled -Dc_args="/guard:cf /we4146 /we4308 /we4509 /we4510 /we4532 /we4533 /we4610 /we4700 /we4789 /wd4703" -Dc_link_args="/guard:cf /profile /DYNAMICBASE" -Dcpp_args="/guard:cf /we4146 /we4308 /we4509 /we4510 /we4532 /we4533 /we4610 /we4700 /we4789 /wd4703" -Dcpp_link_args="/guard:cf /profile /DYNAMICBASE" --default-library=static -Db_vscrt=mt -Dc_std=c17 -Dcpp_std=c++17 -Dmicrosoft-clc=disabled -Ddebug=false --buildtype=release -Dwarning_level=2 -Dvideo-codecs=h264enc -Dmediafoundation-windows-dll-name="msh264enchmft" -Dmediafoundation-store-dll=false -Dgallium-mediafoundation-test=true
```
To build the COM based HEVC codec, use the above command but replace all instances of h264enc with h265enc to setup the build. Similarly for the pending AV1 codec, replace h264enc with av1enc.
For debug builds, change the debug/buildtype to true, e.g.
```
-Ddebug=true --buildtype=true
```
For building the unit test, add the following option to the meson setup.
```
-Dgallium-mediafoundation-test=true
```
To build, run the following command from the mesa folder.
```
ninja -C build/ install
```

View file

@ -0,0 +1,89 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include <vector>
#include "pipe_headers.h"
typedef enum frame_descriptor_reference_type
{
frame_descriptor_reference_type_none = 0,
frame_descriptor_reference_type_short_term = 1,
frame_descriptor_reference_type_long_term = 2,
} frame_descriptor_reference_type;
typedef struct reference_frames_tracker_frame_descriptor
{
} reference_frames_tracker_frame_descriptor;
class reference_frames_tracker_dpb_async_token
{
public:
std::vector<pipe_video_buffer *> dpb_buffers_to_release;
};
class reference_frames_tracker
{
public:
// pass control variables for current frame to reference tracker and compute reference frame states
virtual void begin_frame( reference_frames_tracker_dpb_async_token *pAsyncDPBToken,
bool forceKey,
bool markLTR,
uint32_t markLTRIndex,
bool useLTR,
uint32_t useLTRBitmap,
bool layerCountSet,
uint32_t layerCount,
bool dirtyRectFrameNumSet,
uint32_t dirtyRectFrameNum ) = 0;
// moves the GOP state to the next frame for next frame
virtual void advance_frame() = 0;
// release reference frame buffers
virtual void release_reconpic( reference_frames_tracker_dpb_async_token *pAsyncDPBToken ) = 0;
// returns frame descriptor
virtual const reference_frames_tracker_frame_descriptor *get_frame_descriptor() = 0;
virtual ~reference_frames_tracker()
{ }
};
typedef struct intra_refresh_slices_config
{
enum pipe_video_slice_mode slice_mode;
// Use with PIPE_VIDEO_SLICE_MODE_BLOCKS
unsigned num_slice_descriptors;
struct h264_slice_descriptor slices_descriptors[128];
// Use with PIPE_VIDEO_SLICE_MODE_MAX_SLICE_SLICE
unsigned max_slice_bytes;
} intra_refresh_slices_config;
class intra_refresh_tracker
{
public:
// start intra refresh tracker wave for the current frame
virtual bool start_ir_wave() = 0;
virtual ~intra_refresh_tracker()
{ }
};

View file

@ -0,0 +1,26 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if VIDEO_CODEC_AV1ENC
#endif

View file

@ -0,0 +1,28 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#if VIDEO_CODEC_AV1ENC
#endif

View file

@ -0,0 +1,769 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if VIDEO_CODEC_H264ENC
#include "reference_frames_tracker_h264.h"
#include <algorithm>
#include <cmath>
#include <iterator>
#include "hmft_entrypoints.h"
#include "wpptrace.h"
#include "reference_frames_tracker_h264.tmh"
reference_frames_tracker_h264::reference_frames_tracker_h264( struct pipe_video_codec *codec,
uint32_t textureWidth,
uint32_t textureHeight,
uint32_t gopLength,
uint32_t uiBPictureCount,
bool bLayerCountSet,
uint32_t layerCount,
bool bLowLatency,
uint32_t MaxL0References,
uint32_t MaxL1References,
uint32_t MaxDPBCapacity,
uint32_t MaxLongTermReferences )
: m_codec( codec ),
m_MaxL0References( MaxL0References ),
m_MaxL1References( MaxL1References ),
m_MaxDPBCapacity( MaxDPBCapacity ),
m_MaxLongTermReferences( MaxLongTermReferences ),
m_ALL_LTR_VALID_MASK( ( 1 << m_MaxLongTermReferences ) - 1 ),
m_DPBManager(
m_codec,
textureWidth,
textureHeight,
ConvertProfileToFormat( m_codec->profile ),
m_codec->max_references + 1 /*curr pic*/ +
( bLowLatency ? 0 : MFT_INPUT_QUEUE_DEPTH ) /*MFT process input queue depth for delayed in flight recon pic release*/ )
{
assert( m_MaxL0References == 1 );
m_bLayerCountSet = bLayerCountSet;
m_uiLayerCount = layerCount;
m_ValidLTRBitmap = m_ALL_LTR_VALID_MASK;
m_gopLength = gopLength;
m_layer_count_set = bLayerCountSet;
m_layer_count = layerCount;
m_force_idr_on_gop_start = true;
assert( uiBPictureCount == 0 );
m_p_picture_period = uiBPictureCount + 1;
m_gop_state.idr_pic_id = 0;
const uint32_t maxFrameNumBitsMinus4 = 4; // legal range is 0 to 12, we will fix to 4 which corresponds to [0..255]
m_gop_state.log2_max_frame_num_minus4 = maxFrameNumBitsMinus4;
m_gop_state.log2_max_pic_order_cnt_lsb_minus4 = maxFrameNumBitsMinus4 + 1;
m_max_frame_num = 1 << ( maxFrameNumBitsMinus4 + 4 );
ResetGopStateToIDR();
m_frame_state_descriptor.gop_info = &m_gop_state;
}
// release reference frame buffers
void
reference_frames_tracker_h264::release_reconpic( reference_frames_tracker_dpb_async_token *pAsyncDPBToken )
{
if( pAsyncDPBToken )
{
for( unsigned i = 0; i < pAsyncDPBToken->dpb_buffers_to_release.size(); i++ )
m_DPBManager.release_dpb_buffer( pAsyncDPBToken->dpb_buffers_to_release[i] );
delete pAsyncDPBToken;
}
}
// pass control variables for current frame to reference tracker and compute reference frame states
void
reference_frames_tracker_h264::begin_frame( reference_frames_tracker_dpb_async_token *pAsyncDPBToken,
bool forceKey,
bool markLTR,
uint32_t markLTRIndex,
bool useLTR,
uint32_t useLTRBitmap,
bool layerCountSet,
uint32_t layerCount,
bool dirtyRectFrameNumSet,
uint32_t dirtyRectFrameNum )
{
struct pipe_video_buffer *curframe_dpb_buffer = m_DPBManager.get_fresh_dpb_buffer();
if( markLTR )
{
if( m_pendingMarkLTR )
{
debug_printf( "MFT: Mark LTR dropped due to pending LTR\n" );
}
else
{
m_pendingMarkLTR = markLTR;
m_pendingMarkLTRIndex = markLTRIndex;
}
}
assert( m_bLayerCountSet == layerCountSet );
GOPStateBeginFrame( forceKey );
m_frame_state_descriptor.mmco_operations.clear();
m_frame_state_descriptor.l0_reference_list.clear();
m_frame_state_descriptor.ref_list0_mod_operations.clear();
if( m_frame_state_descriptor.gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR )
{
for( auto &i : m_PrevFramesInfos )
( pAsyncDPBToken )->dpb_buffers_to_release.push_back( i.buffer );
m_PrevFramesInfos.clear();
m_checkValidSTR = false;
m_ValidSTRFrameNumNoWrap = UINT64_MAX;
m_ActiveLTRBitmap = 0;
m_ValidLTRBitmap = m_ALL_LTR_VALID_MASK;
if( m_MaxLongTermReferences > 0 )
{
m_bSendMaxLongTermReferences = true;
}
}
if( m_MaxLongTermReferences > 0 && m_frame_state_descriptor.gop_info->temporal_id == 0 )
{
// h264 auto marking
uint32_t numActiveLTRs = GetNumberOfActiveLTR();
if( !m_pendingMarkLTR && numActiveLTRs < m_MaxLongTermReferences )
{
int emptyIndex = FindEmptyLTRIndex();
assert( emptyIndex != -1 );
m_pendingMarkLTR = true;
m_pendingMarkLTRIndex = emptyIndex;
}
if( m_pendingMarkLTR )
{
assert( m_gop_state.temporal_id == 0 );
m_gop_state.reference_type = frame_descriptor_reference_type_long_term;
m_gop_state.ltr_index = m_pendingMarkLTRIndex;
m_pendingMarkLTR = false;
}
}
if( m_frame_state_descriptor.gop_info->temporal_id == 0 )
{
if( layerCount != m_uiLayerCount )
{
m_uiLayerCount = layerCount;
m_layer_count = m_uiLayerCount;
}
}
const bool isLTR = ( m_frame_state_descriptor.gop_info->reference_type == frame_descriptor_reference_type_long_term );
uint32_t ltrUsedBitMask = 0;
if( m_frame_state_descriptor.gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_P )
{
ltrUsedBitMask = PrepareFrameRefLists( useLTR, useLTRBitmap );
}
uint32_t longTermReferenceFrameInfo =
( ltrUsedBitMask << 16 ) | ( isLTR ? m_frame_state_descriptor.gop_info->ltr_index : 0xFFFF );
m_gop_state.long_term_reference_frame_info = longTermReferenceFrameInfo; // update GOP state
// fill dpb descriptor
m_frame_state_descriptor.dpb_snapshot.clear();
m_frame_state_descriptor.dirty_rect_frame_num.clear();
// Add prev frames DPB info
for( unsigned i = 0; i < m_PrevFramesInfos.size(); i++ )
{
m_frame_state_descriptor.dpb_snapshot.push_back( {
/*id*/ 0u,
/*frame_idx*/ m_PrevFramesInfos[i].is_ltr ? m_PrevFramesInfos[i].ltr_index : m_PrevFramesInfos[i].frame_num,
m_PrevFramesInfos[i].picture_order_count,
m_PrevFramesInfos[i].temporal_id,
m_PrevFramesInfos[i].is_ltr,
m_PrevFramesInfos[i].buffer,
} );
m_frame_state_descriptor.dirty_rect_frame_num.push_back( m_PrevFramesInfos[i].dirty_rect_frame_num );
}
if( m_frame_state_descriptor.gop_info->reference_type != frame_descriptor_reference_type_none )
{
// Add current frame DPB info
m_frame_state_descriptor.dpb_snapshot.push_back( {
/*id*/ 0u,
isLTR ? m_frame_state_descriptor.gop_info->ltr_index : m_frame_state_descriptor.gop_info->frame_num,
m_frame_state_descriptor.gop_info->picture_order_count,
m_frame_state_descriptor.gop_info->temporal_id,
isLTR,
curframe_dpb_buffer,
} );
m_frame_state_descriptor.dirty_rect_frame_num.push_back( dirtyRectFrameNum );
if( m_MaxLongTermReferences > 0 )
{
if( m_bSendMaxLongTermReferences && m_frame_state_descriptor.gop_info->frame_type != PIPE_H2645_ENC_PICTURE_TYPE_IDR )
{
emit_mmco_max_long_term_references();
m_bSendMaxLongTermReferences = false;
}
if( isLTR )
{
emit_mmc0_mark_current_frame_as_ltr( m_frame_state_descriptor.gop_info->ltr_index );
}
if( m_frame_state_descriptor.mmco_operations.size() != 0 )
{
emit_mmco_end_of_memory_management();
}
}
if( m_frame_state_descriptor.mmco_operations.size() == 0 )
{
// Save frame infos if used as reference for next frame
// Remove oldest short-term if DPB full
if( m_PrevFramesInfos.size() == m_MaxDPBCapacity )
{
auto entryToRemove = std::find_if( m_PrevFramesInfos.begin(), m_PrevFramesInfos.end(), [&]( const PrevFrameInfo &p ) {
return !p.is_ltr;
} );
assert( entryToRemove != m_PrevFramesInfos.end() );
if( entryToRemove == m_PrevFramesInfos.end() )
{
unreachable( "Unexpected zero STR" );
}
( pAsyncDPBToken )->dpb_buffers_to_release.push_back( entryToRemove->buffer );
m_PrevFramesInfos.erase( entryToRemove );
}
}
if( isLTR )
{
// if current LTR index is already used, we will remove the existing LTR picture.
if( IsLTRIndexInLTRBitmap( m_frame_state_descriptor.gop_info->ltr_index ) )
{
auto entryToRemove = m_PrevFramesInfos.begin();
for( ; entryToRemove != m_PrevFramesInfos.end(); ++entryToRemove )
{
if( entryToRemove->is_ltr && entryToRemove->ltr_index == m_frame_state_descriptor.gop_info->ltr_index )
{
break;
}
}
assert( entryToRemove != m_PrevFramesInfos.end() );
if( entryToRemove == m_PrevFramesInfos.end() )
{
unreachable( "Unexpected LTR replacement in Bitmap but not in PrevFramesInfos" );
}
( pAsyncDPBToken )->dpb_buffers_to_release.push_back( entryToRemove->buffer );
m_PrevFramesInfos.erase( entryToRemove );
}
MarkLTRIndex( m_frame_state_descriptor.gop_info->ltr_index );
}
m_PrevFramesInfos.push_back( { m_frame_state_descriptor.gop_info->picture_order_count,
m_frame_state_descriptor.gop_info->frame_num,
m_frame_state_descriptor.gop_info->frame_num_no_wrap,
isLTR,
m_frame_state_descriptor.gop_info->ltr_index,
m_frame_state_descriptor.gop_info->temporal_id,
dirtyRectFrameNum,
curframe_dpb_buffer } );
}
else
{
( pAsyncDPBToken )->dpb_buffers_to_release.push_back( curframe_dpb_buffer );
}
}
// prepare the reference list for the current frame
uint32_t
reference_frames_tracker_h264::PrepareFrameRefLists( bool useLTR, uint32_t useLTRBitmap )
{
if( useLTR )
{
m_ValidLTRBitmap = useLTRBitmap & m_ALL_LTR_VALID_MASK;
m_checkValidSTR = true;
m_ValidSTRFrameNumNoWrap = m_frame_state_descriptor.gop_info->frame_num_no_wrap;
}
std::vector<RefSortList> ltrIndices;
std::vector<RefSortList> strIndices;
for( size_t i = 0; i < m_PrevFramesInfos.size(); ++i )
{
RefSortList item = { static_cast<uint8_t>( i ),
m_PrevFramesInfos[i].frame_num_no_wrap,
m_PrevFramesInfos[i].is_ltr,
m_PrevFramesInfos[i].ltr_index,
m_PrevFramesInfos[i].temporal_id };
if( m_PrevFramesInfos[i].is_ltr )
{
ltrIndices.push_back( item );
}
else
{
strIndices.push_back( item );
}
}
std::sort( strIndices.begin(), strIndices.end(), []( const RefSortList &a, const RefSortList &b ) {
return a.frame_num_no_wrap > b.frame_num_no_wrap;
} ); // sort STR descending
bool usedSTR = false;
if( !useLTR )
{
bool foundSuitableSTR = false;
size_t suitableSTRIndex = 0;
uint8_t current_tid = m_frame_state_descriptor.gop_info->temporal_id;
uint8_t target_tid = current_tid > 0 ? current_tid - 1 : 0;
for( size_t i = 0; i < strIndices.size(); ++i )
{
if( ( strIndices[i].temporal_id <= target_tid ) &&
!( m_checkValidSTR && strIndices[i].frame_num_no_wrap <= m_ValidSTRFrameNumNoWrap ) )
{
foundSuitableSTR = true;
suitableSTRIndex = i;
break;
}
}
if( foundSuitableSTR )
{
if( current_tid == 0 )
{
assert( strIndices[suitableSTRIndex].temporal_id == 0 );
}
else
{
assert( strIndices[suitableSTRIndex].temporal_id < current_tid );
}
if( suitableSTRIndex == 0 )
{
m_frame_state_descriptor.l0_reference_list.push_back( strIndices[suitableSTRIndex].pos );
}
else
{
uint64_t predFrameNumNoWrap = m_frame_state_descriptor.gop_info->frame_num_no_wrap;
m_frame_state_descriptor.l0_reference_list.push_back( strIndices[suitableSTRIndex].pos );
m_frame_state_descriptor.ref_list0_mod_operations.push_back(
{ 0u, static_cast<uint32_t>( predFrameNumNoWrap - strIndices[suitableSTRIndex].frame_num_no_wrap - 1 ), 0u } );
predFrameNumNoWrap = strIndices[suitableSTRIndex].frame_num_no_wrap;
}
usedSTR = true;
}
}
if( !usedSTR )
{
std::sort( ltrIndices.begin(), ltrIndices.end(), []( const RefSortList &a, const RefSortList &b ) {
return a.frame_num_no_wrap > b.frame_num_no_wrap;
} ); // sort LTR by frame_num descending
for( auto ltr_idx : ltrIndices )
{
if( IsLTRIndexInValidBitMap( ltr_idx.ltr_index ) )
{
m_frame_state_descriptor.l0_reference_list.push_back( ltr_idx.pos );
m_frame_state_descriptor.ref_list0_mod_operations.push_back( { 2u, 0u, ltr_idx.ltr_index } );
break;
}
}
}
if( m_frame_state_descriptor.ref_list0_mod_operations.size() > 0 )
{
// attach at the back the modification_of_pic_nums_idc = 3 = End modification_of_pic_nums_idc syntax element loop
m_frame_state_descriptor.ref_list0_mod_operations.push_back( { 3u, 0u, 0u } );
}
assert( m_frame_state_descriptor.l0_reference_list.size() == 1 );
assert( m_frame_state_descriptor.ref_list0_mod_operations.size() <= 2 );
uint32_t ltrUsedBitmask = 0;
for( size_t i = 0; i < m_frame_state_descriptor.l0_reference_list.size(); ++i )
{
int idx = m_frame_state_descriptor.l0_reference_list[i];
if( m_PrevFramesInfos[idx].is_ltr )
{
ltrUsedBitmask |= ( 1 << m_PrevFramesInfos[idx].ltr_index );
}
}
return ltrUsedBitmask;
}
const reference_frames_tracker_frame_descriptor *
reference_frames_tracker_h264::get_frame_descriptor()
{
return (const reference_frames_tracker_frame_descriptor *) &m_frame_state_descriptor;
}
// emit the mmco mark the current frame as ltr command
void
reference_frames_tracker_h264::emit_mmc0_mark_current_frame_as_ltr( uint32_t ref_frame_index )
{
m_frame_state_descriptor.mmco_operations.push_back( {
// uint8_t memory_management_control_operation;
// Mark the current picture as "used for long-term reference" and assign a long-term frame index to it
6u,
// uint8_t difference_of_pic_nums_minus1;
0u,
// uint8_t long_term_pic_num;
0u,
// uint8_t long_term_frame_idx;
static_cast<uint8_t>( ref_frame_index ),
// uint8_t max_long_term_frame_idx_plus1;
0u,
} );
}
// emit the mmco max long term references command
void
reference_frames_tracker_h264::emit_mmco_max_long_term_references()
{
// Set max_long_term_frame_idx_plus1 to m_MaxLongTermReferences
m_frame_state_descriptor.mmco_operations.push_back( {
// uint8_t memory_management_control_operation;
// Specify the maximum long-term frame index and mark all long-term reference pictures having long-term
// frame indices greater than the maximum value as "unused for reference"
4u,
// difference_of_pic_nums_minus1;
0u,
// uint8_t long_term_pic_num;
0u,
// uint8_t long_term_frame_idx;
0u,
// uint8_t max_long_term_frame_idx_plus1;
static_cast<uint8_t>( m_MaxLongTermReferences ),
} );
}
// emit the mmco end of memory management control command
void
reference_frames_tracker_h264::emit_mmco_end_of_memory_management()
{
m_frame_state_descriptor.mmco_operations.push_back( { 0u, 0u, 0u, 0u, 0u } );
}
// returns the the number active LTR
uint32_t
reference_frames_tracker_h264::GetNumberOfActiveLTR()
{
uint32_t activeLTR = 0;
for( uint32_t i = 0; i < m_MaxLongTermReferences; ++i )
{
if( m_ActiveLTRBitmap & ( 1 << i ) )
{
activeLTR++;
}
}
return activeLTR;
}
// find a free ltr index in the active LTR bitmap (for LTR auto marking)
int
reference_frames_tracker_h264::FindEmptyLTRIndex()
{
int emptyIndex = -1;
for( int i = 0; i < static_cast<int>( m_MaxLongTermReferences ); ++i )
{
if( ( m_ActiveLTRBitmap & ( 1 << i ) ) == 0 )
{
emptyIndex = i;
break;
}
}
return emptyIndex;
}
// mark the ltr index in the active LTR bitmap
void
reference_frames_tracker_h264::MarkLTRIndex( uint32_t index )
{
assert( index < m_MaxLongTermReferences );
m_ActiveLTRBitmap |= ( 1 << index );
m_ValidLTRBitmap |= ( 1 << index );
}
// return whether the LTR index is in the active LTR bitmap which contains the active LTR indices.
bool
reference_frames_tracker_h264::IsLTRIndexInLTRBitmap( uint32_t index )
{
assert( index < m_MaxLongTermReferences );
return m_ActiveLTRBitmap & ( 1 << index );
}
// return whether the LTR index is in the valid LTR bitmap which contains the valid LTR indices.
bool
reference_frames_tracker_h264::IsLTRIndexInValidBitMap( uint32_t index )
{
assert( index < m_MaxLongTermReferences );
return m_ValidLTRBitmap & ( 1 << index );
}
// returns the frame type for the current frame derived using the current frame position index.
void
reference_frames_tracker_h264::ResetGopStateToIDR()
{
m_current_gop_frame_position_index = 0;
m_gop_state.intra_period = m_gopLength;
m_gop_state.ip_period = m_p_picture_period;
m_gop_state.frame_type = PIPE_H2645_ENC_PICTURE_TYPE_IDR;
m_gop_state.frame_num = 0;
m_gop_state.frame_num_no_wrap = 0;
m_gop_state.current_reference_frame_count = 1;
m_gop_state.picture_order_count = 0;
m_gop_state.temporal_id = 0;
m_gop_state.pic_order_cnt_type = ( m_p_picture_period > 2 ) ? 0u : 2u;
m_gop_state.reference_type = frame_descriptor_reference_type_short_term;
m_gop_state.ltr_index = 0;
}
pipe_h2645_enc_picture_type
reference_frames_tracker_h264::GetNextFrameType()
{
if( m_current_gop_frame_position_index == 0 )
return m_force_idr_on_gop_start ? PIPE_H2645_ENC_PICTURE_TYPE_IDR : PIPE_H2645_ENC_PICTURE_TYPE_I;
else if( m_p_picture_period == 0 )
return PIPE_H2645_ENC_PICTURE_TYPE_I;
else
return ( m_current_gop_frame_position_index % m_p_picture_period == 0 ) ? PIPE_H2645_ENC_PICTURE_TYPE_P :
PIPE_H2645_ENC_PICTURE_TYPE_B;
}
// initializes the gop state for the current frame
void
reference_frames_tracker_h264::GOPStateBeginFrame( bool forceKey )
{
m_gop_state.frame_type = GetNextFrameType();
uint32_t temporal_id = 0;
if( m_layer_count_set )
{
assert( m_layer_count <= 2 );
temporal_id = ( m_current_gop_frame_position_index & ( m_layer_count - 1 ) );
}
m_gop_state.long_term_reference_frame_info = 0x0000FFFF; // [31...16] ltr bitmap, [15...0] ltr index or 0xFFFF for STR
if( forceKey || m_gop_state.frame_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR )
{
if( m_first_idr )
{
m_first_idr = false;
}
else
{
m_gop_state.idr_pic_id++;
}
ResetGopStateToIDR();
}
else
{
// perhaps should add some handling for the unreasonably long encode case.
assert( m_gop_state.current_reference_frame_count != UINT64_MAX );
m_gop_state.frame_num = ( m_gop_state.current_reference_frame_count ) % m_max_frame_num;
m_gop_state.frame_num_no_wrap = m_gop_state.current_reference_frame_count;
if( temporal_id == 0 )
{
m_gop_state.reference_type = frame_descriptor_reference_type_short_term;
m_gop_state.temporal_id = 0;
m_gop_state.picture_order_count = ( 2 * m_gop_state.frame_num ) % ( 2 * m_max_frame_num );
m_gop_state.current_reference_frame_count++;
}
else
{
m_gop_state.reference_type = frame_descriptor_reference_type_none;
m_gop_state.temporal_id = 1;
m_gop_state.picture_order_count = ( 2 * m_gop_state.frame_num - 1 ) % ( 2 * m_max_frame_num );
}
}
}
// moves the GOP state to the next frame for next frame
void
reference_frames_tracker_h264::advance_frame()
{
m_current_gop_frame_position_index = ( m_gopLength > 0 ) ? // Wrap around m_gop_length for non-infinite GOP
( ( m_current_gop_frame_position_index + 1 ) % m_gopLength ) :
( m_current_gop_frame_position_index + 1 );
}
//
// Intra Refresh Tracker
//
void
intra_refresh_tracker_row_h264::reset_ir_state_desc()
{
m_ir_state_desc.base = *( (reference_frames_tracker_frame_descriptor_h264 *) m_ref_pics_tracker->get_frame_descriptor() );
m_ir_state_desc.slices_config = m_non_ir_wave_slices_config;
m_ir_state_desc.current_ir_wave_frame_index = 0;
m_ir_state_desc.intra_refresh_params.mode = INTRA_REFRESH_MODE_NONE;
m_ir_state_desc.intra_refresh_params.need_sequence_header = false;
m_ir_state_desc.intra_refresh_params.offset = 0;
m_ir_state_desc.intra_refresh_params.region_size = 0;
}
intra_refresh_tracker_row_h264::intra_refresh_tracker_row_h264( reference_frames_tracker *ref_pic_tracker,
uint32_t ir_wave_duration,
intra_refresh_slices_config non_ir_wave_slices_config,
uint32_t total_frame_macroblocks,
bool continuous_refresh )
: m_ir_wave_duration( ir_wave_duration ),
m_ref_pics_tracker( ref_pic_tracker ),
m_non_ir_wave_slices_config( non_ir_wave_slices_config ),
m_ir_state_desc( {} ),
m_total_frame_macroblocks( total_frame_macroblocks ),
m_continuous_refresh( continuous_refresh )
{
reset_ir_state_desc();
}
intra_refresh_tracker_row_h264::~intra_refresh_tracker_row_h264()
{
if( m_ref_pics_tracker )
delete m_ref_pics_tracker;
}
// forward to underlying reference tracker
void
intra_refresh_tracker_row_h264::release_reconpic( reference_frames_tracker_dpb_async_token *pAsyncDPBToken )
{
m_ref_pics_tracker->release_reconpic( pAsyncDPBToken );
}
// start intra refresh wave and then forward to underlying reference tracker
void
intra_refresh_tracker_row_h264::begin_frame( reference_frames_tracker_dpb_async_token *pAsyncDPBToken,
bool forceKey,
bool markLTR,
uint32_t mark_ltr_index,
bool useLTR,
uint32_t use_ltr_bitmap,
bool layerCountSet,
uint32_t layerCount,
bool dirtyRectFrameNumSet,
uint32_t dirtyRectFrameNum )
{
if( m_ir_state_desc.intra_refresh_params.mode == INTRA_REFRESH_MODE_UNIT_ROWS )
{
if( ( ++m_ir_state_desc.current_ir_wave_frame_index ) < m_ir_wave_duration )
{
m_ir_state_desc.intra_refresh_params.need_sequence_header = false;
m_ir_state_desc.intra_refresh_params.offset += m_ir_state_desc.intra_refresh_params.region_size;
}
else
{
reset_ir_state_desc();
}
}
m_ref_pics_tracker->begin_frame( pAsyncDPBToken,
forceKey,
markLTR,
mark_ltr_index,
useLTR,
use_ltr_bitmap,
layerCountSet,
layerCount,
dirtyRectFrameNumSet,
dirtyRectFrameNum );
// If the underlying GOP tracker signaled an IDR (e.g a new GOP started) let's end any active IR wave
reference_frames_tracker_frame_descriptor_h264 *underlying_frame_desc =
(reference_frames_tracker_frame_descriptor_h264 *) m_ref_pics_tracker->get_frame_descriptor();
if( underlying_frame_desc->gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR )
{
reset_ir_state_desc();
}
else if( // For P, B frames, restart the continuous IR wave if not already active
( ( underlying_frame_desc->gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_P ) ||
( underlying_frame_desc->gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_B ) ) &&
( m_continuous_refresh && ( m_ir_state_desc.intra_refresh_params.mode == INTRA_REFRESH_MODE_NONE ) ) )
{
start_ir_wave();
}
}
// forward to underlying reference tracker
void
intra_refresh_tracker_row_h264::advance_frame()
{
m_ref_pics_tracker->advance_frame();
}
const reference_frames_tracker_frame_descriptor *
intra_refresh_tracker_row_h264::get_frame_descriptor()
{
m_ir_state_desc.base = *( (reference_frames_tracker_frame_descriptor_h264 *) m_ref_pics_tracker->get_frame_descriptor() );
return (reference_frames_tracker_frame_descriptor *) &m_ir_state_desc.base;
}
// start intra refresh tracker wave for the current frame
bool
intra_refresh_tracker_row_h264::start_ir_wave()
{
auto frame_type = ( (reference_frames_tracker_frame_descriptor_h264 *) get_frame_descriptor() )->gop_info->frame_type;
if( ( frame_type != PIPE_H2645_ENC_PICTURE_TYPE_B ) && ( frame_type != PIPE_H2645_ENC_PICTURE_TYPE_P ) )
{
debug_printf( "[intra_refresh_tracker_row_h264x::start_ir_wave] Error: IR wave can be only started on P/B frames.\n" );
assert( false );
return false;
}
if( m_ir_state_desc.intra_refresh_params.mode == INTRA_REFRESH_MODE_UNIT_ROWS )
{
debug_printf( "[intra_refresh_tracker_row_h264x::start_ir_wave] - Error: Another IR wave is currently active.\n" );
assert( false );
return false;
}
// Start IR wave with m_ir_wave_duration slices per frame (as per DX12 intra-refresh spec)
m_ir_state_desc.intra_refresh_params.mode = INTRA_REFRESH_MODE_UNIT_ROWS;
m_ir_state_desc.intra_refresh_params.need_sequence_header = true;
m_ir_state_desc.intra_refresh_params.offset = 0;
m_ir_state_desc.intra_refresh_params.region_size = m_total_frame_macroblocks / m_ir_wave_duration;
m_ir_state_desc.slices_config.slice_mode = PIPE_VIDEO_SLICE_MODE_BLOCKS;
m_ir_state_desc.slices_config.num_slice_descriptors = m_ir_wave_duration;
uint32_t slice_starting_mb = 0;
memset( m_ir_state_desc.slices_config.slices_descriptors, 0, sizeof( m_ir_state_desc.slices_config.slices_descriptors ) );
for( uint32_t i = 0; i < m_ir_state_desc.slices_config.num_slice_descriptors; i++ )
{
m_ir_state_desc.slices_config.slices_descriptors[i].macroblock_address = slice_starting_mb;
m_ir_state_desc.slices_config.slices_descriptors[i].num_macroblocks = m_ir_state_desc.intra_refresh_params.region_size;
slice_starting_mb += m_ir_state_desc.intra_refresh_params.region_size;
}
return true;
}
#endif

View file

@ -0,0 +1,226 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#if VIDEO_CODEC_H264ENC
#include <deque>
#include <queue>
#include "dpb_buffer_manager.h"
#include "reference_frames_tracker.h"
typedef struct frame_descriptor_h264
{
uint32_t intra_period;
uint32_t ip_period;
pipe_h2645_enc_picture_type frame_type;
uint32_t frame_num; // H264: frame_num % max_frame_num, reset on IDR
uint64_t frame_num_no_wrap;
uint64_t current_reference_frame_count;
uint32_t picture_order_count;
frame_descriptor_reference_type reference_type;
uint32_t ltr_index;
uint32_t pic_order_cnt_type;
uint32_t idr_pic_id;
uint32_t log2_max_frame_num_minus4;
uint8_t temporal_id;
uint32_t log2_max_pic_order_cnt_lsb_minus4;
uint32_t long_term_reference_frame_info; // corresponds to MFT attribute MFSampleExtension_LongTermReferenceFrameInfo
} frame_descriptor_h264;
typedef struct reference_frames_tracker_frame_descriptor_h264
{
reference_frames_tracker_frame_descriptor base;
const struct frame_descriptor_h264 *gop_info;
std::vector<uint8_t> l0_reference_list;
std::vector<struct pipe_h264_ref_list_mod_entry> ref_list0_mod_operations;
std::vector<pipe_h264_ref_pic_marking_entry> mmco_operations;
std::vector<pipe_h264_enc_dpb_entry> dpb_snapshot; // for H265 use the same, ignore frame_num/idx
std::vector<uint32_t> dirty_rect_frame_num; // index corresponds to dbp_snapshot
} reference_frames_tracker_frame_descriptor_h264;
class reference_frames_tracker_h264 : public reference_frames_tracker
{
public:
typedef struct PrevFrameInfo
{
uint32_t picture_order_count;
uint32_t frame_num;
uint64_t frame_num_no_wrap;
bool is_ltr;
uint32_t ltr_index;
uint8_t temporal_id;
uint32_t dirty_rect_frame_num;
struct pipe_video_buffer *buffer;
} PrevFrameInfo;
// used to sort PrevFrameInfo array
typedef struct RefSortList
{
uint8_t pos; // index into location in the PrevFrameInfo array
// below are from PrevFrameInfo
uint64_t frame_num_no_wrap;
bool is_ltr;
uint32_t ltr_index;
uint8_t temporal_id;
} RefSortList;
void begin_frame( reference_frames_tracker_dpb_async_token *pAsyncDPBToken,
bool forceKey,
bool markLTR,
uint32_t markLTRIndex,
bool useLTR,
uint32_t useLTRBitmap,
bool layerCountSet,
uint32_t layerCount,
bool dirtyRectFrameNumSet,
uint32_t dirtyRectFrameNum );
void advance_frame();
const reference_frames_tracker_frame_descriptor *get_frame_descriptor();
void release_reconpic( reference_frames_tracker_dpb_async_token *pAsyncDPBToken );
// Declare other methods
reference_frames_tracker_h264( struct pipe_video_codec *codec,
uint32_t textureWidth,
uint32_t textureHeight,
uint32_t gopLength,
uint32_t uiBPictureCount,
bool bLayerCountSet,
uint32_t layerCount,
bool bLowLatency,
uint32_t MaxL0References,
uint32_t MaxL1References,
uint32_t MaxDPBCapacity,
uint32_t MaxLongTermReferences );
private:
uint32_t PrepareFrameRefLists( bool useLTR, uint32_t useLTRBitmap );
void emit_mmco_max_long_term_references();
void emit_mmc0_mark_current_frame_as_ltr( uint32_t ref_frame_index );
void emit_mmco_end_of_memory_management();
uint32_t GetNumberOfActiveLTR();
int FindEmptyLTRIndex();
void MarkLTRIndex( uint32_t index );
bool IsLTRIndexInLTRBitmap( uint32_t index );
bool IsLTRIndexInValidBitMap( uint32_t index );
reference_frames_tracker_frame_descriptor_h264 m_frame_state_descriptor = {};
uint32_t m_MaxL0References = 0;
uint32_t m_MaxL1References = 0;
uint32_t m_MaxDPBCapacity = 0;
uint32_t m_MaxLongTermReferences = 0;
bool m_bSendMaxLongTermReferences = false;
uint32_t m_ActiveLTRBitmap = 0;
const uint32_t m_ALL_LTR_VALID_MASK;
uint32_t m_ValidLTRBitmap = 0;
bool m_checkValidSTR = false;
uint64_t m_ValidSTRFrameNumNoWrap = UINT64_MAX;
std::deque<struct PrevFrameInfo> m_PrevFramesInfos;
struct pipe_video_codec *m_codec;
dpb_buffer_manager m_DPBManager;
bool m_pendingMarkLTR = false;
uint32_t m_pendingMarkLTRIndex = 0;
bool m_bLayerCountSet = false;
uint32_t m_uiLayerCount = 0;
// GOP Tracker
void GOPStateBeginFrame( bool forceKey );
pipe_h2645_enc_picture_type GetNextFrameType();
void ResetGopStateToIDR();
uint32_t m_gopLength = 0;
const uint32_t m_poc_increment = 2;
uint32_t m_p_picture_period = 0;
uint32_t m_max_frame_num = 0;
bool m_force_idr_on_gop_start = true;
bool m_first_idr = true;
uint32_t m_current_gop_frame_position_index = 0;
frame_descriptor_h264 m_gop_state;
bool m_layer_count_set = false;
uint32_t m_layer_count = 1;
// GOPTracker
};
typedef struct intra_refresh_tracker_frame_descriptor_h264
{
struct reference_frames_tracker_frame_descriptor_h264 base;
struct intra_refresh_slices_config slices_config;
uint32_t current_ir_wave_frame_index;
struct pipe_enc_intra_refresh intra_refresh_params;
} intra_refresh_tracker_frame_descriptor_h264;
class intra_refresh_tracker_row_h264 : public reference_frames_tracker, public intra_refresh_tracker
{
// reference_frames_tracker
public:
void begin_frame( reference_frames_tracker_dpb_async_token *pAsyncDPBToken,
bool forceKey,
bool markLTR,
uint32_t mark_ltr_index,
bool useLTR,
uint32_t use_ltr_bitmap,
bool layerCountSet,
uint32_t layerCount,
bool dirtyRectFrameNumSet,
uint32_t dirtyRectFrameNum );
void advance_frame(); // GOP Tracker
const reference_frames_tracker_frame_descriptor *get_frame_descriptor();
void release_reconpic( reference_frames_tracker_dpb_async_token *pAsyncDPBToken );
public:
// intra_refresh_tracker
bool start_ir_wave();
// intra_refresh_tracker_row_h264
intra_refresh_tracker_row_h264( reference_frames_tracker *ref_pic_tracker,
uint32_t ir_wave_duration,
intra_refresh_slices_config non_ir_wave_slices_config,
uint32_t total_frame_macroblocks,
bool continuous_refresh = true );
~intra_refresh_tracker_row_h264();
private:
bool m_continuous_refresh = true;
const uint32_t m_ir_wave_duration = 0u;
reference_frames_tracker *m_ref_pics_tracker = {};
const intra_refresh_slices_config m_non_ir_wave_slices_config = {};
intra_refresh_tracker_frame_descriptor_h264 m_ir_state_desc = {};
uint32_t m_total_frame_macroblocks = 0;
void reset_ir_state_desc();
};
#endif

View file

@ -0,0 +1,516 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if VIDEO_CODEC_H265ENC
#include "reference_frames_tracker_hevc.h"
#include <algorithm>
#include <cmath>
#include <iterator>
#include "hmft_entrypoints.h"
#include "wpptrace.h"
#include "reference_frames_tracker_hevc.tmh"
reference_frames_tracker_hevc::reference_frames_tracker_hevc( struct pipe_video_codec *codec,
uint32_t textureWidth,
uint32_t textureHeight,
uint32_t gopLength,
uint32_t uiBPictureCount,
bool bLayerCountSet,
uint32_t layerCount,
bool bLowLatency,
uint32_t MaxL0References,
uint32_t MaxL1References,
uint32_t MaxDPBCapacity,
uint32_t MaxLongTermReferences )
: m_codec( codec ),
m_MaxL0References( MaxL0References ),
m_MaxL1References( MaxL1References ),
m_MaxDPBCapacity( MaxDPBCapacity ),
m_MaxLongTermReferences( MaxLongTermReferences ),
m_DPBManager(
m_codec,
textureWidth,
textureHeight,
ConvertProfileToFormat( m_codec->profile ),
m_codec->max_references + 1 /*curr pic*/ +
( bLowLatency ? 0 : MFT_INPUT_QUEUE_DEPTH ) /*MFT process input queue depth for delayed in flight recon pic release*/ )
{
assert( m_MaxL0References == 1 );
m_gopLength = gopLength;
m_force_idr_on_gop_start = true;
m_p_picture_period = uiBPictureCount + 1;
m_gop_state.log2_max_pic_order_cnt_lsb_minus4 = 4; // legal range is 0 to 12, we will fix to 4 which corresponds to [0..255]
ResetGopStateToIDR();
m_frame_state_descriptor.gop_info = &m_gop_state;
}
// release reference frame buffers
void
reference_frames_tracker_hevc::release_reconpic( reference_frames_tracker_dpb_async_token *pAsyncDPBToken )
{
if( pAsyncDPBToken )
{
for( unsigned i = 0; i < pAsyncDPBToken->dpb_buffers_to_release.size(); i++ )
m_DPBManager.release_dpb_buffer( pAsyncDPBToken->dpb_buffers_to_release[i] );
delete pAsyncDPBToken;
}
}
// pass control variables for current frame to reference tracker and compute reference frame states
void
reference_frames_tracker_hevc::begin_frame( reference_frames_tracker_dpb_async_token *pAsyncDPBToken,
bool forceKey,
bool markLTR,
uint32_t markLTRIndex,
bool useLTR,
uint32_t useLTRBitmap,
bool layerCountSet,
uint32_t layerCount,
bool dirtyRectFrameNumSet,
uint32_t dirtyRectFrameNum )
{
struct pipe_video_buffer *curframe_dpb_buffer = m_DPBManager.get_fresh_dpb_buffer();
if( markLTR )
{
if( m_pendingMarkLTR )
{
debug_printf( "MFT: Mark LTR dropped due to pending LTR\n" );
}
else
{
m_pendingMarkLTR = markLTR;
m_pendingMarkLTRIndex = markLTRIndex;
}
}
GOPStateBeginFrame( forceKey );
m_frame_state_descriptor.l0_reference_list.clear();
if( m_frame_state_descriptor.gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR )
{
for( auto &i : m_PrevFramesInfos )
( pAsyncDPBToken )->dpb_buffers_to_release.push_back( i.buffer );
m_PrevFramesInfos.clear();
m_ActiveLTRBitmap = 0;
}
if( m_MaxLongTermReferences > 0 && m_frame_state_descriptor.gop_info->temporal_id == 0 )
{
if( m_pendingMarkLTR )
{
assert( m_gop_state.temporal_id == 0 );
m_gop_state.reference_type = frame_descriptor_reference_type_long_term;
m_gop_state.ltr_index = m_pendingMarkLTRIndex;
m_pendingMarkLTR = false;
}
}
const bool isLTR = ( m_frame_state_descriptor.gop_info->reference_type == frame_descriptor_reference_type_long_term );
uint32_t ltrUsedBitMask = 0;
if( m_frame_state_descriptor.gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_P )
{
if( useLTR )
{
auto itr = m_PrevFramesInfos.begin();
while( itr != m_PrevFramesInfos.end() )
{
bool is_ltr = itr->is_ltr;
uint32_t ltr_index = itr->ltr_index;
if( !is_ltr || ( is_ltr && !( useLTRBitmap & ( 1 << ltr_index ) ) ) )
{
( pAsyncDPBToken )->dpb_buffers_to_release.push_back( itr->buffer );
itr = m_PrevFramesInfos.erase( itr );
}
else
{
++itr;
}
}
}
ltrUsedBitMask = PrepareFrameRefLists();
}
uint32_t longTermReferenceFrameInfo =
( ltrUsedBitMask << 16 ) | ( isLTR ? m_frame_state_descriptor.gop_info->ltr_index : 0xFFFF );
m_gop_state.long_term_reference_frame_info = longTermReferenceFrameInfo; // Update GOP State
// fill dpb descriptor
m_frame_state_descriptor.dpb_snapshot.clear();
m_frame_state_descriptor.dirty_rect_frame_num.clear();
// Add prev frames DPB info
for( unsigned i = 0; i < m_PrevFramesInfos.size(); i++ )
{
m_frame_state_descriptor.dpb_snapshot.push_back( {
/*id*/ 0u,
/*frame_idx ignored*/ 0u,
m_PrevFramesInfos[i].picture_order_count,
m_PrevFramesInfos[i].temporal_id,
m_PrevFramesInfos[i].is_ltr,
m_PrevFramesInfos[i].buffer,
} );
m_frame_state_descriptor.dirty_rect_frame_num.push_back( m_PrevFramesInfos[i].dirty_rect_frame_num );
}
if( m_frame_state_descriptor.gop_info->reference_type != frame_descriptor_reference_type_none )
{
// Add current frame DPB info
m_frame_state_descriptor.dpb_snapshot.push_back( {
/*id*/ 0u,
/* frame_idx - ignored */ 0u,
m_frame_state_descriptor.gop_info->picture_order_count,
m_frame_state_descriptor.gop_info->temporal_id,
isLTR,
curframe_dpb_buffer,
} );
m_frame_state_descriptor.dirty_rect_frame_num.push_back( dirtyRectFrameNum );
// if( m_frame_state_descriptor.gop_info->is_used_as_future_reference )
// Save frame infos if used as reference for next frame
// Remove oldest short-term if DPB full
if( m_PrevFramesInfos.size() == m_MaxDPBCapacity )
{
auto entryToRemove =
std::find_if( m_PrevFramesInfos.begin(), m_PrevFramesInfos.end(), [&]( const PrevFrameInfo &p ) { return !p.is_ltr; } );
assert( entryToRemove != m_PrevFramesInfos.end() );
if( entryToRemove == m_PrevFramesInfos.end() )
{
unreachable( "Unexpected zero STR" );
}
( pAsyncDPBToken )->dpb_buffers_to_release.push_back( entryToRemove->buffer );
m_PrevFramesInfos.erase( entryToRemove );
}
if( isLTR )
{
// if current LTR index is already used, we will remove the existing LTR picture.
if( IsLTRIndexInLTRBitmap( m_frame_state_descriptor.gop_info->ltr_index ) )
{
auto entryToRemove = m_PrevFramesInfos.begin();
for( ; entryToRemove != m_PrevFramesInfos.end(); ++entryToRemove )
{
if( entryToRemove->is_ltr && entryToRemove->ltr_index == m_frame_state_descriptor.gop_info->ltr_index )
{
break;
}
}
assert( entryToRemove != m_PrevFramesInfos.end() );
if( entryToRemove == m_PrevFramesInfos.end() )
{
unreachable( "Unexpected LTR replacement in Bitmap but not in PrevFramesInfos" );
}
( pAsyncDPBToken )->dpb_buffers_to_release.push_back( entryToRemove->buffer );
m_PrevFramesInfos.erase( entryToRemove );
}
MarkLTRIndex( m_frame_state_descriptor.gop_info->ltr_index );
}
m_PrevFramesInfos.push_back( { m_frame_state_descriptor.gop_info->picture_order_count,
isLTR,
m_frame_state_descriptor.gop_info->ltr_index,
m_frame_state_descriptor.gop_info->temporal_id,
dirtyRectFrameNum,
curframe_dpb_buffer } );
}
else
{
( pAsyncDPBToken )->dpb_buffers_to_release.push_back( curframe_dpb_buffer );
}
}
// prepare the reference list for the current frame
uint32_t
reference_frames_tracker_hevc::PrepareFrameRefLists()
{
std::vector<RefSortList> refIndices;
assert( m_PrevFramesInfos.size() >= 1 );
for( size_t i = 0; i < m_PrevFramesInfos.size(); ++i )
{
RefSortList item = { static_cast<uint8_t>( i ),
m_PrevFramesInfos[i].picture_order_count,
m_PrevFramesInfos[i].is_ltr,
m_PrevFramesInfos[i].ltr_index,
m_PrevFramesInfos[i].temporal_id };
refIndices.push_back( item );
}
std::sort( refIndices.begin(), refIndices.end(), []( const RefSortList &a, const RefSortList &b ) {
return a.picture_order_count > b.picture_order_count;
} ); // sort descending
m_frame_state_descriptor.l0_reference_list.push_back( refIndices[0].pos );
assert( m_frame_state_descriptor.l0_reference_list.size() == 1 );
uint32_t ltrUsedBitmask = 0;
for( size_t i = 0; i < m_frame_state_descriptor.l0_reference_list.size(); ++i )
{
int idx = m_frame_state_descriptor.l0_reference_list[i];
if( m_PrevFramesInfos[idx].is_ltr )
{
ltrUsedBitmask |= ( 1 << m_PrevFramesInfos[idx].ltr_index );
}
}
return ltrUsedBitmask;
}
const reference_frames_tracker_frame_descriptor *
reference_frames_tracker_hevc::get_frame_descriptor()
{
return (const reference_frames_tracker_frame_descriptor *) &m_frame_state_descriptor;
}
// mark the ltr index in the active LTR bitmap
void
reference_frames_tracker_hevc::MarkLTRIndex( uint32_t index )
{
assert( index < m_MaxLongTermReferences );
m_ActiveLTRBitmap |= ( 1 << index );
}
// return whether the LTR index is in the active LTR bitmap which contains the active LTR indices.
bool
reference_frames_tracker_hevc::IsLTRIndexInLTRBitmap( uint32_t index )
{
assert( index < m_MaxLongTermReferences );
return m_ActiveLTRBitmap & ( 1 << index );
}
// reset gop state to IDR
void
reference_frames_tracker_hevc::ResetGopStateToIDR()
{
m_current_gop_frame_position_index = 0;
m_gop_state.intra_period = m_gopLength;
m_gop_state.ip_period = m_p_picture_period;
m_gop_state.frame_type = PIPE_H2645_ENC_PICTURE_TYPE_IDR;
m_gop_state.picture_order_count = 0;
m_gop_state.temporal_id = 0;
m_gop_state.is_used_as_future_reference = true;
m_gop_state.pic_order_cnt_type = ( m_p_picture_period > 2 ) ? 0u : 2u; // might not be needed
m_gop_state.reference_type = frame_descriptor_reference_type_short_term;
m_gop_state.ltr_index = 0;
}
// returns the frame type for the current frame derived using the current frame position index.
pipe_h2645_enc_picture_type
reference_frames_tracker_hevc::GetNextFrameType()
{
if( m_current_gop_frame_position_index == 0 )
return m_force_idr_on_gop_start ? PIPE_H2645_ENC_PICTURE_TYPE_IDR : PIPE_H2645_ENC_PICTURE_TYPE_I;
else if( m_p_picture_period == 0 )
return PIPE_H2645_ENC_PICTURE_TYPE_I;
else
return ( m_current_gop_frame_position_index % m_p_picture_period == 0 ) ? PIPE_H2645_ENC_PICTURE_TYPE_P :
PIPE_H2645_ENC_PICTURE_TYPE_B;
}
// initializes the gop state for the current frame
void
reference_frames_tracker_hevc::GOPStateBeginFrame( bool forceKey )
{
pipe_h2645_enc_picture_type next_frame_type = GetNextFrameType();
if( forceKey || next_frame_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR )
{
ResetGopStateToIDR();
}
else
{
m_gop_state.picture_order_count++;
m_gop_state.is_used_as_future_reference = true; // Note to future self: Do not use B frames as references for other frames
m_gop_state.frame_type = next_frame_type;
m_gop_state.long_term_reference_frame_info = 0x0000FFFF; // [31...16] ltr bitmap, [15...0] ltr index or 0xFFFF for STR
m_gop_state.reference_type = frame_descriptor_reference_type_short_term;
}
}
// moves the GOP state to the next frame for next frame
void
reference_frames_tracker_hevc::advance_frame()
{
m_current_gop_frame_position_index = ( m_gopLength > 0 ) ? // Wrap around m_gop_length for non-infinite GOP
( ( m_current_gop_frame_position_index + 1 ) % m_gopLength ) :
( m_current_gop_frame_position_index + 1 );
}
//
// Intra Refresh Tracker
//
void
intra_refresh_tracker_row_hevc::reset_ir_state_desc()
{
m_ir_state_desc.base = *( (reference_frames_tracker_frame_descriptor_hevc *) m_ref_pics_tracker->get_frame_descriptor() );
m_ir_state_desc.slices_config = m_non_ir_wave_slices_config;
m_ir_state_desc.current_ir_wave_frame_index = 0;
m_ir_state_desc.intra_refresh_params.mode = INTRA_REFRESH_MODE_NONE;
m_ir_state_desc.intra_refresh_params.need_sequence_header = false;
m_ir_state_desc.intra_refresh_params.offset = 0;
m_ir_state_desc.intra_refresh_params.region_size = 0;
}
intra_refresh_tracker_row_hevc::intra_refresh_tracker_row_hevc( reference_frames_tracker *ref_pic_tracker,
uint32_t ir_wave_duration,
intra_refresh_slices_config non_ir_wave_slices_config,
uint32_t total_frame_macroblocks,
bool continuous_refresh )
: m_ir_wave_duration( ir_wave_duration ),
m_ref_pics_tracker( ref_pic_tracker ),
m_non_ir_wave_slices_config( non_ir_wave_slices_config ),
m_ir_state_desc( {} ),
m_total_frame_macroblocks( total_frame_macroblocks ),
m_continuous_refresh( continuous_refresh )
{
reset_ir_state_desc();
}
intra_refresh_tracker_row_hevc::~intra_refresh_tracker_row_hevc()
{
if( m_ref_pics_tracker )
delete m_ref_pics_tracker;
}
// forward to underlying reference tracker
void
intra_refresh_tracker_row_hevc::release_reconpic( reference_frames_tracker_dpb_async_token *pAsyncDPBToken )
{
m_ref_pics_tracker->release_reconpic( pAsyncDPBToken );
}
// start intra refresh wave and then forward to underlying reference tracker
void
intra_refresh_tracker_row_hevc::begin_frame( reference_frames_tracker_dpb_async_token *pAsyncDPBToken,
bool forceKey,
bool markLTR,
uint32_t mark_ltr_index,
bool useLTR,
uint32_t use_ltr_bitmap,
bool layerCountSet,
uint32_t layerCount,
bool dirtyRectFrameNumSet,
uint32_t dirtyRectFrameNum )
{
if( m_ir_state_desc.intra_refresh_params.mode == INTRA_REFRESH_MODE_UNIT_ROWS )
{
if( ( ++m_ir_state_desc.current_ir_wave_frame_index ) < m_ir_wave_duration )
{
m_ir_state_desc.intra_refresh_params.need_sequence_header = false;
m_ir_state_desc.intra_refresh_params.offset += m_ir_state_desc.intra_refresh_params.region_size;
}
else
{
reset_ir_state_desc();
}
}
m_ref_pics_tracker->begin_frame( pAsyncDPBToken,
forceKey,
markLTR,
mark_ltr_index,
useLTR,
use_ltr_bitmap,
layerCountSet,
layerCount,
dirtyRectFrameNumSet,
dirtyRectFrameNum );
// If the underlying GOP tracker signaled an IDR (e.g a new GOP started) let's end any active IR wave
reference_frames_tracker_frame_descriptor_hevc *underlying_frame_desc =
(reference_frames_tracker_frame_descriptor_hevc *) m_ref_pics_tracker->get_frame_descriptor();
if( underlying_frame_desc->gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_IDR )
{
reset_ir_state_desc();
}
else if( // For P, B frames, restart the continuous IR wave if not already active
( ( underlying_frame_desc->gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_P ) ||
( underlying_frame_desc->gop_info->frame_type == PIPE_H2645_ENC_PICTURE_TYPE_B ) ) &&
( m_continuous_refresh && ( m_ir_state_desc.intra_refresh_params.mode == INTRA_REFRESH_MODE_NONE ) ) )
{
start_ir_wave();
}
}
// forward to underlying reference tracker
void
intra_refresh_tracker_row_hevc::advance_frame()
{
m_ref_pics_tracker->advance_frame();
}
const reference_frames_tracker_frame_descriptor *
intra_refresh_tracker_row_hevc::get_frame_descriptor()
{
m_ir_state_desc.base = *( (reference_frames_tracker_frame_descriptor_hevc *) m_ref_pics_tracker->get_frame_descriptor() );
return (reference_frames_tracker_frame_descriptor *) &m_ir_state_desc.base;
}
// start intra refresh tracker wave for the current frame
bool
intra_refresh_tracker_row_hevc::start_ir_wave()
{
auto frame_type = ( (reference_frames_tracker_frame_descriptor_hevc *) get_frame_descriptor() )->gop_info->frame_type;
if( ( frame_type != PIPE_H2645_ENC_PICTURE_TYPE_B ) && ( frame_type != PIPE_H2645_ENC_PICTURE_TYPE_P ) )
{
debug_printf( "[intra_refresh_tracker_row_h264x::start_ir_wave] Error: IR wave can be only started on P/B frames.\n" );
assert( false );
return false;
}
if( m_ir_state_desc.intra_refresh_params.mode == INTRA_REFRESH_MODE_UNIT_ROWS )
{
debug_printf( "[intra_refresh_tracker_row_h264x::start_ir_wave] - Error: Another IR wave is currently active.\n" );
assert( false );
return false;
}
// Start IR wave with m_ir_wave_duration slices per frame (as per DX12 intra-refresh spec)
m_ir_state_desc.intra_refresh_params.mode = INTRA_REFRESH_MODE_UNIT_ROWS;
m_ir_state_desc.intra_refresh_params.need_sequence_header = true;
m_ir_state_desc.intra_refresh_params.offset = 0;
m_ir_state_desc.intra_refresh_params.region_size = m_total_frame_macroblocks / m_ir_wave_duration;
m_ir_state_desc.slices_config.slice_mode = PIPE_VIDEO_SLICE_MODE_BLOCKS;
m_ir_state_desc.slices_config.num_slice_descriptors = m_ir_wave_duration;
uint32_t slice_starting_mb = 0;
memset( m_ir_state_desc.slices_config.slices_descriptors, 0, sizeof( m_ir_state_desc.slices_config.slices_descriptors ) );
for( uint32_t i = 0; i < m_ir_state_desc.slices_config.num_slice_descriptors; i++ )
{
m_ir_state_desc.slices_config.slices_descriptors[i].macroblock_address = slice_starting_mb;
m_ir_state_desc.slices_config.slices_descriptors[i].num_macroblocks = m_ir_state_desc.intra_refresh_params.region_size;
slice_starting_mb += m_ir_state_desc.intra_refresh_params.region_size;
}
return true;
}
#endif

View file

@ -0,0 +1,199 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#if VIDEO_CODEC_H265ENC
#include <deque>
#include <queue>
#include "dpb_buffer_manager.h"
#include "reference_frames_tracker.h"
typedef struct frame_descriptor_hevc
{
uint32_t intra_period;
uint32_t ip_period;
pipe_h2645_enc_picture_type frame_type;
bool is_used_as_future_reference;
uint32_t picture_order_count; // corresponds to PicOrderCntVal
frame_descriptor_reference_type reference_type;
uint32_t ltr_index;
uint32_t pic_order_cnt_type;
uint8_t temporal_id;
uint8_t log2_max_pic_order_cnt_lsb_minus4;
uint32_t long_term_reference_frame_info; // corresponds to MFT attribute MFSampleExtension_LongTermReferenceFrameInfo
} frame_descriptor_hevc;
typedef struct reference_frames_tracker_frame_descriptor_hevc
{
reference_frames_tracker_frame_descriptor base;
const struct frame_descriptor_hevc *gop_info;
std::vector<uint8_t> l0_reference_list;
std::vector<struct pipe_h264_ref_list_mod_entry> ref_list0_mod_operations;
std::vector<pipe_h264_enc_dpb_entry> dpb_snapshot; // for HEVC use the same, ignore frame_num/idx
std::vector<uint32_t> dirty_rect_frame_num; // index corresponds to dbp_snapshot
} reference_frames_tracker_frame_descriptor_hevc;
class reference_frames_tracker_hevc : public reference_frames_tracker
{
public:
typedef struct PrevFrameInfo
{
uint32_t picture_order_count;
bool is_ltr;
uint32_t ltr_index;
uint8_t temporal_id;
uint32_t dirty_rect_frame_num;
struct pipe_video_buffer *buffer;
} PrevFrameInfo;
// used to sort PrevFrameInfo array
typedef struct RefSortList
{
uint8_t pos; // index into location in the PrevFrameInfo array
// below are from PrevFrameInfo
uint32_t picture_order_count;
bool is_ltr;
uint32_t ltr_index;
uint8_t temporal_id;
} RefSortList;
// Declare reference_frames_tracker interface methods
void begin_frame( reference_frames_tracker_dpb_async_token *pAsyncDPBToken,
bool forceKey,
bool markLTR,
uint32_t markLTRIndex,
bool useLTR,
uint32_t useLTRBitmap,
bool layerCountSet,
uint32_t layerCount,
bool dirtyRectFrameNumSet,
uint32_t dirtyRectFrameNum );
void advance_frame(); // GOPTracker
const reference_frames_tracker_frame_descriptor *get_frame_descriptor();
void release_reconpic( reference_frames_tracker_dpb_async_token *pAsyncDPBToken );
// Declare other methods
reference_frames_tracker_hevc( struct pipe_video_codec *codec,
uint32_t textureWidth,
uint32_t textureHeight,
uint32_t gopLength,
uint32_t uiBPictureCount,
bool bLayerCountSet,
uint32_t layerCount,
bool bLowLatency,
uint32_t MaxL0References,
uint32_t MaxL1References,
uint32_t MaxDPBCapacity,
uint32_t MaxLongTermReferences );
private:
uint32_t PrepareFrameRefLists();
void MarkLTRIndex( uint32_t index );
bool IsLTRIndexInLTRBitmap( uint32_t index );
reference_frames_tracker_frame_descriptor_hevc m_frame_state_descriptor = {};
uint32_t m_MaxL0References = 0;
uint32_t m_MaxL1References = 0;
uint32_t m_MaxDPBCapacity = 0;
uint32_t m_MaxLongTermReferences = 0;
uint32_t m_ActiveLTRBitmap = 0;
std::deque<struct PrevFrameInfo> m_PrevFramesInfos;
struct pipe_video_codec *m_codec;
dpb_buffer_manager m_DPBManager;
bool m_pendingMarkLTR = false;
uint32_t m_pendingMarkLTRIndex = 0;
// GOP Tracker
void GOPStateBeginFrame( bool forceKey );
pipe_h2645_enc_picture_type GetNextFrameType();
void ResetGopStateToIDR();
uint32_t m_gopLength = 0;
uint32_t m_p_picture_period = 0;
bool m_force_idr_on_gop_start = true;
uint32_t m_current_gop_frame_position_index = 0;
frame_descriptor_hevc m_gop_state;
// GOP Tracker
};
typedef struct intra_refresh_tracker_frame_descriptor_hevc
{
struct reference_frames_tracker_frame_descriptor_hevc base;
struct intra_refresh_slices_config slices_config;
uint32_t current_ir_wave_frame_index;
struct pipe_enc_intra_refresh intra_refresh_params;
} intra_refresh_tracker_frame_descriptor_hevc;
class intra_refresh_tracker_row_hevc : public reference_frames_tracker, public intra_refresh_tracker
{
// reference_frames_tracker
public:
void begin_frame( reference_frames_tracker_dpb_async_token *pAsyncDPBToken,
bool forceKey,
bool markLTR,
uint32_t mark_ltr_index,
bool useLTR,
uint32_t use_ltr_bitmap,
bool layerCountSet,
uint32_t layerCount,
bool dirtyRectFrameNumSet,
uint32_t dirtyRectFrameNum );
void advance_frame();
const reference_frames_tracker_frame_descriptor *get_frame_descriptor();
void release_reconpic( reference_frames_tracker_dpb_async_token *pAsyncDPBToken );
public:
// intra_refresh_tracker
bool start_ir_wave();
// intra_refresh_tracker_row_h264x
intra_refresh_tracker_row_hevc( reference_frames_tracker *ref_pic_tracker,
uint32_t ir_wave_duration,
intra_refresh_slices_config non_ir_wave_slices_config,
uint32_t total_frame_macroblocks,
bool continuous_refresh = true );
~intra_refresh_tracker_row_hevc();
private:
bool m_continuous_refresh = true;
const uint32_t m_ir_wave_duration = 0u;
reference_frames_tracker *m_ref_pics_tracker = {};
const intra_refresh_slices_config m_non_ir_wave_slices_config = {};
intra_refresh_tracker_frame_descriptor_hevc m_ir_state_desc = {};
uint32_t m_total_frame_macroblocks = 0;
void reset_ir_state_desc();
};
#endif

View file

@ -0,0 +1,66 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <strmif.h>
#include "../hmft_entrypoints.h"
#include <gtest/gtest.h>
#include "hmft_test_helpers.h"
TEST( MediaFoundationEntrypoint, VerifyBasicCodecAPI )
{
HRESULT hr = S_OK;
ComPtr<CDX12EncHMFT> spMFT {};
ComPtr<IMFDXGIDeviceManager> spDXGIMan {};
ComPtr<IMFAttributes> spAttrs {};
ComPtr<ICodecAPI> spCodecAPI {};
CHECKHR_GOTO( CDX12EncHMFT::CreateInstance( &spMFT ), done );
CHECKHR_GOTO( CreateD3D12Manager( &spDXGIMan, 0 ), done );
CHECKHR_GOTO( spMFT->GetAttributes( &spAttrs ), done );
UINT32 bAsync;
if( S_OK == spAttrs->GetUINT32( MF_TRANSFORM_ASYNC, &bAsync ) && bAsync )
{
CHECKHR_GOTO(spAttrs->SetUINT32( MF_TRANSFORM_ASYNC_UNLOCK, TRUE ), done );
}
CHECKHR_GOTO(spMFT->ProcessMessage( MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR) spDXGIMan.Get() ), done);
CHECKHR_GOTO(spMFT.As( &spCodecAPI ), done);
{
VARIANT vValue, vGetValue;
vValue.vt = VT_UI4;
vValue.ulVal = 8;
CHECKHR_GOTO( spCodecAPI->SetValue( &CODECAPI_AVEncCommonQualityVsSpeed, &vValue ), done );
CHECKHR_GOTO( spCodecAPI->GetValue( &CODECAPI_AVEncCommonQualityVsSpeed, &vGetValue ), done );
ASSERT_EQ( vValue.ulVal, vGetValue.ulVal );
}
{
VARIANT *pValues;
ULONG ulValuesCount;
CHECKHR_GOTO( spCodecAPI->GetParameterValues( &CODECAPI_AVEncVideoLTRBufferControl, &pValues, &ulValuesCount ), done );
}
done:
ASSERT_HRESULT_SUCCEEDED( hr );
}

View file

@ -0,0 +1,225 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <strmif.h>
#include "../hmft_entrypoints.h"
#include <gtest/gtest.h>
#include "hmft_test_helpers.h"
TEST( MediaFoundationEntrypoint, VerifySimpleEncodeSoftwareSample )
{
HRESULT hr = S_OK;
MFStartupHelper mfStartupHelper( hr );
ASSERT_HRESULT_SUCCEEDED( hr );
CFrameGenerator frameGenerator;
ComPtr<CDX12EncHMFT> spMFT {};
ComPtr<IMFDXGIDeviceManager> spDXGIMan {};
ComPtr<IMFAttributes> spAttrs {};
ComPtr<ICodecAPI> spCodecAPI {};
ComPtr<IMFMediaEvent> spEvent {};
ComPtr<IMFMediaEventGenerator> spEventGenerator {};
uint32_t uiNeedInputCount = 0;
uint32_t uiProcessInputCount = 0;
uint32_t uiProcessOutputCount = 0;
uint32_t uiSampleCount = 0;
bool bIsMFTAllocator = true;
bool bEndOfStream = false;
const uint32_t frameRateDiv = frameGenerator.m_uiDiv;
const uint32_t frameRateNum = frameGenerator.m_uiNum;
const uint32_t width = frameGenerator.m_uiWidth;
const uint32_t height = frameGenerator.m_uiHeight;
const uint32_t bitRate = frameGenerator.m_uiBitRate;
ComPtr<IMFSample> spInSample;
ComPtr<IMFSample> spOutSample;
ComPtr<IMFMediaBuffer> spOutBuffer;
ComPtr<IMFMediaType> spInType = CreateVideoMT( width, height, FOURCC_NV12, FALSE, frameRateNum, frameRateDiv );
#if VIDEO_CODEC_H264ENC
ComPtr<IMFMediaType> spOutType = CreateVideoMT( width, height, FOURCC_H264, FALSE, frameRateNum, frameRateDiv, bitRate * 1024 );
#elif VIDEO_CODEC_H265ENC
ComPtr<IMFMediaType> spOutType = CreateVideoMT( width, height, FOURCC_HEVC, FALSE, frameRateNum, frameRateDiv, bitRate * 1024 );
#elif VIDEO_CODEC_AV1ENC
// NO AV1 doesn't work yet...
assert( false );
ComPtr<IMFMediaType> spOutType = CreateVideoMT( width, height, FOURCC_AV01, FALSE, frameRateNum, frameRateDiv, bitRate * 1024 );
#else
#error VIDEO_CODEC_xxx must be defined
#endif
GUID m_guidOutputVideoSubtype( MFVideoFormat_IYUV );
CHECKHR_GOTO( CDX12EncHMFT::CreateInstance( &spMFT ), done );
CHECKHR_GOTO( CreateD3D12Manager( &spDXGIMan, 0 ), done );
CHECKHR_GOTO( spMFT->GetAttributes( &spAttrs ), done );
UINT32 bAsync;
if( S_OK == spAttrs->GetUINT32( MF_TRANSFORM_ASYNC, &bAsync ) && bAsync )
{
CHECKHR_GOTO( spAttrs->SetUINT32( MF_TRANSFORM_ASYNC_UNLOCK, TRUE ), done );
}
CHECKHR_GOTO( spMFT->ProcessMessage( MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR) spDXGIMan.Get() ), done );
CHECKHR_GOTO( spMFT.As( &spCodecAPI ), done );
{
VARIANT *pValues;
ULONG ulValuesCount;
CHECKHR_GOTO( spCodecAPI->GetParameterValues( &CODECAPI_AVEncVideoLTRBufferControl, &pValues, &ulValuesCount ), done );
}
CHECKHR_GOTO( spMFT->SetOutputType( 0, spOutType.Get(), 0 ), done );
CHECKHR_GOTO( spMFT->SetInputType( 0, spInType.Get(), 0 ), done );
CHECKHR_GOTO( MFCreateSample( &spOutSample ), done );
ASSERT_HRESULT_SUCCEEDED( spMFT->QueryInterface( IID_IMFMediaEventGenerator, &spEventGenerator ) );
ASSERT_HRESULT_SUCCEEDED( spMFT->ProcessMessage( MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0 ) );
ASSERT_HRESULT_SUCCEEDED( spMFT->ProcessMessage( MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0 ) );
for( ;; )
{
MediaEventType eventType = 0;
DWORD status;
MFT_OUTPUT_DATA_BUFFER OutputDataBuf = { 0 };
OutputDataBuf.pSample = nullptr; // spOutSample.Get();
if( bEndOfStream )
{
if( uiProcessInputCount == uiProcessOutputCount )
{
break;
}
CHECKHR_GOTO( spEventGenerator->GetEvent( 0, &spEvent ), done );
CHECKHR_GOTO( spEvent->GetType( &eventType ), done );
if( eventType != METransformHaveOutput && eventType != METransformDrainComplete )
{
// Only respond to METransformHaveOutput or METransformDrainComplete events after source is at EOS
continue;
}
}
else if( uiNeedInputCount > uiProcessInputCount && uiProcessInputCount == uiProcessOutputCount )
{
// We already counted this so decrement to avoid counting twice
uiNeedInputCount--;
// The MFT has requested more samples than we have given so try to feed a sample
eventType = METransformNeedInput;
}
else
{
CHECKHR_GOTO( spEventGenerator->GetEvent( 0, &spEvent ), done );
CHECKHR_GOTO( spEvent->GetType( &eventType ), done );
}
if( eventType == METransformNeedInput )
{
uiNeedInputCount++;
frameGenerator.GenerateSoftwareFrame( &spInSample, &bEndOfStream );
if( !bEndOfStream )
{
CHECKHR_GOTO( spMFT->ProcessInput( 0, spInSample.Get(), 0 ), done );
uiProcessInputCount++;
}
else
{
CHECKHR_GOTO( spMFT->ProcessMessage( MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0 ), done );
CHECKHR_GOTO( spMFT->ProcessMessage( MFT_MESSAGE_COMMAND_DRAIN, 0 ), done );
}
}
else if( eventType == METransformHaveOutput )
{
if( bIsMFTAllocator )
{
OutputDataBuf.pSample = NULL;
}
status = 0;
hr = spMFT->ProcessOutput( 0, 1, &OutputDataBuf, &status );
if( SUCCEEDED( hr ) )
{
uiProcessOutputCount++;
uiSampleCount++;
if( OutputDataBuf.pSample != NULL )
{
CHECKHR_GOTO( OutputDataBuf.pSample->GetBufferByIndex( 0, &spOutBuffer ), done );
DWORD dwLen;
BYTE *pBuf;
CHECKHR_GOTO( spOutBuffer->Lock( &pBuf, NULL, &dwLen ), done );
printf( "Received %d\n", dwLen );
// #define DUMP
#if defined( DUMP )
{
#if VIDEO_CODEC_H264ENC
FILE *fp = fopen( "d:\\test\\output.h264", "ab" );
#elif VIDEO_CODEC_H265ENC
FILE *fp = fopen( "d:\\test\\output.h265", "ab" );
#elif VIDEO_CODEC_AV1ENC
FILE *fp = fopen( "d:\\test\\output.av1", "ab" );
#endif
fwrite( pBuf, 1, dwLen, fp );
fclose( fp );
}
#endif
CHECKHR_GOTO( spOutBuffer->Unlock(), done );
spOutBuffer.Reset();
OutputDataBuf.pSample->Release();
}
else
{
CHECKHR_GOTO( E_FAIL, done );
}
}
else if( MF_E_TRANSFORM_STREAM_CHANGE == hr )
{
ComPtr<IMFMediaType> spNewOutputType;
CHECKHR_GOTO( spMFT->GetOutputAvailableType( 0, 0, &spNewOutputType ), done );
CHECKHR_GOTO( spMFT->SetOutputType( 0, spNewOutputType.Get(), 0 ), done );
}
else
{
CHECKHR_GOTO( hr, done );
}
}
else if( eventType == METransformDrainComplete )
{
CHECKHR_GOTO( spMFT->ProcessMessage( MFT_MESSAGE_COMMAND_FLUSH, 0 ), done );
break;
}
else
{
CHECKHR_GOTO( E_FAIL, done ); // unexpected event type.
}
}
CHECKHR_GOTO( spMFT->ProcessMessage( MFT_MESSAGE_SET_D3D_MANAGER, (ULONG_PTR) NULL ), done );
done:
ASSERT_HRESULT_SUCCEEDED( hr );
}

View file

@ -0,0 +1,297 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <strmif.h>
#include "../hmft_entrypoints.h"
#include <gtest/gtest.h>
#include "hmft_test_helpers.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
const char *
TryFindD3D12CoreNextToSelf( char *path, DWORD path_arr_size )
{
UINT32 path_size = GetModuleFileNameA( (HINSTANCE) &__ImageBase, path, path_arr_size );
if( !path_arr_size || path_size == path_arr_size )
{
printf( "Unable to get path to self\n" );
return nullptr;
}
auto last_slash = strrchr( path, '\\' );
if( !last_slash )
{
printf( "Unable to get path to self\n" );
return nullptr;
}
*( last_slash + 1 ) = '\0';
if( strcat_s( path, path_arr_size, "D3D12Core.dll" ) != 0 )
{
printf( "Unable to get path to D3D12Core.dll next to self\n" );
return nullptr;
}
if( GetFileAttributesA( path ) == INVALID_FILE_ATTRIBUTES )
{
printf( "No D3D12Core.dll exists next to self\n" );
return nullptr;
}
*( last_slash + 1 ) = '\0';
return path;
}
HRESULT
CreateD3D12DeviceFactory( ID3D12DeviceFactory **ppFactory, UINT32 SDKVersion )
{
/* A device factory allows us to isolate things like debug layer enablement from other callers,
* and can potentially even refer to a different D3D12 redist implementation from others.
*/
HMODULE D3D12Module = LoadLibraryW( L"d3d12.dll" );
if( !D3D12Module )
{
printf( "D3D12: Failed to LoadLibrary d3d12.dll" );
return MF_E_DXGI_DEVICE_NOT_INITIALIZED;
}
typedef HRESULT( WINAPI * PFN_D3D12_GET_INTERFACE )( REFCLSID clsid, REFIID riid, void **ppFactory );
PFN_D3D12_GET_INTERFACE D3D12GetInterface = (PFN_D3D12_GET_INTERFACE) GetProcAddress( D3D12Module, "D3D12GetInterface" );
if( !D3D12GetInterface )
{
printf( "D3D12: Failed to retrieve D3D12GetInterface" );
return MF_E_DXGI_DEVICE_NOT_INITIALIZED;
}
/* First, try to create a device factory from a DLL-parallel D3D12Core.dll */
ID3D12SDKConfiguration *sdk_config = nullptr;
if( SUCCEEDED( D3D12GetInterface( CLSID_D3D12SDKConfiguration, IID_PPV_ARGS( &sdk_config ) ) ) )
{
ID3D12SDKConfiguration1 *sdk_config1 = nullptr;
if( SUCCEEDED( sdk_config->QueryInterface( &sdk_config1 ) ) )
{
char self_path[MAX_PATH];
const char *d3d12core_path = TryFindD3D12CoreNextToSelf( self_path, sizeof( self_path ) );
if( d3d12core_path &&
SUCCEEDED( sdk_config1->CreateDeviceFactory( SDKVersion, d3d12core_path, IID_PPV_ARGS( ppFactory ) ) ) )
{
sdk_config->Release();
sdk_config1->Release();
( *ppFactory )
->SetFlags( D3D12_DEVICE_FACTORY_FLAG_ALLOW_RETURNING_EXISTING_DEVICE |
D3D12_DEVICE_FACTORY_FLAG_ALLOW_RETURNING_INCOMPATIBLE_EXISTING_DEVICE );
return S_OK;
}
printf( "D3D12: Can't find matching D3D12Core.dll next to self." );
sdk_config->Release();
sdk_config1->Release();
return MF_E_DXGI_DEVICE_NOT_INITIALIZED;
}
else
{
printf( "D3D12: Failed to retrieve ID3D12SDKConfiguration1" );
return MF_E_DXGI_DEVICE_NOT_INITIALIZED;
}
}
else
{
printf( "D3D12: Failed to retrieve CLSID_D3D12SDKConfiguration" );
return MF_E_DXGI_DEVICE_NOT_INITIALIZED;
}
return MF_E_DXGI_DEVICE_NOT_INITIALIZED;
}
HRESULT
CreateD3D12Manager( IMFDXGIDeviceManager **ppMgr, UINT32 SDKVersion )
{
HRESULT hr = S_OK;
UINT resetToken;
ComPtr<ID3D12Device> spD3D12Device;
DXGI_ADAPTER_DESC adapterDesc = { 0 };
ComPtr<IDXGIFactory4> spDXGIFactory;
ComPtr<IDXGIAdapter> spDXGIAdapter;
ComPtr<ID3D12DeviceFactory> spFactory;
CHECKHR_GOTO( CreateDXGIFactory1( IID_PPV_ARGS( &spDXGIFactory ) ), done );
if( 0 /* m_inputParams.m_eGPU == GPU_WARP */ )
{
CHECKHR_GOTO( spDXGIFactory->EnumWarpAdapter( IID_PPV_ARGS( &spDXGIAdapter ) ), done );
CHECKHR_GOTO( spDXGIAdapter->GetDesc( &adapterDesc ), done );
}
else
{
for( UINT i = 0; spDXGIFactory->EnumAdapters( i, &spDXGIAdapter ) != DXGI_ERROR_NOT_FOUND; ++i )
{
CHECKHR_GOTO( spDXGIAdapter->GetDesc( &adapterDesc ), done );
}
}
CHECKHR_GOTO( MFCreateDXGIDeviceManager( &resetToken, ppMgr ), done );
if( SDKVersion != 0 )
{
CHECKHR_GOTO( CreateD3D12DeviceFactory( &spFactory, SDKVersion ), done );
/*
if( m_inputParams.m_bD3DDebugLayer )
{
ComPtr<ID3D12Debug> spDebugController;
CHECKHR_GOTO( spFactory->GetConfigurationInterface( CLSID_D3D12Debug, IID_PPV_ARGS( &spDebugController ) ), done );
spDebugController->EnableDebugLayer();
}
*/
CHECKHR_GOTO( spFactory->CreateDevice( spDXGIAdapter.Get(), D3D_FEATURE_LEVEL_12_1, IID_PPV_ARGS( &spD3D12Device ) ), done );
}
else
{
CHECKHR_GOTO( D3D12CreateDevice( spDXGIAdapter.Get(), D3D_FEATURE_LEVEL_12_1, IID_PPV_ARGS( &spD3D12Device ) ), done );
}
CHECKHR_GOTO( ( *ppMgr )->ResetDevice( spD3D12Device.Get(), resetToken ), done );
done:
return hr;
}
IMFMediaType *
CreateVideoMT( ULONG32 Width, ULONG32 Height, DWORD fourCC, BOOL interlaced, ULONG32 frNum, ULONG32 frDenom, UINT32 bitRate )
{
HRESULT hr = S_OK;
ComPtr<IMFMediaType> spVideoType {};
GUID guid = { 0x00000000, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 };
guid.Data1 = fourCC;
CHECKHR_GOTO( MFCreateMediaType( &spVideoType ), done );
CHECKHR_GOTO( spVideoType->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Video ), done );
CHECKHR_GOTO( spVideoType->SetGUID( MF_MT_SUBTYPE, guid ), done );
CHECKHR_GOTO( MFSetAttributeRatio( spVideoType.Get(), MF_MT_FRAME_RATE, frNum, frDenom ), done );
CHECKHR_GOTO( MFSetAttributeSize( spVideoType.Get(), MF_MT_FRAME_SIZE, Width, Height ), done );
CHECKHR_GOTO( spVideoType->SetUINT32( MF_MT_AVG_BITRATE, ( Width * Height ) / 8 ), done );
CHECKHR_GOTO( spVideoType->SetUINT32( MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive ), done );
if( bitRate > 0 )
{
CHECKHR_GOTO( spVideoType->SetUINT32( MF_MT_AVG_BITRATE, bitRate ), done );
}
done:
if( FAILED( hr ) )
{
return nullptr;
}
return spVideoType.Detach();
}
CFrameGenerator::CFrameGenerator()
{ }
CFrameGenerator::~CFrameGenerator()
{ }
HRESULT
CFrameGenerator::GenerateSoftwareFrame( IMFSample **pFrame, bool *endOfStream )
{
HRESULT hr = S_OK;
const uint32_t inLength = m_uiWidth * m_uiHeight * 3 / 2;
ComPtr<IMFSample> spInSample;
ComPtr<IMFMediaBuffer> spInBuffer;
uint8_t *pBuf;
uint32_t offsetX = m_uiOffsetX;
uint32_t offsetY = m_uiOffsetY;
const uint8_t yellowY = 210;
const uint8_t yellowU = 16;
const uint8_t yellowV = 146;
const uint8_t tealY = 93;
const uint8_t tealU = 146;
const uint8_t tealV = 71;
const uint32_t boxWidth = 40;
const uint32_t boxHeight = 80;
*endOfStream = false;
if( m_uiFrameCount >= m_maxFrameCount )
{
*endOfStream = true;
return hr;
}
CHECKHR_GOTO( MFCreateSample( &spInSample ), done );
CHECKHR_GOTO( MFCreateMemoryBuffer( inLength, &spInBuffer ), done );
spInSample->AddBuffer( spInBuffer.Get() );
spInBuffer->Lock( &pBuf, NULL, NULL );
{
BYTE *pNV12 = pBuf;
for( uint32_t i = 0; i < m_uiHeight; i++ )
{
memset( pNV12 + i * m_uiWidth, yellowY, m_uiWidth );
}
pNV12 += m_uiWidth * m_uiHeight;
for( uint32_t i = 0; i < m_uiHeight / 2; i++ )
{
for( uint32_t j = 0; j < m_uiWidth; j += 2 )
{
pNV12[i * m_uiWidth + j] = yellowU;
pNV12[i * m_uiWidth + j + 1] = yellowV;
}
}
pNV12 = pBuf + offsetY * m_uiWidth + offsetX;
for( uint32_t i = 0; i < boxHeight; i++ )
{
memset( pNV12 + i * m_uiWidth, tealY, boxWidth );
}
pNV12 = pBuf + m_uiWidth * m_uiHeight + ( offsetY ) * ( m_uiWidth / 2 ) + ( offsetX );
for( uint32_t i = 0; i < boxHeight / 2; i++ )
{
for( uint32_t j = 0; j < boxWidth; j += 2 )
{
pNV12[i * m_uiWidth + j] = tealU;
pNV12[i * m_uiWidth + j + 1] = tealV;
}
}
m_uiOffsetX += 8;
if( m_uiOffsetX >= m_uiWidth - boxWidth )
{
m_uiOffsetX = 0;
}
}
spInBuffer->Unlock();
spInSample->SetSampleTime( ( m_uiFrameCount * 10000000i64 ) / m_uiNum );
spInSample->SetSampleDuration( 10000000i64 / m_uiNum );
m_uiFrameCount++;
*pFrame = spInSample.Detach();
done:
return hr;
}

View file

@ -0,0 +1,79 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "../hmft_entrypoints.h"
const char *
TryFindD3D12CoreNextToSelf( char *path, DWORD path_arr_size );
HRESULT
CreateD3D12DeviceFactory( ID3D12DeviceFactory **ppFactory, UINT32 SDKVersion );
HRESULT
CreateD3D12Manager( IMFDXGIDeviceManager **ppMgr, UINT32 SDKVersion );
IMFMediaType *
CreateVideoMT( ULONG32 Width, ULONG32 Height, DWORD fourCC, BOOL interlaced, ULONG32 frNum, ULONG32 frDenon, UINT32 bitRate = 0);
class MFStartupHelper
{
public:
MFStartupHelper(HRESULT& hr)
{
hr = S_OK;
if( SUCCEEDED( hr = CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED ) ) )
{
hr = MFStartup( MF_VERSION );
}
}
~MFStartupHelper()
{
MFShutdown();
// Free any unreferenced modules to catch potential leaks.
CoFreeUnusedLibrariesEx( 0, 0 );
CoUninitialize();
// CheckMFPlatStartupLeaks();
}
};
class CFrameGenerator
{
public:
CFrameGenerator();
~CFrameGenerator();
HRESULT GenerateSoftwareFrame( IMFSample **pFrame, bool *endOfStream );
public:
const uint32_t m_uiDiv = 1;
const uint32_t m_uiNum = 30;
const uint32_t m_uiWidth = 320;
const uint32_t m_uiHeight = 240;
const uint32_t m_maxFrameCount = 88;
uint32_t m_uiBitRate = 150;
uint32_t m_uiFrameCount = 0;
uint32_t m_uiOffsetX = 0;
uint32_t m_uiOffsetY = 0;
};

View file

@ -0,0 +1,13 @@
mediafoundation_test_st = static_library(
'mediafoundation_test_st',
files(
'hmft_test_helpers.cpp',
'hmft_codecapi_test.cpp',
'hmft_simple_encode_test.cpp',
'stub.cpp',
),
cpp_args : mf_cpp_args,
override_options: ['cpp_std=c++20'],
include_directories: [inc_include, inc_src, inc_gallium, inc_gallium_aux],
dependencies: [idep_gtest, dep_dxheaders],
)

View file

@ -0,0 +1,43 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <guiddef.h>
#if VIDEO_CODEC_H264ENC
const wchar_t *g_pMFTFriendlyName = L"Microsoft AVC DX12 Encoder HMFT";
DEFINE_GUID( CLSID_CDX12EncoderHMFT, 0x8994db7c, 0x288a, 0x4c62, 0xa1, 0x36, 0xa3, 0xc3, 0xc2, 0xa2, 0x08, 0xa8 );
#elif VIDEO_CODEC_H265ENC
const wchar_t *g_pMFTFriendlyName = L"Microsoft HEVC DX12 Encoder HMFT";
DEFINE_GUID( CLSID_CDX12EncoderHMFT, 0xe7ffb8eb, 0xfa0b, 0x4fb0, 0xac, 0xdf, 0x12, 0x2, 0xf6, 0x63, 0xcd, 0xe5 );
#elif VIDEO_CODEC_AV1ENC
const wchar_t *g_pMFTFriendlyName = L"Microsoft AV1 DX12 Encoder HMFT";
DEFINE_GUID( CLSID_CDX12EncoderHMFT, 0x1a6f3150, 0xb121, 0x4ce9, 0x94, 0x97, 0x50, 0xfe, 0xdb, 0x3d, 0xcb, 0x70 );
#else
#error VIDEO_CODEC_xxx must be defined
#endif
extern "C" struct pipe_screen *
sw_screen_create_vk( struct sw_winsys *winsys, const struct pipe_screen_config *config, bool sw_vk )
{
return nullptr;
}

View file

@ -0,0 +1,237 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "videobufferlock.h"
#include "mfbufferhelp.h"
HRESULT
VideoBufferLock::lock( __in MF2DBuffer_LockFlags flags )
{
HRESULT hr = S_OK;
DWORD maxSize = 0;
BYTE *pbBufferStart = NULL;
DWORD dwBufferLength = 0;
UINT32 dwMinSize = 0;
LONG lDefaultStride = 0;
if( _bLocked2D || _bLocked2D2 || _bLockedBuffer )
{
return S_FALSE;
}
if( _pInputBuffer == nullptr )
{
return MF_E_NOT_INITIALIZED;
}
GUID format;
UINT32 unWidth = 0, unHeight = 0;
hr = _pmt->GetGUID( MF_MT_SUBTYPE, &format );
if( SUCCEEDED( hr ) )
{
hr = MFGetAttributeSize( _pmt.Get(), MF_MT_FRAME_SIZE, &unWidth, &unHeight );
if( SUCCEEDED( hr ) )
{
hr = MFGetStrideForBitmapInfoHeader( format.Data1, unWidth, &lDefaultStride );
}
}
if( FAILED( hr ) )
{
lDefaultStride = 0;
}
hr = MFCalculateImageSize( format, unWidth, unHeight, &dwMinSize );
if( FAILED( hr ) )
{
dwMinSize = 0;
(void) _pInputBuffer->GetCurrentLength( (DWORD *) &dwMinSize );
}
hr = _pInputBuffer.As( &_pInputBuffer2D2 );
if( SUCCEEDED( hr ) )
{
hr = _pInputBuffer2D2->Lock2DSize( flags, &_pData, &_lPitch, &pbBufferStart, &dwBufferLength );
if( SUCCEEDED( hr ) )
{
if( dwBufferLength < dwMinSize || abs( _lPitch ) < abs( lDefaultStride ) )
{
_pInputBuffer2D2->Unlock2D();
return MF_E_BUFFERTOOSMALL;
}
_bLocked2D2 = TRUE;
_uiSize = dwBufferLength;
_pDataTop = _pData;
_pData = pbBufferStart;
}
}
if( !_bLocked2D2 )
{
hr = _pInputBuffer.As( &_pInputBuffer2D );
if( SUCCEEDED( hr ) )
{
hr = _pInputBuffer2D->Lock2D( &_pData, &_lPitch );
if( SUCCEEDED( hr ) )
{
hr = _pInputBuffer->GetMaxLength( (DWORD *) &_uiSize );
if( FAILED( hr ) || abs( _lPitch ) < abs( lDefaultStride ) || _uiSize < dwMinSize )
{
_pInputBuffer2D->Unlock2D();
return MF_E_BUFFERTOOSMALL;
}
_uiSize = dwMinSize;
_bLocked2D = TRUE;
_pDataTop = _pData;
if( _lPitch < 0 )
{
_pData += _uiSize - abs( _lPitch );
}
}
}
}
if( !( _bLocked2D2 || _bLocked2D ) )
{
hr = _pInputBuffer->Lock( &_pData, &maxSize, NULL );
if( SUCCEEDED( hr ) )
{
hr = _pInputBuffer->GetMaxLength( (DWORD *) &_uiSize );
if( FAILED( hr ) || _uiSize < dwMinSize )
{
_pInputBuffer->Unlock();
return MF_E_BUFFERTOOSMALL;
}
_uiSize = dwMinSize;
_bLockedBuffer = TRUE;
_lPitch = lDefaultStride;
_pDataTop = _pData;
if( _lPitch < 0 )
{
_pData += _uiSize - abs( _lPitch );
}
}
}
return hr;
}
HRESULT
VideoBufferLock::lockRemap( __in MF2DBuffer_LockFlags flags, bool topDown, LONG restride )
{
HRESULT hr = S_OK;
if( FAILED( hr = lock( flags ) ) )
{
return hr;
}
if( restride != 0 || topDown && _lPitch < 0 )
{
LONG newPitch = _lPitch;
if( restride != 0 )
{
newPitch = restride;
}
if( topDown )
{
newPitch = abs( _lPitch );
}
BYTE *temp = new BYTE[newPitch * lines()];
if( nullptr == temp )
{
return E_OUTOFMEMORY;
}
if( FAILED( hr = MFCopyImage( temp, newPitch, _pData, _lPitch, abs( _lPitch ), lines() ) ) )
{
delete[] temp;
return hr;
}
_uiSize = newPitch * lines();
_pData = temp;
_pDataTop = temp;
_lPitch = newPitch;
_localAlloc = true;
}
return hr;
}
BOOL
VideoBufferLock::validate( BYTE *ptr ) const
{
if( _lPitch >= 0 )
{
return ptr <= _pData + _uiSize && ptr >= _pData;
}
else
{
return ptr >= _pData - _uiSize + abs( _lPitch ) && ptr <= _pData + abs( _lPitch );
}
}
VideoBufferLock &
VideoBufferLock::operator=( VideoBufferLock &&move )
{
_bLockedBuffer = move._bLockedBuffer;
_bLocked2D = move._bLocked2D;
_bLocked2D2 = move._bLocked2D2;
_localAlloc = move._localAlloc;
_pDataTop = move._pDataTop;
_pData = move._pData;
_lPitch = move._lPitch;
_pInputBuffer = move._pInputBuffer;
_uiSize = move._uiSize;
_pmt = move._pmt;
move._bLocked2D = FALSE;
move._bLocked2D2 = FALSE;
move._bLockedBuffer = FALSE;
return *this;
}
HRESULT
VideoBufferLock::unlock()
{
HRESULT hr = S_OK;
if( _bLockedBuffer && _pInputBuffer )
{
hr = _pInputBuffer->Unlock();
_bLockedBuffer = FALSE;
_pInputBuffer = nullptr;
}
if( _bLocked2D && _pInputBuffer2D )
{
hr = _pInputBuffer2D->Unlock2D();
_bLocked2D = FALSE;
_pInputBuffer2D = nullptr;
}
if( _bLocked2D2 && _pInputBuffer2D2 )
{
hr = _pInputBuffer2D2->Unlock2D();
_bLocked2D2 = FALSE;
_pInputBuffer2D2 = nullptr;
}
if( _localAlloc )
{
delete[] _pData;
_localAlloc = false;
}
return hr;
}

View file

@ -0,0 +1,129 @@
/*
* Copyright © Microsoft Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include <Windows.h>
#include <mfapi.h>
#include <mferror.h>
#include <utility>
#include <wrl/client.h>
using namespace Microsoft::WRL;
// RAII class for safely taking IMFMediaBuffer locks.
// Provides extended information about the buffer using
// the IMFMediaType associated with the buffer
class VideoBufferLock
{
public:
VideoBufferLock()
{ }
// Main ctor
VideoBufferLock( IMFMediaBuffer *pBuffer, IMFMediaType *pmt ) : _pInputBuffer( pBuffer ), _pmt( pmt )
{ }
// Shortcut ctor that calls GetBufferByIndex for you
VideoBufferLock( IMFSample *pSamp, IMFMediaType *pmt, UINT index = 0 ) : _pmt( pmt )
{
// If this call fails, _pInputBuffer will be NULL which will fail lock()
(void) pSamp->GetBufferByIndex( index, &_pInputBuffer );
}
// Only support move semantics
VideoBufferLock( VideoBufferLock &&move )
{
*this = std::move( move );
}
// Only support move semantics
VideoBufferLock &operator=( VideoBufferLock &&move );
// Lock a video buffer
HRESULT lock( __in MF2DBuffer_LockFlags flags = MF2DBuffer_LockFlags_ReadWrite );
// Lock a video buffer, if the buffer does not match the given topDown and stride parameters the buffer
// will be copied into a buffer matching those properties.
HRESULT
lockRemap( __in MF2DBuffer_LockFlags flags = MF2DBuffer_LockFlags_ReadWrite, bool topDown = true, LONG restride = 0 );
// Unlock video buffer
HRESULT unlock();
~VideoBufferLock()
{
unlock();
}
// Returns a pointer to the buffer
BYTE *data() const
{
return _pData;
}
// Returns a pointer to the top of the buffer (no stride adjustment)
BYTE *dataTop() const
{
return _pDataTop;
}
// Returns the size of the buffer in bytes
UINT size() const
{
return _uiSize;
}
// Returns the size of one line of the buffer in bytes,
// may be negative for bottom-up images
LONG stride() const
{
return _lPitch;
}
// Returns the number of lines in the image (count of stride in size)
LONG lines() const
{
return _uiSize / abs( _lPitch );
}
// Validate if the given pointer is inside the buffer
BOOL validate( BYTE *ptr ) const;
private:
ComPtr<IMFMediaType> _pmt;
ComPtr<IMFMediaBuffer> _pInputBuffer;
BOOL _bLockedBuffer = FALSE;
ComPtr<IMF2DBuffer> _pInputBuffer2D;
BOOL _bLocked2D = FALSE;
ComPtr<IMF2DBuffer2> _pInputBuffer2D2;
BOOL _bLocked2D2 = FALSE;
BYTE *_pData = nullptr;
BYTE *_pDataTop = nullptr;
LONG _lPitch = 0;
UINT _uiSize = 0;
BOOL _localAlloc = FALSE;
};

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,467 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_COM_APARTMENT_VARIABLE_INCLUDED
#define __WIL_COM_APARTMENT_VARIABLE_INCLUDED
#include <any>
#include <objidl.h>
#include <roapi.h>
#include <type_traits>
#include <unordered_map>
#include <winrt/Windows.Foundation.h>
#include "com.h"
#include "cppwinrt.h"
#include "result_macros.h"
#include "win32_helpers.h"
#ifndef WIL_ENABLE_EXCEPTIONS
#error This header requires exceptions
#endif
namespace wil
{
// Determine if apartment variables are supported in the current process context.
// Prior to build 22365, the APIs needed to create apartment variables (e.g. RoGetApartmentIdentifier)
// failed for unpackaged processes. For MS people, see http://task.ms/31861017 for details.
// APIs needed to implement apartment variables did not work in non-packaged processes.
inline bool are_apartment_variables_supported()
{
unsigned long long apartmentId{};
return RoGetApartmentIdentifier(&apartmentId) != HRESULT_FROM_WIN32(ERROR_API_UNAVAILABLE);
}
// COM will implicitly rundown the apartment registration when it invokes a handler
// and blocks calling unregister when executing the callback. So be careful to release()
// this when callback is invoked to avoid a double free of the cookie.
using unique_apartment_shutdown_registration = unique_any<APARTMENT_SHUTDOWN_REGISTRATION_COOKIE, decltype(&::RoUnregisterForApartmentShutdown), ::RoUnregisterForApartmentShutdown>;
struct apartment_variable_platform
{
static unsigned long long GetApartmentId()
{
unsigned long long apartmentId{};
FAIL_FAST_IF_FAILED(RoGetApartmentIdentifier(&apartmentId));
return apartmentId;
}
static auto RegisterForApartmentShutdown(IApartmentShutdown* observer)
{
unsigned long long id{};
shutdown_type cookie;
THROW_IF_FAILED(RoRegisterForApartmentShutdown(observer, &id, cookie.put()));
return cookie;
}
static void UnRegisterForApartmentShutdown(APARTMENT_SHUTDOWN_REGISTRATION_COOKIE cookie)
{
FAIL_FAST_IF_FAILED(RoUnregisterForApartmentShutdown(cookie));
}
static auto CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/)
{
return wil::CoInitializeEx(coinitFlags);
}
// disable the test hook
inline static constexpr unsigned long AsyncRundownDelayForTestingRaces = INFINITE;
using shutdown_type = wil::unique_apartment_shutdown_registration;
};
enum class apartment_variable_leak_action { fail_fast, ignore };
// "pins" the current module in memory by incrementing the module reference count and leaking that.
inline void ensure_module_stays_loaded()
{
static INIT_ONCE s_initLeakModule{}; // avoiding magic statics
wil::init_once_failfast(s_initLeakModule, []()
{
HMODULE result{};
FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, L"", &result));
return S_OK;
});
}
namespace details
{
// For the address of data, you can detect global variables by the ability to resolve the module from the address.
inline bool IsGlobalVariable(const void* moduleAddress) noexcept
{
wil::unique_hmodule moduleHandle;
return GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<PCWSTR>(moduleAddress), &moduleHandle) != FALSE;
}
struct any_maker_base
{
std::any(*adapter)(void*);
void* inner;
WI_NODISCARD std::any operator()() const
{
return adapter(inner);
}
};
template<typename T>
struct any_maker : any_maker_base
{
any_maker()
{
adapter = [](auto) -> std::any { return T{}; };
}
any_maker(T(*maker)())
{
adapter = [](auto maker) -> std::any { return reinterpret_cast<T(*)()>(maker)(); };
inner = reinterpret_cast<void*>(maker);
}
template<typename F>
any_maker(F&& f)
{
adapter = [](auto maker) -> std::any { return reinterpret_cast<F*>(maker)[0](); };
inner = std::addressof(f);
}
};
template<apartment_variable_leak_action leak_action = apartment_variable_leak_action::fail_fast,
typename test_hook = apartment_variable_platform>
struct apartment_variable_base
{
inline static winrt::slim_mutex s_lock;
struct apartment_variable_storage
{
apartment_variable_storage(apartment_variable_storage&& other) noexcept = default;
apartment_variable_storage(const apartment_variable_storage& other) = delete;
apartment_variable_storage(typename test_hook::shutdown_type&& cookie_) : cookie(std::move(cookie_))
{
}
winrt::apartment_context context;
typename test_hook::shutdown_type cookie;
// Variables are stored using the address of the apartment_variable_base<> as the key.
std::unordered_map<apartment_variable_base<leak_action, test_hook>*, std::any> variables;
};
// Apartment id -> variables storage.
inline static wil::object_without_destructor_on_shutdown<
std::unordered_map<unsigned long long, apartment_variable_storage>>
s_apartmentStorage;
constexpr apartment_variable_base() = default;
~apartment_variable_base()
{
// Global variables (object with static storage duration)
// are run down when the process is shutting down or when the
// dll is unloaded. At these points it is not possible to start
// an async operation and the work performed is not needed,
// the apartments with variable have been run down already.
const auto isGlobal = details::IsGlobalVariable(this);
if (!isGlobal)
{
clear_all_apartments_async();
}
if constexpr (leak_action == apartment_variable_leak_action::fail_fast)
{
if (isGlobal && !ProcessShutdownInProgress())
{
// If you hit this fail fast it means the storage in s_apartmentStorage will be leaked.
// For apartment variables used in .exes, this is expected and
// this fail fast should be disabled using
// wil::apartment_variable<T, wil::apartment_variable_leak_action::ignore>
//
// For DLLs, if this is expected, disable this fail fast using
// wil::apartment_variable<T, wil::apartment_variable_leak_action::ignore>
//
// Use of apartment variables in DLLs only loaded by COM will never hit this case
// as COM will unload DLLs before apartments are rundown,
// providing the opportunity to empty s_apartmentStorage.
//
// But DLLs loaded and unloaded to call DLL entry points (outside of COM) may
// create variable storage that can't be cleaned up as the DLL lifetime is
// shorter that the COM lifetime. In these cases either
// 1) accept the leaks and disable the fail fast as describe above
// 2) disable module unloading by calling wil::ensure_module_stays_loaded
// 3) CoCreate an object from this DLL to make COM aware of the DLL
FAIL_FAST_IF(!s_apartmentStorage.get().empty());
}
}
}
// non-copyable, non-assignable
apartment_variable_base(apartment_variable_base const&) = delete;
void operator=(apartment_variable_base const&) = delete;
// get current value or throw if no value has been set
std::any& get_existing()
{
if (auto any = get_if())
{
return *any;
}
THROW_HR(E_NOT_SET);
}
static apartment_variable_storage* get_current_apartment_variable_storage()
{
auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId());
if (storage != s_apartmentStorage.get().end())
{
return &storage->second;
}
return nullptr;
}
apartment_variable_storage* ensure_current_apartment_variables()
{
auto variables = get_current_apartment_variable_storage();
if (variables)
{
return variables;
}
struct ApartmentObserver : public winrt::implements<ApartmentObserver, IApartmentShutdown>
{
void STDMETHODCALLTYPE OnUninitialize(unsigned long long apartmentId) noexcept override
{
// This code runs at apartment rundown so be careful to avoid deadlocks by
// extracting the variables under the lock then release them outside.
auto variables = [apartmentId]()
{
auto lock = winrt::slim_lock_guard(s_lock);
return s_apartmentStorage.get().extract(apartmentId);
}();
WI_ASSERT(variables.key() == apartmentId);
// The system implicitly releases the shutdown observer
// after invoking the callback and does not allow calling unregister
// in the callback. So release the reference to the registration.
variables.mapped().cookie.release();
}
};
auto shutdownRegistration = test_hook::RegisterForApartmentShutdown(winrt::make<ApartmentObserver>().get());
return &s_apartmentStorage.get().insert({ test_hook::GetApartmentId(), apartment_variable_storage(std::move(shutdownRegistration)) }).first->second;
}
// get current value or custom-construct one on demand
template<typename T>
std::any& get_or_create(any_maker<T> && creator)
{
apartment_variable_storage* variable_storage = nullptr;
{ // scope for lock
auto lock = winrt::slim_lock_guard(s_lock);
variable_storage = ensure_current_apartment_variables();
auto variable = variable_storage->variables.find(this);
if (variable != variable_storage->variables.end())
{
return variable->second;
}
} // drop the lock
// create the object outside the lock to avoid reentrant deadlock
auto value = creator();
auto insert_lock = winrt::slim_lock_guard(s_lock);
// The insertion may fail if creator() recursively caused itself to be created,
// in which case we return the existing object and the falsely-created one is discarded.
return variable_storage->variables.insert({ this, std::move(value) }).first->second;
}
// get pointer to current value or nullptr if no value has been set
std::any* get_if()
{
auto lock = winrt::slim_lock_guard(s_lock);
if (auto variable_storage = get_current_apartment_variable_storage())
{
auto variable = variable_storage->variables.find(this);
if (variable != variable_storage->variables.end())
{
return &(variable->second);
}
}
return nullptr;
}
// replace or create the current value, fail fasts if the value is not already stored
void set(std::any value)
{
// release value, with the swapped value, outside of the lock
{
auto lock = winrt::slim_lock_guard(s_lock);
auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId());
FAIL_FAST_IF(storage == s_apartmentStorage.get().end());
auto& variable_storage = storage->second;
auto variable = variable_storage.variables.find(this);
FAIL_FAST_IF(variable == variable_storage.variables.end());
variable->second.swap(value);
}
}
// remove any current value
void clear()
{
auto lock = winrt::slim_lock_guard(s_lock);
if (auto variable_storage = get_current_apartment_variable_storage())
{
variable_storage->variables.erase(this);
if (variable_storage->variables.size() == 0)
{
s_apartmentStorage.get().erase(test_hook::GetApartmentId());
}
}
}
winrt::Windows::Foundation::IAsyncAction clear_all_apartments_async()
{
// gather all the apartments that hold objects we need to destruct
// (do not gather the objects themselves, because the apartment might
// destruct before we get around to it, and we should let the apartment
// destruct the object while it still can).
std::vector<winrt::apartment_context> contexts;
{ // scope for lock
auto lock = winrt::slim_lock_guard(s_lock);
for (auto& [id, storage] : s_apartmentStorage.get())
{
auto variable = storage.variables.find(this);
if (variable != storage.variables.end())
{
contexts.push_back(storage.context);
}
}
}
if (contexts.empty())
{
co_return;
}
wil::unique_mta_usage_cookie mta_reference; // need to extend the MTA due to async cleanup
FAIL_FAST_IF_FAILED(CoIncrementMTAUsage(mta_reference.put()));
// From a background thread hop into each apartment to run down the object
// if it's still there.
co_await winrt::resume_background();
// This hook enables testing the case where execution of this method loses the race with
// apartment rundown by other means.
if constexpr (test_hook::AsyncRundownDelayForTestingRaces != INFINITE)
{
Sleep(test_hook::AsyncRundownDelayForTestingRaces);
}
for (auto&& context : contexts)
{
try
{
co_await context;
clear();
}
catch (winrt::hresult_error const& e)
{
// Ignore failure if apartment ran down before we could clean it up.
// The object already ran down as part of apartment cleanup.
if ((e.code() != RPC_E_SERVER_DIED_DNE) &&
(e.code() != RPC_E_DISCONNECTED))
{
throw;
}
}
catch (...)
{
FAIL_FAST();
}
}
}
static const auto& storage()
{
return s_apartmentStorage.get();
}
static size_t current_apartment_variable_count()
{
auto lock = winrt::slim_lock_guard(s_lock);
if (auto variable_storage = get_current_apartment_variable_storage())
{
return variable_storage->variables.size();
}
return 0;
}
};
}
// Apartment variables enable storing COM objects safely in globals
// (objects with static storage duration) by creating a unique copy
// in each apartment and managing their lifetime based on apartment rundown
// notifications.
// They can also be used for automatic or dynamic storage duration but those
// cases are less common.
// This type is also useful for storing references to apartment affine objects.
//
// Note, that apartment variables hosted in a COM DLL need to integrate with
// the DllCanUnloadNow() function to include the ref counts contributed by
// C++ WinRT objects. This is automatic for DLLs that host C++ WinRT objects
// but WRL projects will need to be updated to call winrt::get_module_lock().
template<typename T, apartment_variable_leak_action leak_action = apartment_variable_leak_action::fail_fast,
typename test_hook = wil::apartment_variable_platform>
struct apartment_variable : details::apartment_variable_base<leak_action, test_hook>
{
using base = details::apartment_variable_base<leak_action, test_hook>;
constexpr apartment_variable() = default;
// Get current value or throw if no value has been set.
T& get_existing() { return std::any_cast<T&>(base::get_existing()); }
// Get current value or default-construct one on demand.
T& get_or_create()
{
return std::any_cast<T&>(base::get_or_create(details::any_maker<T>()));
}
// Get current value or custom-construct one on demand.
template<typename F>
T& get_or_create(F&& f)
{
return std::any_cast<T&>(base::get_or_create(details::any_maker<T>(std::forward<F>(f))));
}
// get pointer to current value or nullptr if no value has been set
T* get_if() { return std::any_cast<T>(base::get_if()); }
// replace or create the current value, fail fasts if the value is not already stored
template<typename V> void set(V&& value) { return base::set(std::forward<V>(value)); }
// Clear the value in the current apartment.
using base::clear;
// Asynchronously clear the value in all apartments it is present in.
using base::clear_all_apartments_async;
// For testing only.
// 1) To observe the state of the storage in the debugger assign this to
// a temporary variable (const&) and watch its contents.
// 2) Use this to test the implementation.
using base::storage;
// For testing only. The number of variables in the current apartment.
using base::current_apartment_variable_count;
};
}
#endif // __WIL_COM_APARTMENT_VARIABLE_INCLUDED

View file

@ -0,0 +1,798 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_COMMON_INCLUDED
#define __WIL_COMMON_INCLUDED
#if defined(_KERNEL_MODE ) && !defined(__WIL_MIN_KERNEL)
// This define indicates that the WIL usage is in a kernel mode context where
// a high degree of WIL functionality is desired.
//
// Use (sparingly) to change behavior based on whether WIL is being used in kernel
// mode or user mode.
#define WIL_KERNEL_MODE
#endif
// Defining WIL_HIDE_DEPRECATED will hide everything deprecated.
// Each wave of deprecation will add a new WIL_HIDE_DEPRECATED_YYMM number that can be used to lock deprecation at
// a particular point, allowing components to avoid backslide and catch up to the current independently.
#ifdef WIL_HIDE_DEPRECATED
#define WIL_HIDE_DEPRECATED_1809
#endif
#ifdef WIL_HIDE_DEPRECATED_1809
#define WIL_HIDE_DEPRECATED_1612
#endif
#ifdef WIL_HIDE_DEPRECATED_1612
#define WIL_HIDE_DEPRECATED_1611
#endif
// Implementation side note: ideally the deprecation would be done with the function-level declspec
// as it allows you to utter the error text when used. The declspec works, but doing it selectively with
// a macro makes intellisense deprecation comments not work. So we just use the #pragma deprecation.
#ifdef WIL_WARN_DEPRECATED
#define WIL_WARN_DEPRECATED_1809
#endif
#ifdef WIL_WARN_DEPRECATED_1809
#define WIL_WARN_DEPRECATED_1612
#endif
#ifdef WIL_WARN_DEPRECATED_1612
#define WIL_WARN_DEPRECATED_1611
#endif
#ifdef WIL_WARN_DEPRECATED_1809
#define WIL_WARN_DEPRECATED_1809_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
#else
#define WIL_WARN_DEPRECATED_1809_PRAGMA(...)
#endif
#ifdef WIL_WARN_DEPRECATED_1611
#define WIL_WARN_DEPRECATED_1611_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
#else
#define WIL_WARN_DEPRECATED_1611_PRAGMA(...)
#endif
#ifdef WIL_WARN_DEPRECATED_1612
#define WIL_WARN_DEPRECATED_1612_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
#else
#define WIL_WARN_DEPRECATED_1612_PRAGMA(...)
#endif
#if defined(_MSVC_LANG)
#define __WI_SUPPRESS_4127_S __pragma(warning(push)) __pragma(warning(disable:4127)) __pragma(warning(disable:26498)) __pragma(warning(disable:4245))
#define __WI_SUPPRESS_4127_E __pragma(warning(pop))
#define __WI_SUPPRESS_NULLPTR_ANALYSIS __pragma(warning(suppress:28285)) __pragma(warning(suppress:6504))
#define __WI_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495))
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439))
#else
#define __WI_SUPPRESS_4127_S
#define __WI_SUPPRESS_4127_E
#define __WI_SUPPRESS_NULLPTR_ANALYSIS
#define __WI_SUPPRESS_NONINIT_ANALYSIS
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS
#endif
#include <sal.h>
// Some SAL remapping / decoration to better support Doxygen. Macros that look like function calls can
// confuse Doxygen when they are used to decorate a function or variable. We simplify some of these to
// basic macros without the function for common use cases.
/// @cond
#define _Success_return_ _Success_(return)
#define _Success_true_ _Success_(true)
#define __declspec_noinline_ __declspec(noinline)
#define __declspec_selectany_ __declspec(selectany)
/// @endcond
//! @defgroup macrobuilding Macro Composition
//! The following macros are building blocks primarily intended for authoring other macros.
//! @{
//! Re-state a macro value (indirection for composition)
#define WI_FLATTEN(...) __VA_ARGS__
/// @cond
#define __WI_PASTE_imp(a, b) a##b
/// @endcond
//! This macro is for use in other macros to paste two tokens together, such as a constant and the __LINE__ macro.
#define WI_PASTE(a, b) __WI_PASTE_imp(a, b)
/// @cond
#define __WI_HAS_VA_OPT_IMPL(F, T, ...) T
#define __WI_HAS_VA_OPT_(...) __WI_HAS_VA_OPT_IMPL(__VA_OPT__(0,) 1, 0)
/// @endcond
//! Evaluates to '1' when support for '__VA_OPT__' is available, else '0'
#define WI_HAS_VA_OPT __WI_HAS_VA_OPT_(unused)
/// @cond
#define __WI_ARGS_COUNT1(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, \
A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, \
A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75, A76, A77, A78, A79, A80, A81, A82, A83, A84, A85, A86, A87, A88, A89, \
A90, A91, A92, A93, A94, A95, A96, A97, A98, A99, count, ...) count
#define __WI_ARGS_COUNT0(...) WI_FLATTEN(__WI_ARGS_COUNT1(__VA_ARGS__, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \
79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \
39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
#define __WI_ARGS_COUNT_PREFIX(...) 0, __VA_ARGS__
/// @endcond
//! This variadic macro returns the number of arguments passed to it (up to 99).
#if WI_HAS_VA_OPT
#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(0 __VA_OPT__(, __VA_ARGS__))
#else
#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(__WI_ARGS_COUNT_PREFIX(__VA_ARGS__))
#endif
/// @cond
#define __WI_FOR_imp0( fn)
#define __WI_FOR_imp1( fn, arg) fn(arg)
#define __WI_FOR_imp2( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp1(fn, __VA_ARGS__))
#define __WI_FOR_imp3( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp2(fn, __VA_ARGS__))
#define __WI_FOR_imp4( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp3(fn, __VA_ARGS__))
#define __WI_FOR_imp5( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp4(fn, __VA_ARGS__))
#define __WI_FOR_imp6( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp5(fn, __VA_ARGS__))
#define __WI_FOR_imp7( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp6(fn, __VA_ARGS__))
#define __WI_FOR_imp8( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp7(fn, __VA_ARGS__))
#define __WI_FOR_imp9( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp8(fn, __VA_ARGS__))
#define __WI_FOR_imp10(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp9(fn, __VA_ARGS__))
#define __WI_FOR_imp11(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp10(fn, __VA_ARGS__))
#define __WI_FOR_imp12(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp11(fn, __VA_ARGS__))
#define __WI_FOR_imp13(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp12(fn, __VA_ARGS__))
#define __WI_FOR_imp14(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp13(fn, __VA_ARGS__))
#define __WI_FOR_imp15(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp14(fn, __VA_ARGS__))
#define __WI_FOR_imp16(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp15(fn, __VA_ARGS__))
#define __WI_FOR_imp17(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp16(fn, __VA_ARGS__))
#define __WI_FOR_imp18(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp17(fn, __VA_ARGS__))
#define __WI_FOR_imp19(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp18(fn, __VA_ARGS__))
#define __WI_FOR_imp20(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp19(fn, __VA_ARGS__))
#define __WI_FOR_imp21(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp20(fn, __VA_ARGS__))
#define __WI_FOR_imp22(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp21(fn, __VA_ARGS__))
#define __WI_FOR_imp23(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp22(fn, __VA_ARGS__))
#define __WI_FOR_imp24(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp23(fn, __VA_ARGS__))
#define __WI_FOR_imp25(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp24(fn, __VA_ARGS__))
#define __WI_FOR_imp26(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp25(fn, __VA_ARGS__))
#define __WI_FOR_imp27(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp26(fn, __VA_ARGS__))
#define __WI_FOR_imp28(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp27(fn, __VA_ARGS__))
#define __WI_FOR_imp29(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp28(fn, __VA_ARGS__))
#define __WI_FOR_imp30(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp29(fn, __VA_ARGS__))
#define __WI_FOR_imp31(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp30(fn, __VA_ARGS__))
#define __WI_FOR_imp32(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp31(fn, __VA_ARGS__))
#define __WI_FOR_imp33(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp32(fn, __VA_ARGS__))
#define __WI_FOR_imp34(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp33(fn, __VA_ARGS__))
#define __WI_FOR_imp35(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp34(fn, __VA_ARGS__))
#define __WI_FOR_imp36(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp35(fn, __VA_ARGS__))
#define __WI_FOR_imp37(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp36(fn, __VA_ARGS__))
#define __WI_FOR_imp38(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp37(fn, __VA_ARGS__))
#define __WI_FOR_imp39(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp38(fn, __VA_ARGS__))
#define __WI_FOR_imp40(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp39(fn, __VA_ARGS__))
#define __WI_FOR_imp41(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp40(fn, __VA_ARGS__))
#define __WI_FOR_imp42(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp41(fn, __VA_ARGS__))
#define __WI_FOR_imp43(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp42(fn, __VA_ARGS__))
#define __WI_FOR_imp44(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp43(fn, __VA_ARGS__))
#define __WI_FOR_imp45(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp44(fn, __VA_ARGS__))
#define __WI_FOR_imp46(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp45(fn, __VA_ARGS__))
#define __WI_FOR_imp47(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp46(fn, __VA_ARGS__))
#define __WI_FOR_imp48(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp47(fn, __VA_ARGS__))
#define __WI_FOR_imp49(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp48(fn, __VA_ARGS__))
#define __WI_FOR_imp50(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp49(fn, __VA_ARGS__))
#define __WI_FOR_imp51(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp50(fn, __VA_ARGS__))
#define __WI_FOR_imp52(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp51(fn, __VA_ARGS__))
#define __WI_FOR_imp53(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp52(fn, __VA_ARGS__))
#define __WI_FOR_imp54(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp53(fn, __VA_ARGS__))
#define __WI_FOR_imp55(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp54(fn, __VA_ARGS__))
#define __WI_FOR_imp56(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp55(fn, __VA_ARGS__))
#define __WI_FOR_imp57(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp56(fn, __VA_ARGS__))
#define __WI_FOR_imp58(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp57(fn, __VA_ARGS__))
#define __WI_FOR_imp59(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp58(fn, __VA_ARGS__))
#define __WI_FOR_imp60(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp59(fn, __VA_ARGS__))
#define __WI_FOR_imp61(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp60(fn, __VA_ARGS__))
#define __WI_FOR_imp62(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp61(fn, __VA_ARGS__))
#define __WI_FOR_imp63(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp62(fn, __VA_ARGS__))
#define __WI_FOR_imp64(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp63(fn, __VA_ARGS__))
#define __WI_FOR_imp65(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp64(fn, __VA_ARGS__))
#define __WI_FOR_imp66(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp65(fn, __VA_ARGS__))
#define __WI_FOR_imp67(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp66(fn, __VA_ARGS__))
#define __WI_FOR_imp68(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp67(fn, __VA_ARGS__))
#define __WI_FOR_imp69(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp68(fn, __VA_ARGS__))
#define __WI_FOR_imp70(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp69(fn, __VA_ARGS__))
#define __WI_FOR_imp71(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp70(fn, __VA_ARGS__))
#define __WI_FOR_imp72(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp71(fn, __VA_ARGS__))
#define __WI_FOR_imp73(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp72(fn, __VA_ARGS__))
#define __WI_FOR_imp74(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp73(fn, __VA_ARGS__))
#define __WI_FOR_imp75(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp74(fn, __VA_ARGS__))
#define __WI_FOR_imp76(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp75(fn, __VA_ARGS__))
#define __WI_FOR_imp77(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp76(fn, __VA_ARGS__))
#define __WI_FOR_imp78(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp77(fn, __VA_ARGS__))
#define __WI_FOR_imp79(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp78(fn, __VA_ARGS__))
#define __WI_FOR_imp80(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp79(fn, __VA_ARGS__))
#define __WI_FOR_imp81(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp80(fn, __VA_ARGS__))
#define __WI_FOR_imp82(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp81(fn, __VA_ARGS__))
#define __WI_FOR_imp83(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp82(fn, __VA_ARGS__))
#define __WI_FOR_imp84(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp83(fn, __VA_ARGS__))
#define __WI_FOR_imp85(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp84(fn, __VA_ARGS__))
#define __WI_FOR_imp86(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp85(fn, __VA_ARGS__))
#define __WI_FOR_imp87(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp86(fn, __VA_ARGS__))
#define __WI_FOR_imp88(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp87(fn, __VA_ARGS__))
#define __WI_FOR_imp89(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp88(fn, __VA_ARGS__))
#define __WI_FOR_imp90(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp89(fn, __VA_ARGS__))
#define __WI_FOR_imp91(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp90(fn, __VA_ARGS__))
#define __WI_FOR_imp92(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp91(fn, __VA_ARGS__))
#define __WI_FOR_imp93(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp92(fn, __VA_ARGS__))
#define __WI_FOR_imp94(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp93(fn, __VA_ARGS__))
#define __WI_FOR_imp95(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp94(fn, __VA_ARGS__))
#define __WI_FOR_imp96(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp95(fn, __VA_ARGS__))
#define __WI_FOR_imp97(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp96(fn, __VA_ARGS__))
#define __WI_FOR_imp98(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp97(fn, __VA_ARGS__))
#define __WI_FOR_imp99(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp98(fn, __VA_ARGS__))
#define __WI_FOR_imp(n, fnAndArgs) WI_PASTE(__WI_FOR_imp, n) fnAndArgs
/// @endcond
//! Iterates through each of the given arguments invoking the specified macro against each one.
#define WI_FOREACH(fn, ...) __WI_FOR_imp(WI_ARGS_COUNT(__VA_ARGS__), (fn, ##__VA_ARGS__))
//! Dispatches a single macro name to separate macros based on the number of arguments passed to it.
#define WI_MACRO_DISPATCH(name, ...) WI_PASTE(WI_PASTE(name, WI_ARGS_COUNT(__VA_ARGS__)), (__VA_ARGS__))
//! @} // Macro composition helpers
#if !defined(__cplusplus) || defined(__WIL_MIN_KERNEL)
#define WI_ODR_PRAGMA(NAME, TOKEN)
#define WI_NOEXCEPT
#else
#pragma warning(push)
#pragma warning(disable:4714) // __forceinline not honored
// DO NOT add *any* further includes to this file -- there should be no dependencies from its usage
#include "wistd_type_traits.h"
//! This macro inserts ODR violation protection; the macro allows it to be compatible with straight "C" code
#define WI_ODR_PRAGMA(NAME, TOKEN) __pragma(detect_mismatch("ODR_violation_" NAME "_mismatch", TOKEN))
#ifdef WIL_KERNEL_MODE
WI_ODR_PRAGMA("WIL_KERNEL_MODE", "1")
#else
WI_ODR_PRAGMA("WIL_KERNEL_MODE", "0")
#endif
#if (defined(_CPPUNWIND) || defined(__EXCEPTIONS)) && !defined(WIL_SUPPRESS_EXCEPTIONS)
/** This define is automatically set when exceptions are enabled within wil.
It is automatically defined when your code is compiled with exceptions enabled (via checking for the built-in
_CPPUNWIND or __EXCEPTIONS flag) unless you explicitly define WIL_SUPPRESS_EXCEPTIONS ahead of including your first wil
header. All exception-based WIL methods and classes are included behind:
~~~~
#ifdef WIL_ENABLE_EXCEPTIONS
// code
#endif
~~~~
This enables exception-free code to directly include WIL headers without worrying about exception-based
routines suddenly becoming available. */
#define WIL_ENABLE_EXCEPTIONS
#endif
/// @endcond
/// @cond
#if defined(WIL_EXCEPTION_MODE)
static_assert(WIL_EXCEPTION_MODE <= 2, "Invalid exception mode");
#elif !defined(WIL_LOCK_EXCEPTION_MODE)
#define WIL_EXCEPTION_MODE 0 // default, can link exception-based and non-exception based libraries together
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "0")
#elif defined(WIL_ENABLE_EXCEPTIONS)
#define WIL_EXCEPTION_MODE 1 // new code optimization: ONLY support linking libraries together that have exceptions enabled
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "1")
#else
#define WIL_EXCEPTION_MODE 2 // old code optimization: ONLY support linking libraries that are NOT using exceptions
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "2")
#endif
#if WIL_EXCEPTION_MODE == 1 && !defined(WIL_ENABLE_EXCEPTIONS)
#error Must enable exceptions when WIL_EXCEPTION_MODE == 1
#endif
// block for documentation only
#if defined(WIL_DOXYGEN)
/** This define can be explicitly set to disable exception usage within wil.
Normally this define is never needed as the WIL_ENABLE_EXCEPTIONS macro is enabled automatically by looking
at _CPPUNWIND. If your code compiles with exceptions enabled, but does not want to enable the exception-based
classes and methods from WIL, define this macro ahead of including the first WIL header. */
#define WIL_SUPPRESS_EXCEPTIONS
/** This define can be explicitly set to lock the process exception mode to WIL_ENABLE_EXCEPTIONS.
Locking the exception mode provides optimizations to exception barriers, staging hooks and DLL load costs as it eliminates the need to
do copy-on-write initialization of various function pointers and the necessary indirection that's done within WIL to avoid ODR violations
when linking libraries together with different exception handling semantics. */
#define WIL_LOCK_EXCEPTION_MODE
/** This define explicit sets the exception mode for the process to control optimizations.
Three exception modes are available:
0) This is the default. This enables a binary to link both exception-based and non-exception based libraries together that
use WIL. This adds overhead to exception barriers, DLL copy on write pages and indirection through function pointers to avoid ODR
violations when linking libraries together with different exception handling semantics.
1) Prefer this setting when it can be used. This locks the binary to only supporting libraries which were built with exceptions enabled.
2) This locks the binary to libraries built without exceptions. */
#define WIL_EXCEPTION_MODE
#endif
#if (__cplusplus >= 201703) || (_MSVC_LANG >= 201703)
#define WIL_HAS_CXX_17 1
#else
#define WIL_HAS_CXX_17 0
#endif
// Until we'll have C++17 enabled in our code base, we're falling back to SAL
#define WI_NODISCARD __WI_LIBCPP_NODISCARD_ATTRIBUTE
#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t<wistd::is_class<ptrType>::value, void*> = nullptr
#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t<!wistd::is_class<ptrType>::value, void*> = nullptr
//! @defgroup bitwise Bitwise Inspection and Manipulation
//! Bitwise helpers to improve readability and reduce the error rate of bitwise operations.
//! Several macros have been constructed to assist with bitwise inspection and manipulation. These macros exist
//! for two primary purposes:
//!
//! 1. To improve the readability of bitwise comparisons and manipulation.
//!
//! The macro names are the more concise, readable form of what's being done and do not require that any flags
//! or variables be specified multiple times for the comparisons.
//!
//! 2. To reduce the error rate associated with bitwise operations.
//!
//! The readability improvements naturally lend themselves to this by cutting down the number of concepts.
//! Using `WI_IsFlagSet(var, MyEnum::Flag)` rather than `((var & MyEnum::Flag) == MyEnum::Flag)` removes the comparison
//! operator and repetition in the flag value.
//!
//! Additionally, these macros separate single flag operations (which tend to be the most common) from multi-flag
//! operations so that compile-time errors are generated for bitwise operations which are likely incorrect,
//! such as: `WI_IsFlagSet(var, MyEnum::None)` or `WI_IsFlagSet(var, MyEnum::ValidMask)`.
//!
//! Note that the single flag helpers should be used when a compile-time constant single flag is being manipulated. These
//! helpers provide compile-time errors on misuse and should be preferred over the multi-flag helpers. The multi-flag helpers
//! should be used when multiple flags are being used simultaneously or when the flag values are not compile-time constants.
//!
//! Common example usage (manipulation of flag variables):
//! ~~~~
//! WI_SetFlag(m_flags, MyFlags::Foo); // Set a single flag in the given variable
//! WI_SetAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Set one or more flags
//! WI_ClearFlagIf(m_flags, MyFlags::Bar, isBarClosed); // Conditionally clear a single flag based upon a bool
//! WI_ClearAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Clear one or more flags from the given variable
//! WI_ToggleFlag(m_flags, MyFlags::Foo); // Toggle (change to the opposite value) a single flag
//! WI_UpdateFlag(m_flags, MyFlags::Bar, isBarClosed); // Sets or Clears a single flag from the given variable based upon a bool value
//! WI_UpdateFlagsInMask(m_flags, flagsMask, newFlagValues); // Sets or Clears the flags in flagsMask to the masked values from newFlagValues
//! ~~~~
//! Common example usage (inspection of flag variables):
//! ~~~~
//! if (WI_IsFlagSet(m_flags, MyFlags::Foo)) // Is a single flag set in the given variable?
//! if (WI_IsAnyFlagSet(m_flags, MyFlags::Foo | MyFlags::Bar)) // Is at least one flag from the given mask set?
//! if (WI_AreAllFlagsClear(m_flags, MyFlags::Foo | MyFlags::Bar)) // Are all flags in the given list clear?
//! if (WI_IsSingleFlagSet(m_flags)) // Is *exactly* one flag set in the given variable?
//! ~~~~
//! @{
//! Returns the unsigned type of the same width and numeric value as the given enum
#define WI_EnumValue(val) static_cast<::wil::integral_from_enum<decltype(val)>>(val)
//! Validates that exactly ONE bit is set in compile-time constant `flag`
#define WI_StaticAssertSingleBitSet(flag) static_cast<decltype(flag)>(::wil::details::verify_single_flag_helper<static_cast<unsigned long long>(WI_EnumValue(flag))>::value)
//! @name Bitwise manipulation macros
//! @{
//! Set zero or more bitflags specified by `flags` in the variable `var`.
#define WI_SetAllFlags(var, flags) ((var) |= (flags))
//! Set a single compile-time constant `flag` in the variable `var`.
#define WI_SetFlag(var, flag) WI_SetAllFlags(var, WI_StaticAssertSingleBitSet(flag))
//! Conditionally sets a single compile-time constant `flag` in the variable `var` only if `condition` is true.
#define WI_SetFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_SetFlag(var, flag); } } while ((void)0, 0)
//! Clear zero or more bitflags specified by `flags` from the variable `var`.
#define WI_ClearAllFlags(var, flags) ((var) &= ~(flags))
//! Clear a single compile-time constant `flag` from the variable `var`.
#define WI_ClearFlag(var, flag) WI_ClearAllFlags(var, WI_StaticAssertSingleBitSet(flag))
//! Conditionally clear a single compile-time constant `flag` in the variable `var` only if `condition` is true.
#define WI_ClearFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_ClearFlag(var, flag); } } while ((void)0, 0)
//! Changes a single compile-time constant `flag` in the variable `var` to be set if `isFlagSet` is true or cleared if `isFlagSet` is false.
#define WI_UpdateFlag(var, flag, isFlagSet) (wil::verify_bool(isFlagSet) ? WI_SetFlag(var, flag) : WI_ClearFlag(var, flag))
//! Changes only the flags specified by `flagsMask` in the variable `var` to match the corresponding flags in `newFlags`.
#define WI_UpdateFlagsInMask(var, flagsMask, newFlags) wil::details::UpdateFlagsInMaskHelper(var, flagsMask, newFlags)
//! Toggles (XOR the value) of multiple bitflags specified by `flags` in the variable `var`.
#define WI_ToggleAllFlags(var, flags) ((var) ^= (flags))
//! Toggles (XOR the value) of a single compile-time constant `flag` in the variable `var`.
#define WI_ToggleFlag(var, flag) WI_ToggleAllFlags(var, WI_StaticAssertSingleBitSet(flag))
//! @} // bitwise manipulation macros
//! @name Bitwise inspection macros
//! @{
//! Evaluates as true if every bitflag specified in `flags` is set within `val`.
#define WI_AreAllFlagsSet(val, flags) wil::details::AreAllFlagsSetHelper(val, flags)
//! Evaluates as true if one or more bitflags specified in `flags` are set within `val`.
#define WI_IsAnyFlagSet(val, flags) (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) != static_cast<decltype((val) & (flags))>(0))
//! Evaluates as true if a single compile-time constant `flag` is set within `val`.
#define WI_IsFlagSet(val, flag) WI_IsAnyFlagSet(val, WI_StaticAssertSingleBitSet(flag))
//! Evaluates as true if every bitflag specified in `flags` is clear within `val`.
#define WI_AreAllFlagsClear(val, flags) (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) == static_cast<decltype((val) & (flags))>(0))
//! Evaluates as true if one or more bitflags specified in `flags` are clear within `val`.
#define WI_IsAnyFlagClear(val, flags) (!wil::details::AreAllFlagsSetHelper(val, flags))
//! Evaluates as true if a single compile-time constant `flag` is clear within `val`.
#define WI_IsFlagClear(val, flag) WI_AreAllFlagsClear(val, WI_StaticAssertSingleBitSet(flag))
//! Evaluates as true if exactly one bit (any bit) is set within `val`.
#define WI_IsSingleFlagSet(val) wil::details::IsSingleFlagSetHelper(val)
//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val`.
#define WI_IsSingleFlagSetInMask(val, mask) wil::details::IsSingleFlagSetHelper((val) & (mask))
//! Evaluates as true if exactly one bit (any bit) is set within `val` or if there are no bits set within `val`.
#define WI_IsClearOrSingleFlagSet(val) wil::details::IsClearOrSingleFlagSetHelper(val)
//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val` or if there are no bits from `mask` set within `val`.
#define WI_IsClearOrSingleFlagSetInMask(val, mask) wil::details::IsClearOrSingleFlagSetHelper((val) & (mask))
//! @}
#if defined(WIL_DOXYGEN)
/** This macro provides a C++ header with a guaranteed initialization function.
Normally, were a global object's constructor used for this purpose, the optimizer/linker might throw
the object away if it's unreferenced (which throws away the side-effects that the initialization function
was trying to achieve). Using this macro forces linker inclusion of a variable that's initialized by the
provided function to elide that optimization.
//!
This functionality is primarily provided as a building block for header-based libraries (such as WIL)
to be able to layer additional functionality into other libraries by their mere inclusion. Alternative models
of initialization should be used whenever they are available.
~~~~
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
WI_HEADER_INITITALIZATION_FUNCTION(InitializeDesktopFamilyApis, []
{
g_pfnGetModuleName = GetCurrentModuleName;
g_pfnFailFastInLoaderCallout = FailFastInLoaderCallout;
return 1;
});
#endif
~~~~
The above example is used within WIL to decide whether or not the library containing WIL is allowed to use
desktop APIs. Building this functionality as #IFDEFs within functions would create ODR violations, whereas
doing it with global function pointers and header initialization allows a runtime determination. */
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn)
#elif defined(_M_IX86)
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \
extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast<unsigned char>(fn()); } \
__pragma(comment(linker, "/INCLUDE:_g_header_init_" #name))
#elif defined(_M_IA64) || defined(_M_AMD64) || defined(_M_ARM) || defined(_M_ARM64)
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \
extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast<unsigned char>(fn()); } \
__pragma(comment(linker, "/INCLUDE:g_header_init_" #name))
#else
#error linker pragma must include g_header_init variation
#endif
/** All Windows Implementation Library classes and functions are located within the "wil" namespace.
The 'wil' namespace is an intentionally short name as the intent is for code to be able to reference
the namespace directly (example: `wil::srwlock lock;`) without a using statement. Resist adding a using
statement for wil to avoid introducing potential name collisions between wil and other namespaces. */
namespace wil
{
/// @cond
namespace details
{
template <typename T>
class pointer_range
{
public:
pointer_range(T begin_, T end_) : m_begin(begin_), m_end(end_) {}
WI_NODISCARD T begin() const { return m_begin; }
WI_NODISCARD T end() const { return m_end; }
private:
T m_begin;
T m_end;
};
}
/// @endcond
/** Enables using range-based for between a begin and end object pointer.
~~~~
for (auto& obj : make_range(objPointerBegin, objPointerEnd)) { }
~~~~ */
template <typename T>
details::pointer_range<T> make_range(T begin, T end)
{
return details::pointer_range<T>(begin, end);
}
/** Enables using range-based for on a range when given the base pointer and the number of objects in the range.
~~~~
for (auto& obj : make_range(objPointer, objCount)) { }
~~~~ */
template <typename T>
details::pointer_range<T> make_range(T begin, size_t count)
{
return details::pointer_range<T>(begin, begin + count);
}
//! @defgroup outparam Output Parameters
//! Improve the conciseness of assigning values to optional output parameters.
//! @{
/** Assign the given value to an optional output parameter.
Makes code more concise by removing trivial `if (outParam)` blocks. */
template <typename T>
inline void assign_to_opt_param(_Out_opt_ T *outParam, T val)
{
if (outParam != nullptr)
{
*outParam = val;
}
}
/** Assign NULL to an optional output pointer parameter.
Makes code more concise by removing trivial `if (outParam)` blocks. */
template <typename T>
inline void assign_null_to_opt_param(_Out_opt_ T *outParam)
{
if (outParam != nullptr)
{
*outParam = nullptr;
}
}
//! @} // end output parameter helpers
/** Performs a logical or of the given variadic template parameters allowing indirect compile-time boolean evaluation.
Example usage:
~~~~
template <unsigned int... Rest>
struct FeatureRequiredBy
{
static const bool enabled = wil::variadic_logical_or<WilFeature<Rest>::enabled...>::value;
};
~~~~ */
template <bool...> struct variadic_logical_or;
/// @cond
template <> struct variadic_logical_or<> : wistd::false_type { };
template <bool... Rest> struct variadic_logical_or<true, Rest...> : wistd::true_type { };
template <bool... Rest> struct variadic_logical_or<false, Rest...> : variadic_logical_or<Rest...>::type { };
/// @endcond
/// @cond
namespace details
{
template <unsigned long long flag>
struct verify_single_flag_helper
{
static_assert((flag != 0) && ((flag & (flag - 1)) == 0), "Single flag expected, zero or multiple flags found");
static const unsigned long long value = flag;
};
}
/// @endcond
//! @defgroup typesafety Type Validation
//! Helpers to validate variable types to prevent accidental, but allowed type conversions.
//! These helpers are most useful when building macros that accept a particular type. Putting these functions around the types accepted
//! prior to pushing that type through to a function (or using it within the macro) allows the macro to add an additional layer of type
//! safety that would ordinarily be stripped away by C++ implicit conversions. This system is extensively used in the error handling helper
//! macros to validate the types given to various macro parameters.
//! @{
/** Verify that `val` can be evaluated as a logical bool.
Other types will generate an intentional compilation error. Allowed types for a logical bool are bool, BOOL,
boolean, BOOLEAN, and classes with an explicit bool cast.
@param val The logical bool expression
@return A C++ bool representing the evaluation of `val`. */
template <typename T, __R_ENABLE_IF_IS_CLASS(T)>
_Post_satisfies_(return == static_cast<bool>(val))
__forceinline constexpr bool verify_bool(const T& val)
{
return static_cast<bool>(val);
}
template <typename T, __R_ENABLE_IF_IS_NOT_CLASS(T)>
__forceinline constexpr bool verify_bool(T /*val*/)
{
static_assert(!wistd::is_same<T, T>::value, "Wrong Type: bool/BOOL/BOOLEAN/boolean expected");
return false;
}
template <>
_Post_satisfies_(return == val)
__forceinline constexpr bool verify_bool<bool>(bool val)
{
return val;
}
template <>
_Post_satisfies_(return == (val != 0))
__forceinline constexpr bool verify_bool<int>(int val)
{
return (val != 0);
}
template <>
_Post_satisfies_(return == (val != 0))
__forceinline constexpr bool verify_bool<unsigned char>(unsigned char val)
{
return (val != 0);
}
/** Verify that `val` is a Win32 BOOL value.
Other types (including other logical bool expressions) will generate an intentional compilation error. Note that this will
accept any `int` value as long as that is the underlying typedef behind `BOOL`.
@param val The Win32 BOOL returning expression
@return A Win32 BOOL representing the evaluation of `val`. */
template <typename T>
_Post_satisfies_(return == val)
__forceinline constexpr int verify_BOOL(T val)
{
// Note: Written in terms of 'int' as BOOL is actually: typedef int BOOL;
static_assert((wistd::is_same<T, int>::value), "Wrong Type: BOOL expected");
return val;
}
/** Verify that `hr` is an HRESULT value.
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the
underlying typedef behind HRESULT.
//!
Note that occasionally you might run into an HRESULT which is directly defined with a #define, such as:
~~~~
#define UIA_E_NOTSUPPORTED 0x80040204
~~~~
Though this looks like an `HRESULT`, this is actually an `unsigned long` (the hex specification forces this). When
these are encountered and they are NOT in the public SDK (have not yet shipped to the public), then you should change
their definition to match the manner in which `HRESULT` constants are defined in winerror.h:
~~~~
#define E_NOTIMPL _HRESULT_TYPEDEF_(0x80004001L)
~~~~
When these are encountered in the public SDK, their type should not be changed and you should use a static_cast
to use this value in a macro that utilizes `verify_hresult`, for example:
~~~~
RETURN_HR_IF(static_cast<HRESULT>(UIA_E_NOTSUPPORTED), (patternId != UIA_DragPatternId));
~~~~
@param hr The HRESULT returning expression
@return An HRESULT representing the evaluation of `val`. */
template <typename T>
_Post_satisfies_(return == hr)
inline constexpr long verify_hresult(T hr)
{
// Note: Written in terms of 'long' as HRESULT is actually: typedef _Return_type_success_(return >= 0) long HRESULT
static_assert(wistd::is_same<T, long>::value, "Wrong Type: HRESULT expected");
return hr;
}
/** Verify that `status` is an NTSTATUS value.
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the
underlying typedef behind NTSTATUS.
//!
Note that occasionally you might run into an NTSTATUS which is directly defined with a #define, such as:
~~~~
#define STATUS_NOT_SUPPORTED 0x1
~~~~
Though this looks like an `NTSTATUS`, this is actually an `unsigned long` (the hex specification forces this). When
these are encountered and they are NOT in the public SDK (have not yet shipped to the public), then you should change
their definition to match the manner in which `NTSTATUS` constants are defined in ntstatus.h:
~~~~
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
~~~~
When these are encountered in the public SDK, their type should not be changed and you should use a static_cast
to use this value in a macro that utilizes `verify_ntstatus`, for example:
~~~~
NT_RETURN_IF_FALSE(static_cast<NTSTATUS>(STATUS_NOT_SUPPORTED), (dispatch->Version == HKE_V1_0));
~~~~
@param status The NTSTATUS returning expression
@return An NTSTATUS representing the evaluation of `val`. */
template <typename T>
_Post_satisfies_(return == status)
inline long verify_ntstatus(T status)
{
// Note: Written in terms of 'long' as NTSTATUS is actually: typedef _Return_type_success_(return >= 0) long NTSTATUS
static_assert(wistd::is_same<T, long>::value, "Wrong Type: NTSTATUS expected");
return status;
}
/** Verify that `error` is a Win32 error code.
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is
the underlying type used for WIN32 error codes, as well as any `DWORD` (`unsigned long`) value since this is the type
commonly used when manipulating Win32 error codes.
@param error The Win32 error code returning expression
@return An Win32 error code representing the evaluation of `error`. */
template <typename T>
_Post_satisfies_(return == error)
inline T verify_win32(T error)
{
// Note: Win32 error code are defined as 'long' (#define ERROR_SUCCESS 0L), but are more frequently used as DWORD (unsigned long).
// This accept both types.
static_assert(wistd::is_same<T, long>::value || wistd::is_same<T, unsigned long>::value, "Wrong Type: Win32 error code (long / unsigned long) expected");
return error;
}
/// @} // end type validation routines
/// @cond
// Implementation details for macros and helper functions... do not use directly.
namespace details
{
// Use size-specific casts to avoid sign extending numbers -- avoid warning C4310: cast truncates constant value
#define __WI_MAKE_UNSIGNED(val) \
(__pragma(warning(push)) __pragma(warning(disable: 4310 4309)) (sizeof(val) == 1 ? static_cast<unsigned char>(val) : \
sizeof(val) == 2 ? static_cast<unsigned short>(val) : \
sizeof(val) == 4 ? static_cast<unsigned long>(val) : \
static_cast<unsigned long long>(val)) __pragma(warning(pop)))
#define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1)))
#define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val))
template <typename TVal, typename TFlags>
__forceinline constexpr bool AreAllFlagsSetHelper(TVal val, TFlags flags)
{
return ((val & flags) == static_cast<decltype(val & flags)>(flags));
}
template <typename TVal>
__forceinline constexpr bool IsSingleFlagSetHelper(TVal val)
{
return __WI_IS_SINGLE_FLAG_SET(val);
}
template <typename TVal>
__forceinline constexpr bool IsClearOrSingleFlagSetHelper(TVal val)
{
return ((val == static_cast<wistd::remove_reference_t<TVal>>(0)) || IsSingleFlagSetHelper(val));
}
template <typename TVal, typename TMask, typename TFlags>
__forceinline constexpr void UpdateFlagsInMaskHelper(_Inout_ TVal& val, TMask mask, TFlags flags)
{
val = static_cast<wistd::remove_reference_t<TVal>>((val & ~mask) | (flags & mask));
}
template <long>
struct variable_size;
template <>
struct variable_size<1>
{
using type = unsigned char;
};
template <>
struct variable_size<2>
{
using type = unsigned short;
};
template <>
struct variable_size<4>
{
using type = unsigned long;
};
template <>
struct variable_size<8>
{
using type = unsigned long long;
};
template <typename T>
struct variable_size_mapping
{
using type = typename variable_size<sizeof(T)>::type;
};
} // details
/// @endcond
/** Defines the unsigned type of the same width (1, 2, 4, or 8 bytes) as the given type.
This allows code to generically convert any enum class to it's corresponding underlying type. */
template <typename T>
using integral_from_enum = typename details::variable_size_mapping<T>::type;
//! Declares a name that intentionally hides a name from an outer scope.
//! Use this to prevent accidental use of a parameter or lambda captured variable.
using hide_name = void(struct hidden_name);
} // wil
#pragma warning(pop)
#endif // __cplusplus
#endif // __WIL_COMMON_INCLUDED

View file

@ -0,0 +1,810 @@
#ifndef __WIL_COROUTINE_INCLUDED
#define __WIL_COROUTINE_INCLUDED
/*
* A wil::task<T> / com_task<T> is a coroutine with the following characteristics:
*
* - T must be a copyable object, movable object, reference, or void.
* - The coroutine may be awaited at most once. The second await will crash.
* - The coroutine may be abandoned (allowed to destruct without co_await),
* in which case unobserved exceptions are fatal.
* - By default, wil::task resumes on an arbitrary thread.
* - By default, wil::com_task resumes in the same COM apartment.
* - task.resume_any_thread() allows resumption on any thread.
* - task.resume_same_apartment() forces resumption in the same COM apartment.
*
* The wil::task and wil::com_task are intended to supplement PPL and C++/WinRT,
* not to replace them. It provides coroutine implementations for scenarios that PPL
* and C++/WinRT do not support, but it does not support everything that PPL and
* C++/WinRT do.
*
* The implementation is optimized on the assumption that the coroutine is
* awaited only once, and that the coroutine is discarded after completion.
* To ensure proper usage, the task object is move-only, and
* co_await takes ownership of the task. See further discussion below.
*
* Comparison with PPL and C++/WinRT:
*
* | | PPL | C++/WinRT | wil::*task |
* |-----------------------------------------------------|-----------|-----------|---------------|
* | T can be non-constructible | No | Yes | Yes |
* | T can be void | Yes | Yes | Yes |
* | T can be reference | No | No | Yes |
* | T can be WinRT object | Yes | Yes | Yes |
* | T can be non-WinRT object | Yes | No | Yes |
* | T can be move-only | No | No | Yes |
* | Coroutine can be cancelled | Yes | Yes | No |
* | Coroutine can throw arbitrary exceptions | Yes | No | Yes |
* | Can co_await more than once | Yes | No | No |
* | Can have multiple clients waiting for completion | Yes | No | No |
* | co_await resumes in same COM context | Sometimes | Yes | You choose [1]|
* | Can force co_await to resume in same context | Yes | N/A | Yes [1] |
* | Can force co_await to resume in any thread | Yes | No | Yes |
* | Can change coroutine's resumption model | No | No | Yes |
* | Can wait synchronously | Yes | Yes | Yes [2] |
* | Can be consumed by non-C++ languages | No | Yes | No |
* | Implementation is small and efficient | No | Yes | Yes |
* | Can abandon coroutine (fail to co_await) | Yes | Yes | Yes |
* | Exception in abandoned coroutine | Crash | Ignored | Crash |
* | Coroutine starts automatically | Yes | Yes | Yes |
* | Coroutine starts synchronously | No | Yes | Yes |
* | Integrates with C++/WinRT coroutine callouts | No | Yes | No |
*
* [1] Resumption in the same COM apartment requires that you include COM headers.
* [2] Synchronous waiting requires that you include <synchapi.h> (usually via <windows.h>).
*
* You can include the COM headers and/or synchapi.h headers, and then
* re-include this header file to activate the features dependent upon
* those headers.
*
* Examples:
*
* Implement a coroutine that returns a move-only non-WinRT type
* and which resumes on an arbitrary thread.
*
* wil::task<wil::unique_cotaskmem_string> GetNameAsync()
* {
* co_await resume_background(); // do work on BG thread
* wil::unique_cotaskmem_string name;
* THROW_IF_FAILED(GetNameSlow(&name));
* co_return name; // awaiter will resume on arbitrary thread
* }
*
* Consumers:
*
* winrt::IAsyncAction UpdateNameAsync()
* {
* // wil::task resumes on an arbitrary thread.
* auto name = co_await GetNameAsync();
* // could be on any thread now
* co_await SendNameAsync(name.get());
* }
*
* winrt::IAsyncAction UpdateNameAsync()
* {
* // override default behavior of wil::task and
* // force it to resume in the same COM apartment.
* auto name = co_await GetNameAsync().resume_same_apartment();
* // so we are still on the UI thread
* NameElement().Text(winrt::hstring(name.get()));
* }
*
* Conversely, a coroutine that returns a
* wil::com_task<T> defaults to resuming in the same
* COM apartment, but you can allow it to resume on any thread
* by doing co_await GetNameAsync().resume_any_thread().
*
* There is no harm in doing resume_same_apartment() / resume_any_thread() for a
* task that already defaults to resuming in that manner. In fact, awaiting the
* task directly is just a shorthand for awaiting the corresponding
* resume_whatever() method.
*
* Alternatively, you can just convert between wil::task<T> and wil::com_task<T>
* to change the default resumption context.
*
* co_await wil::com_task(GetNameAsync()); // now defaults to resume_same_apartment();
*
* You can store the task in a variable, but since it is a move-only
* object, you will have to use std::move in order to transfer ownership out of
* an lvalue.
*
* winrt::IAsyncAction SomethingAsync()
* {
* wil::com_task<int> task;
* switch (source)
* {
* // Some of these might return wil::task<int>,
* // but assigning to a wil::com_task<int> will make
* // the task resume in the same COM apartment.
* case widget: task = GetValueFromWidgetAsync(); break;
* case gadget: task = GetValueFromGadgetAsync(); break;
* case doodad: task = GetValueFromDoodadAsync(); break;
* default: FAIL_FAST(); // unknown source
* }
* auto value = co_await std::move(task); // **** need std::move
* DoSomethingWith(value);
* }
*
* You can wait synchronously by calling get(). The usual caveats
* about synchronous waits on STA threads apply.
*
* auto value = GetValueFromWidgetAsync().get();
*
* auto task = GetValueFromWidgetAsync();
* auto value = std::move(task).get(); // **** need std::move
*/
// Detect which version of the coroutine standard we have.
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
#include <experimental/coroutine>
#define __WI_COROUTINE_NAMESPACE ::std::experimental
#elif defined(__cpp_lib_coroutine)
#include <coroutine>
#define __WI_COROUTINE_NAMESPACE ::std
#else
#error You must compile with C++20 coroutine support to use coroutine.h.
#endif
#include <atomic>
#include <exception>
#include <wil/wistd_memory.h>
#include <wil/wistd_type_traits.h>
#include <wil/result_macros.h>
namespace wil
{
// There are three general categories of T that you can
// use with a task. We give them these names:
//
// T = void ("void category")
// T = some kind of reference ("reference category")
// T = non-void non-reference ("object category")
//
// Take care that the implementation supports all three categories.
//
// There is a sub-category of object category for move-only types.
// We designed our task to be co_awaitable only once, so that
// it can contain a move-only type. Any transfer of T as an
// object category must be done as an rvalue reference.
template<typename T>
struct task;
template<typename T>
struct com_task;
}
namespace wil::details::coro
{
template<typename T>
struct task_promise;
// Unions may not contain references, C++/CX types, or void.
// To work around that, we put everything inside a result_wrapper
// struct, and put the struct in the union. For void,
// we create a special empty structure.
//
// get_value returns rvalue reference to T for object
// category, or just T itself for void and reference
// category.
//
// We take advantage of the reference collapsing rules
// so that T&& = T if T is reference category.
template<typename T>
struct result_wrapper
{
T value;
T get_value() { return wistd::forward<T>(value); }
};
template<>
struct result_wrapper<void>
{
void get_value() { }
};
// The result_holder is basically a
// std::variant<std::monotype, T, std::exception_ptr>
// but with these extra quirks:
// * The only valid transition is monotype -> something-else.
// Consequently, it does not have valueless_by_exception.
template<typename T>
struct result_holder
{
// The content of the result_holder
// depends on the result_status:
//
// empty: No active member.
// value: Active member is wrap.
// error: Active member is error.
enum class result_status { empty, value, error };
result_status status{ result_status::empty };
union variant
{
variant() {}
~variant() {}
result_wrapper<T> wrap;
std::exception_ptr error;
} result;
// emplace_value will be called with
//
// * no parameters (void category)
// * The reference type T (reference category)
// * Some kind of reference to T (object category)
//
// Set the status after constructing the object.
// That way, if object construction throws an exception,
// the holder remains empty.
template<typename...Args>
void emplace_value(Args&&... args)
{
WI_ASSERT(status == result_status::empty);
new (wistd::addressof(result.wrap)) result_wrapper<T>{ wistd::forward<Args>(args)... };
status = result_status::value;
}
void unhandled_exception() noexcept
{
WI_ASSERT(status == result_status::empty);
new (wistd::addressof(result.error)) std::exception_ptr(std::current_exception());
status = result_status::error;
}
T get_value()
{
if (status == result_status::value)
{
return result.wrap.get_value();
}
WI_ASSERT(status == result_status::error);
std::rethrow_exception(wistd::exchange(result.error, {}));
}
result_holder() = default;
result_holder(result_holder const&) = delete;
void operator=(result_holder const&) = delete;
~result_holder() noexcept(false)
{
switch (status)
{
case result_status::value:
result.wrap.~result_wrapper();
break;
case result_status::error:
// Rethrow unobserved exception. Delete this line to
// discard unobserved exceptions.
if (result.error) std::rethrow_exception(result.error);
result.error.~exception_ptr();
}
}
};
// Most of the work is done in the promise_base,
// It is a CRTP-like base class for task_promise<void> and
// task_promise<non-void> because the language forbids
// a single promise from containing both return_value and
// return_void methods (even if one of them is deleted by SFINAE).
template<typename T>
struct promise_base
{
// The coroutine state remains alive as long as the coroutine is
// still running (hasn't reached final_suspend) or the associated
// task has not yet abandoned the coroutine (either finished awaiting
// or destructed without awaiting).
//
// This saves an allocation, but does mean that the local
// frame of the coroutine will remain allocated (with the
// coroutine's imbound parameters still live) until all
// references are destroyed. To force the promise_base to be
// destroyed after co_await, we make the promise_base a
// move-only object and require co_await to be given an rvalue reference.
// Special values for m_waiting.
static void* running_ptr() { return nullptr; }
static void* completed_ptr() { return reinterpret_cast<void*>(1); }
static void* abandoned_ptr() { return reinterpret_cast<void*>(2); }
// The awaiting coroutine is resumed by calling the
// m_resumer with the m_waiting. If the resumer is null,
// then the m_waiting is assumed to be the address of a
// coroutine_handle<>, which is resumed synchronously.
// Externalizing the resumer allows unused awaiters to be
// removed by the linker and removes a hard dependency on COM.
// Using nullptr to represent the default resumer avoids a
// CFG check.
void(__stdcall* m_resumer)(void*);
std::atomic<void*> m_waiting{ running_ptr() };
result_holder<T> m_holder;
// Make it easier to access our CRTP derived class.
using Promise = task_promise<T>;
auto as_promise() noexcept
{
return static_cast<Promise*>(this);
}
// Make it easier to access the coroutine handle.
auto as_handle() noexcept
{
return __WI_COROUTINE_NAMESPACE::coroutine_handle<Promise>::from_promise(*as_promise());
}
auto get_return_object() noexcept
{
// let the compiler construct the task / com_task from the promise.
return as_promise();
}
void destroy()
{
as_handle().destroy();
}
// The client lost interest in the coroutine, either because they are discarding
// the result without awaiting (risky!), or because they have finished awaiting.
// Discarding the result without awaiting is risky because any exception in the coroutine
// will be unobserved and result in a crash. If you want to disallow it, then
// raise an exception if waiting == running_ptr.
void abandon()
{
auto waiting = m_waiting.exchange(abandoned_ptr(), std::memory_order_acq_rel);
if (waiting != running_ptr()) destroy();
}
__WI_COROUTINE_NAMESPACE::suspend_never initial_suspend() noexcept
{
return {};
}
template<typename...Args>
void emplace_value(Args&&... args)
{
m_holder.emplace_value(wistd::forward<Args>(args)...);
}
void unhandled_exception() noexcept
{
m_holder.unhandled_exception();
}
void resume_waiting_coroutine(void* waiting) const
{
if (m_resumer)
{
m_resumer(waiting);
}
else
{
__WI_COROUTINE_NAMESPACE::coroutine_handle<>::from_address(waiting).resume();
}
}
auto final_suspend() noexcept
{
struct awaiter : __WI_COROUTINE_NAMESPACE::suspend_always
{
promise_base& self;
void await_suspend(__WI_COROUTINE_NAMESPACE::coroutine_handle<>) const noexcept
{
// Need acquire so we can read from m_resumer.
// Need release so that the results are published in the case that nobody
// is awaiting right now, so that the eventual awaiter (possibly on another thread)
// can read the results.
auto waiting = self.m_waiting.exchange(completed_ptr(), std::memory_order_acq_rel);
if (waiting == abandoned_ptr())
{
self.destroy();
}
else if (waiting != running_ptr())
{
WI_ASSERT(waiting != completed_ptr());
self.resume_waiting_coroutine(waiting);
}
};
};
return awaiter{ {}, *this };
}
// The remaining methods are used by the awaiters.
bool client_await_ready()
{
// Need acquire in case the coroutine has already completed,
// so we can read the results. This matches the release in
// the final_suspend's await_suspend.
auto waiting = m_waiting.load(std::memory_order_acquire);
WI_ASSERT((waiting == running_ptr()) || (waiting == completed_ptr()));
return waiting != running_ptr();
}
auto client_await_suspend(void* waiting, void(__stdcall* resumer)(void*))
{
// "waiting" needs to be a pointer to an object. We reserve the first 16
// pseudo-pointers as sentinels.
WI_ASSERT(reinterpret_cast<uintptr_t>(waiting) > 16);
m_resumer = resumer;
// Acquire to ensure that we can read the results of the return value, if the coroutine is completed.
// Release to ensure that our resumption state is published, if the coroutine is not completed.
auto previous = m_waiting.exchange(waiting, std::memory_order_acq_rel);
// Suspend if the coroutine is still running.
// Otherwise, the coroutine is completed: Nobody will resume us, so we will have to resume ourselves.
WI_ASSERT((previous == running_ptr()) || (previous == completed_ptr()));
return previous == running_ptr();
}
T client_await_resume()
{
return m_holder.get_value();
}
};
template<typename T>
struct task_promise : promise_base<T>
{
template<typename U>
void return_value(U&& value)
{
this->emplace_value(wistd::forward<U>(value));
}
template<typename Dummy = void>
wistd::enable_if_t<!wistd::is_reference_v<T>, Dummy>
return_value(T const& value)
{
this->emplace_value(value);
}
};
template<>
struct task_promise<void> : promise_base<void>
{
void return_void()
{
this->emplace_value();
}
};
template<typename T>
struct promise_deleter
{
void operator()(promise_base<T>* promise) const noexcept
{
promise->abandon();
}
};
template<typename T>
using promise_ptr = wistd::unique_ptr<promise_base<T>, promise_deleter<T>>;
template<typename T>
struct agile_awaiter
{
agile_awaiter(promise_ptr<T>&& initial) : promise(wistd::move(initial)) { }
promise_ptr<T> promise;
bool await_ready()
{
return promise->client_await_ready();
}
auto await_suspend(__WI_COROUTINE_NAMESPACE::coroutine_handle<> handle)
{
// Use the default resumer.
return promise->client_await_suspend(handle.address(), nullptr);
}
T await_resume()
{
return promise->client_await_resume();
}
};
template<typename T>
struct task_base
{
auto resume_any_thread() && noexcept
{
return agile_awaiter<T>{ wistd::move(promise) };
}
// You must #include <ole2.h> before <wil\coroutine.h> to enable apartment-aware awaiting.
auto resume_same_apartment() && noexcept;
// Compiler error message metaprogramming: Tell people that they
// need to use std::move() if they try to co_await an lvalue.
struct cannot_await_lvalue_use_std_move { void await_ready() {} };
cannot_await_lvalue_use_std_move operator co_await() & = delete;
// You must #include <synchapi.h> (usually via <windows.h>) to enable synchronous waiting.
decltype(auto) get() &&;
protected:
task_base(task_promise<T>* initial = nullptr) noexcept : promise(initial) {}
template<typename D>
D& assign(D* self, task_base&& other) noexcept
{
static_cast<task_base&>(*this) = wistd::move(other);
return *self;
}
private:
promise_ptr<T> promise;
static void __stdcall wake_by_address(void* completed);
};
}
namespace wil
{
// Must write out both classes separately
// Cannot use deduction guides with alias template type prior to C++20.
template<typename T>
struct task : details::coro::task_base<T>
{
using base = details::coro::task_base<T>;
// Constructing from task_promise<T>* cannot be explicit because get_return_object relies on implicit conversion.
task(details::coro::task_promise<T>* initial = nullptr) noexcept : base(initial) {}
explicit task(base&& other) noexcept : base(wistd::move(other)) {}
task& operator=(base&& other) noexcept
{
return base::assign(this, wistd::move(other));
}
using base::operator co_await;
auto operator co_await() && noexcept
{
return wistd::move(*this).resume_any_thread();
}
};
template<typename T>
struct com_task : details::coro::task_base<T>
{
using base = details::coro::task_base<T>;
// Constructing from task_promise<T>* cannot be explicit because get_return_object relies on implicit conversion.
com_task(details::coro::task_promise<T>* initial = nullptr) noexcept : base(initial) {}
explicit com_task(base&& other) noexcept : base(wistd::move(other)) {}
com_task& operator=(base&& other) noexcept
{
return base::assign(this, wistd::move(other));
}
using base::operator co_await;
auto operator co_await() && noexcept
{
// You must #include <ole2.h> before <wil\coroutine.h> to enable non-agile awaiting.
return wistd::move(*this).resume_same_apartment();
}
};
template<typename T>
task(com_task<T>&&)->task<T>;
template<typename T>
com_task(task<T>&&)->com_task<T>;
}
template <typename T, typename... Args>
struct __WI_COROUTINE_NAMESPACE::coroutine_traits<wil::task<T>, Args...>
{
using promise_type = wil::details::coro::task_promise<T>;
};
template <typename T, typename... Args>
struct __WI_COROUTINE_NAMESPACE::coroutine_traits<wil::com_task<T>, Args...>
{
using promise_type = wil::details::coro::task_promise<T>;
};
#endif // __WIL_COROUTINE_INCLUDED
// Can re-include this header after including synchapi.h (usually via windows.h) to enable synchronous wait.
#if defined(_SYNCHAPI_H_) && !defined(__WIL_COROUTINE_SYNCHRONOUS_GET_INCLUDED)
#define __WIL_COROUTINE_SYNCHRONOUS_GET_INCLUDED
namespace wil::details::coro
{
template<typename T>
decltype(auto) task_base<T>::get() &&
{
if (!promise->client_await_ready())
{
bool completed = false;
if (promise->client_await_suspend(&completed, wake_by_address))
{
bool pending = false;
while (!completed)
{
WaitOnAddress(&completed, &pending, sizeof(pending), INFINITE);
}
}
}
return std::exchange(promise, {})->client_await_resume();
}
template<typename T>
void __stdcall task_base<T>::wake_by_address(void* completed)
{
*reinterpret_cast<bool*>(completed) = true;
WakeByAddressSingle(completed);
}
}
#endif // __WIL_COROUTINE_SYNCHRONOUS_GET_INCLUDED
// Can re-include this header after including COM header files to enable non-agile tasks.
#if defined(_COMBASEAPI_H_) && defined(_THREADPOOLAPISET_H_) && !defined(__WIL_COROUTINE_NON_AGILE_INCLUDED)
#define __WIL_COROUTINE_NON_AGILE_INCLUDED
#include <ctxtcall.h>
#include <wil/com.h>
namespace wil::details::coro
{
struct apartment_info
{
APTTYPE aptType;
APTTYPEQUALIFIER aptTypeQualifier;
void load()
{
if (FAILED(CoGetApartmentType(&aptType, &aptTypeQualifier)))
{
// If COM is not initialized, then act as if we are running
// on the implicit MTA.
aptType = APTTYPE_MTA;
aptTypeQualifier = APTTYPEQUALIFIER_IMPLICIT_MTA;
}
}
};
// apartment_resumer resumes a coroutine in a captured apartment.
struct apartment_resumer
{
static auto as_self(void* p)
{
return reinterpret_cast<apartment_resumer*>(p);
}
static bool is_sta()
{
apartment_info info;
info.load();
switch (info.aptType)
{
case APTTYPE_STA:
case APTTYPE_MAINSTA:
return true;
case APTTYPE_NA:
return info.aptTypeQualifier == APTTYPEQUALIFIER_NA_ON_STA ||
info.aptTypeQualifier == APTTYPEQUALIFIER_NA_ON_MAINSTA;
default:
return false;
}
}
static wil::com_ptr<IContextCallback> current_context()
{
wil::com_ptr<IContextCallback> context;
// This will fail if COM is not initialized. Treat as implicit MTA.
// Do not use IID_PPV_ARGS to avoid ambiguity between ::IUnknown and winrt::IUnknown.
CoGetObjectContext(__uuidof(IContextCallback), reinterpret_cast<void**>(&context));
return context;
}
__WI_COROUTINE_NAMESPACE::coroutine_handle<> waiter;
wil::com_ptr<IContextCallback> context{ nullptr };
apartment_info info;
HRESULT resume_result = S_OK;
void capture_context(__WI_COROUTINE_NAMESPACE::coroutine_handle<> handle)
{
waiter = handle;
info.load();
context = current_context();
if (context == nullptr)
{
__debugbreak();
}
}
static void __stdcall resume_in_context(void* parameter)
{
auto self = as_self(parameter);
if (self->context == nullptr || self->context == current_context())
{
self->context = nullptr; // removes the context cleanup from the resume path
self->waiter();
}
else if (is_sta())
{
submit_threadpool_callback(resume_context, self);
}
else
{
self->resume_context_sync();
}
}
static void submit_threadpool_callback(PTP_SIMPLE_CALLBACK callback, void* context)
{
THROW_IF_WIN32_BOOL_FALSE(TrySubmitThreadpoolCallback(callback, context, nullptr));
}
static void CALLBACK resume_context(PTP_CALLBACK_INSTANCE /*instance*/, void* parameter)
{
as_self(parameter)->resume_context_sync();
}
void resume_context_sync()
{
ComCallData data{};
data.pUserDefined = this;
// The call to resume_apartment_callback will destruct the context.
// Capture into a local so we don't destruct it while it's in use.
// This also removes the context cleanup from the resume path.
auto local_context = wistd::move(context);
auto result = local_context->ContextCallback(resume_apartment_callback, &data, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr);
if (FAILED(result))
{
// Unable to resume on the correct apartment.
// Resume on the wrong apartment, but tell the coroutine why.
resume_result = result;
waiter();
}
}
static HRESULT CALLBACK resume_apartment_callback(ComCallData* data) noexcept
{
as_self(data->pUserDefined)->waiter();
return S_OK;
}
void check()
{
THROW_IF_FAILED(resume_result);
}
};
// The COM awaiter captures the COM context when the co_await begins.
// When the co_await completes, it uses that COM context to resume execution.
// This follows the same algorithm employed by C++/WinRT, which has features like
// avoiding stack buildup and proper handling of the neutral apartment.
// It does, however, introduce fail-fast code paths if thread pool tasks cannot
// be submitted. (Those fail-fasts could be removed by preallocating the tasks,
// but that means paying an up-front cost for something that may never end up used,
// as well as introducing extra cleanup code in the fast-path.)
template<typename T>
struct com_awaiter : agile_awaiter<T>
{
com_awaiter(promise_ptr<T>&& initial) : agile_awaiter<T>(wistd::move(initial)) { }
apartment_resumer resumer;
auto await_suspend(__WI_COROUTINE_NAMESPACE::coroutine_handle<> handle)
{
resumer.capture_context(handle);
return this->promise->client_await_suspend(wistd::addressof(resumer), apartment_resumer::resume_in_context);
}
decltype(auto) await_resume()
{
resumer.check();
return agile_awaiter<T>::await_resume();
}
};
template<typename T>
auto task_base<T>::resume_same_apartment() && noexcept
{
return com_awaiter<T>{ wistd::move(promise) };
}
}
#endif // __WIL_COROUTINE_NON_AGILE_INCLUDED

View file

@ -0,0 +1,491 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_CPPWINRT_INCLUDED
#define __WIL_CPPWINRT_INCLUDED
#include "common.h"
#include <windows.h>
#include <unknwn.h>
#include <inspectable.h>
#include <hstring.h>
// WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to
// understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to
// C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below -
// into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global
// function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and
// 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined.
/// @cond
namespace wil::details
{
// Since the C++/WinRT version macro is a string...
// For example: "2.0.221104.6"
inline constexpr int version_from_string(const char* versionString)
{
int result = 0;
while ((*versionString >= '0') && (*versionString <= '9'))
{
result = result * 10 + (*versionString - '0');
++versionString;
}
return result;
}
inline constexpr int major_version_from_string(const char* versionString)
{
return version_from_string(versionString);
}
inline constexpr int minor_version_from_string(const char* versionString)
{
int dotCount = 0;
while ((*versionString != '\0'))
{
if (*versionString == '.')
{
++dotCount;
}
++versionString;
if (dotCount == 2)
{
return version_from_string(versionString);
}
}
return 0;
}
}
/// @endcond
#ifdef CPPWINRT_VERSION
// Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of
// 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer
// problematic, so only emit an error when using a version of C++/WinRT prior to 2.0
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
"Please include wil/cppwinrt.h before including any C++/WinRT headers");
#endif
// NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed
#ifdef WINRT_EXTERNAL_CATCH_CLAUSE
#define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1
#else
#define WINRT_EXTERNAL_CATCH_CLAUSE \
catch (const wil::ResultException& e) \
{ \
return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \
}
#endif
#include "result_macros.h"
#include <winrt/base.h>
#if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
"C++/WinRT external catch clause already defined outside of WIL");
#endif
// In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid
// linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to
// use it unless the version of C++/WinRT is high enough
extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept;
// The same is true with this function pointer as well, except that the version must be 2.X or higher.
extern void(__stdcall* winrt_throw_hresult_handler)(uint32_t, char const*, char const*, void*, winrt::hresult const) noexcept;
/// @cond
namespace wil::details
{
inline void MaybeGetExceptionString(
const winrt::hresult_error& exception,
_Out_writes_opt_(debugStringChars) PWSTR debugString,
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars)
{
if (debugString)
{
StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str());
}
}
inline HRESULT __stdcall ResultFromCaughtException_CppWinRt(
_Inout_updates_opt_(debugStringChars) PWSTR debugString,
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars,
_Inout_ bool* isNormalized) noexcept
{
if (g_pfnResultFromCaughtException)
{
try
{
throw;
}
catch (const ResultException& exception)
{
*isNormalized = true;
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.GetErrorCode();
}
catch (const winrt::hresult_error& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.to_abi();
}
catch (const std::bad_alloc& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_OUTOFMEMORY;
}
catch (const std::out_of_range& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_BOUNDS;
}
catch (const std::invalid_argument& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_INVALIDARG;
}
catch (...)
{
auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars);
if (FAILED(hr))
{
return hr;
}
}
}
else
{
try
{
throw;
}
catch (const ResultException& exception)
{
*isNormalized = true;
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.GetErrorCode();
}
catch (const winrt::hresult_error& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.to_abi();
}
catch (const std::bad_alloc& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_OUTOFMEMORY;
}
catch (const std::out_of_range& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_BOUNDS;
}
catch (const std::invalid_argument& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_INVALIDARG;
}
catch (const std::exception& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION);
}
catch (...)
{
// Fall through to returning 'S_OK' below
}
}
// Tell the caller that we were unable to map the exception by succeeding...
return S_OK;
}
}
/// @endcond
namespace wil
{
inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept
{
// C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't
// have accurate file/line/etc. information
return static_cast<std::int32_t>(details::ReportFailure_CaughtException<FailureType::Return>(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress)));
}
inline void __stdcall winrt_throw_hresult(uint32_t lineNumber, char const* fileName, char const* functionName, void* returnAddress, winrt::hresult const result) noexcept
{
void* callerReturnAddress{nullptr}; PCSTR code{nullptr};
wil::details::ReportFailure_Hr<FailureType::Log>(__R_FN_CALL_FULL __R_COMMA result);
}
inline void WilInitialize_CppWinRT()
{
details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt;
if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2)
{
WI_ASSERT(winrt_to_hresult_handler == nullptr);
winrt_to_hresult_handler = winrt_to_hresult;
if constexpr (details::minor_version_from_string(CPPWINRT_VERSION) >= 210122)
{
WI_ASSERT(winrt_throw_hresult_handler == nullptr);
winrt_throw_hresult_handler = winrt_throw_hresult;
}
}
}
/// @cond
namespace details
{
#ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0")
WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, []
{
::wil::WilInitialize_CppWinRT();
return 1;
});
#else
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1")
#endif
}
/// @endcond
// Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type.
inline long verify_hresult(winrt::hresult hr) noexcept
{
return hr;
}
// Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience.
template <typename T>
auto get_abi(T const& object) noexcept
{
return winrt::get_abi(object);
}
inline auto get_abi(winrt::hstring const& object) noexcept
{
return static_cast<HSTRING>(winrt::get_abi(object));
}
inline auto str_raw_ptr(const winrt::hstring& str) noexcept
{
return str.c_str();
}
template <typename T>
auto put_abi(T& object) noexcept
{
return winrt::put_abi(object);
}
inline auto put_abi(winrt::hstring& object) noexcept
{
return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
}
inline ::IUnknown* com_raw_ptr(const winrt::Windows::Foundation::IUnknown& ptr) noexcept
{
return static_cast<::IUnknown*>(winrt::get_abi(ptr));
}
// Needed to power wil::cx_object_from_abi that requires IInspectable
inline ::IInspectable* com_raw_ptr(const winrt::Windows::Foundation::IInspectable& ptr) noexcept
{
return static_cast<::IInspectable*>(winrt::get_abi(ptr));
}
// Taken from the docs.microsoft.com article
template <typename T>
T convert_from_abi(::IUnknown* from)
{
T to{ nullptr }; // `T` is a projected type.
winrt::check_hresult(from->QueryInterface(winrt::guid_of<T>(), winrt::put_abi(to)));
return to;
}
// For obtaining an object from an interop method on the factory. Example:
// winrt::InputPane inputPane = wil::capture_interop<winrt::InputPane>(&IInputPaneInterop::GetForWindow, hwnd);
// If the method produces something different from the factory type:
// winrt::IAsyncAction action = wil::capture_interop<winrt::IAsyncAction, winrt::AccountsSettingsPane>(&IAccountsSettingsPaneInterop::ShowAddAccountForWindow, hwnd);
template<typename WinRTResult, typename WinRTFactory = WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
auto capture_interop(HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args)
{
auto interop = winrt::get_activation_factory<WinRTFactory, Interface>();
return winrt::capture<WinRTResult>(interop, method, std::forward<Args>(args)...);
}
// For obtaining an object from an interop method on an instance. Example:
// winrt::UserActivitySession session = wil::capture_interop<winrt::UserActivitySession>(activity, &IUserActivityInterop::CreateSessionForWindow, hwnd);
template<typename WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
auto capture_interop(winrt::Windows::Foundation::IUnknown const& o, HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args)
{
return winrt::capture<WinRTResult>(o.as<Interface>(), method, std::forward<Args>(args)...);
}
/** Holds a reference to the host C++/WinRT module to prevent it from being unloaded.
Normally, this is done by being in an IAsyncOperation coroutine or by holding a strong
reference to a C++/WinRT object hosted in the same module, but if you have neither,
you will need to hold a reference explicitly. For the WRL equivalent, see wrl_module_reference.
This can be used as a base, which permits EBO:
~~~~
struct NonWinrtObject : wil::winrt_module_reference
{
int value;
};
// DLL will not be unloaded as long as NonWinrtObject is still alive.
auto p = std::make_unique<NonWinrtObject>();
~~~~
Or it can be used as a member (with [[no_unique_address]] to avoid
occupying any memory):
~~~~
struct NonWinrtObject
{
int value;
[[no_unique_address]] wil::winrt_module_reference module_ref;
};
// DLL will not be unloaded as long as NonWinrtObject is still alive.
auto p = std::make_unique<NonWinrtObject>();
~~~~
If using it to prevent the host DLL from unloading while a thread
or threadpool work item is still running, create the object before
starting the thread, and pass it to the thread. This avoids a race
condition where the host DLL could get unloaded before the thread starts.
~~~~
std::thread([module_ref = wil::winrt_module_reference()]() { do_background_work(); });
// Don't do this (race condition)
std::thread([]() { wil::winrt_module_reference module_ref; do_background_work(); }); // WRONG
~~~~
Also useful in coroutines that neither capture DLL-hosted COM objects, nor are themselves
DLL-hosted COM objects. (If the coroutine returns IAsyncAction or captures a get_strong()
of its containing WinRT class, then the IAsyncAction or strong reference will itself keep
a strong reference to the host module.)
~~~~
winrt::fire_and_forget ContinueBackgroundWork()
{
// prevent DLL from unloading while we are running on a background thread.
// Do this before switching to the background thread.
wil::winrt_module_reference module_ref;
co_await winrt::resume_background();
do_background_work();
};
~~~~
*/
struct [[nodiscard]] winrt_module_reference
{
winrt_module_reference()
{
++winrt::get_module_lock();
}
winrt_module_reference(winrt_module_reference const&) : winrt_module_reference() {}
~winrt_module_reference()
{
--winrt::get_module_lock();
}
};
/** Implements a C++/WinRT class where some interfaces are conditionally supported.
~~~~
// Assume the existence of a class "Version2" which says whether
// the IMyThing2 interface should be supported.
struct Version2 { static bool IsEnabled(); };
// Declare implementation class which conditionally supports IMyThing2.
struct MyThing : wil::winrt_conditionally_implements<MyThingT<MyThing>,
Version2, IMyThing2>
{
// implementation goes here
};
~~~~
If `Version2::IsEnabled()` returns `false`, then the `QueryInterface`
for `IMyThing2` will fail.
Any interface not listed as conditional is assumed to be enabled unconditionally.
You can add additional Version / Interface pairs to the template parameter list.
Interfaces may be conditionalized on at most one Version class. If you need a
complex conditional, create a new helper class.
~~~~
// Helper class for testing two Versions.
struct Version2_or_greater {
static bool IsEnabled() { return Version2::IsEnabled() || Version3::IsEnabled(); }
};
// This implementation supports IMyThing2 if either Version2 or Version3 is enabled,
// and supports IMyThing3 only if Version3 is enabled.
struct MyThing : wil::winrt_conditionally_implements<MyThingT<MyThing>,
Version2_or_greater, IMyThing2, Version3, IMyThing3>
{
// implementation goes here
};
~~~~
*/
template<typename Implements, typename... Rest>
struct winrt_conditionally_implements : Implements
{
using Implements::Implements;
void* find_interface(winrt::guid const& iid) const noexcept override
{
static_assert(sizeof...(Rest) % 2 == 0, "Extra template parameters should come in groups of two");
if (is_enabled<0, std::tuple<Rest...>>(iid))
{
return Implements::find_interface(iid);
}
return nullptr;
}
private:
template<std::size_t index, typename Tuple>
static bool is_enabled(winrt::guid const& iid)
{
if constexpr (index >= std::tuple_size_v<Tuple>)
{
return true;
}
else
{
check_no_duplicates<1, index + 1, Tuple>();
return (iid == winrt::guid_of<std::tuple_element_t<index + 1, Tuple>>()) ?
std::tuple_element_t<index, Tuple>::IsEnabled() :
is_enabled<index + 2, Tuple>(iid);
}
}
template<std::size_t index, std::size_t upto, typename Tuple>
static constexpr void check_no_duplicates()
{
if constexpr (index < upto)
{
static_assert(!std::is_same_v<std::tuple_element_t<index, Tuple>, std::tuple_element_t<upto, Tuple>>,
"Duplicate interfaces found in winrt_conditionally_implements");
check_no_duplicates<index + 2, upto, Tuple>();
}
}
};
}
#endif // __WIL_CPPWINRT_INCLUDED

View file

@ -0,0 +1,290 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
namespace wil
{
#ifndef __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED
#define __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED
namespace details
{
template<typename T>
struct single_threaded_property_storage
{
T m_value{};
single_threaded_property_storage() = default;
single_threaded_property_storage(const T& value) : m_value(value) {}
operator T& () { return m_value; }
operator T const& () const { return m_value; }
template<typename Q> auto operator=(Q&& q)
{
m_value = wistd::forward<Q>(q);
return *this;
}
};
}
template <typename T>
struct single_threaded_property : std::conditional_t<std::is_scalar_v<T> || std::is_final_v<T>, wil::details::single_threaded_property_storage<T>, T>
{
single_threaded_property() = default;
template <typename... TArgs> single_threaded_property(TArgs&&... value) : base_type(std::forward<TArgs>(value)...) {}
using base_type = std::conditional_t<std::is_scalar_v<T> || std::is_final_v<T>, wil::details::single_threaded_property_storage<T>, T>;
T operator()() const
{
return *this;
}
// This is the only setter exposed. We don't expose `operator()(Q&& q)`,
// since that is what C++/WinRT uses to implement public setters. Since
// single_threaded_property is intended for readonly properties, we
// don't want to expose that.
//
// To set the value of this property *internally* (within your
// implementation), use this `operator=`:
//
// MyProperty = 42;
// // MyProperty(42); // won't work
//
// For settable properties, use single_threaded_rw_property<T> instead.
template<typename Q> auto& operator=(Q&& q)
{
static_cast<base_type&>(*this) = std::forward<Q>(q);
return *this;
}
};
template <typename T>
struct single_threaded_rw_property : single_threaded_property<T>
{
using base_type = single_threaded_property<T>;
template<typename... TArgs> single_threaded_rw_property(TArgs&&... value) : base_type(std::forward<TArgs>(value)...) {}
using base_type::operator();
// needed in lieu of deducing-this
template<typename Q> auto& operator()(Q&& q)
{
return *this = std::forward<Q>(q);
}
// needed in lieu of deducing-this
template<typename Q> auto& operator=(Q&& q)
{
base_type::operator=(std::forward<Q>(q));
return *this;
}
};
#endif // __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED
#if !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H) // WinRT / XAML helpers
#define __WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION
namespace details
{
template<typename T>
struct event_base {
winrt::event_token operator()(const T& handler)
{
return m_handler.add(handler);
}
auto operator()(const winrt::event_token& token) noexcept
{
return m_handler.remove(token);
}
template<typename... TArgs>
auto invoke(TArgs&&... args)
{
return m_handler(std::forward<TArgs>(args)...);
}
private:
winrt::event<T> m_handler;
};
}
/**
* @brief A default event handler that maps to [Windows.Foundation.EventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.eventhandler-1).
* @tparam T The event data type.
*/
template<typename T>
struct untyped_event : wil::details::event_base<winrt::Windows::Foundation::EventHandler<T>> {};
/**
* @brief A default event handler that maps to [Windows.Foundation.TypedEventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.typedeventhandler-2).
* @tparam T The event data type.
* @details Usage example:
* @code
* // In IDL, this corresponds to:
* // event Windows.Foundation.TypedEventHandler<ModalPage, String> OkClicked;
* wil::typed_event<MarkupSample::ModalPage, winrt::hstring> OkClicked;
* @endcode
*/
template<typename TSender, typename TArgs>
struct typed_event : wil::details::event_base<winrt::Windows::Foundation::TypedEventHandler<TSender, TArgs>> {};
#endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H)
#if !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H)) // INotifyPropertyChanged helpers
#define __WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA
namespace details
{
#ifdef WINRT_Microsoft_UI_Xaml_Data_H
using Xaml_Data_PropertyChangedEventHandler = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler;
using Xaml_Data_PropertyChangedEventArgs = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventArgs;
#elif defined(WINRT_Windows_UI_Xaml_Data_H)
using Xaml_Data_PropertyChangedEventHandler = winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler;
using Xaml_Data_PropertyChangedEventArgs = winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs;
#endif
}
/**
* @brief Helper base class to inherit from to have a simple implementation of [INotifyPropertyChanged](https://docs.microsoft.com/uwp/api/windows.ui.xaml.data.inotifypropertychanged).
* @tparam T CRTP type
* @details When you declare your class, make this class a base class and pass your class as a template parameter:
* @code
* struct MyPage : MyPageT<MyPage>, wil::notify_property_changed_base<MyPage>
* {
* wil::single_threaded_notifying_property<int> MyInt;
* MyPage() : INIT_NOTIFYING_PROPERTY(MyInt, 42) { }
* // or
* WIL_NOTIFYING_PROPERTY(int, MyInt, 42);
* };
* @endcode
*/
template<typename T,
typename Xaml_Data_PropertyChangedEventHandler = wil::details::Xaml_Data_PropertyChangedEventHandler,
typename Xaml_Data_PropertyChangedEventArgs = wil::details::Xaml_Data_PropertyChangedEventArgs>
struct notify_property_changed_base
{
using Type = T;
auto PropertyChanged(Xaml_Data_PropertyChangedEventHandler const& value)
{
return m_propertyChanged.add(value);
}
void PropertyChanged(winrt::event_token const& token)
{
m_propertyChanged.remove(token);
}
Type& self()
{
return *static_cast<Type*>(this);
}
/**
* @brief Raises a property change notification event
* @param name The name of the property
* @return
* @details Usage example\n
* C++
* @code
* void MyPage::DoSomething()
* {
* // modify MyInt
* // MyInt = ...
*
* // now send a notification to update the bound UI elements
* RaisePropertyChanged(L"MyInt");
* }
* @endcode
*/
auto RaisePropertyChanged(std::wstring_view name)
{
return m_propertyChanged(self(), Xaml_Data_PropertyChangedEventArgs{ name });
}
protected:
winrt::event<Xaml_Data_PropertyChangedEventHandler> m_propertyChanged;
};
/**
* @brief Implements a property type with notifications
* @tparam T the property type
* @details Use the #INIT_NOTIFY_PROPERTY macro to initialize this property in your class constructor. This will set up the right property name, and bind it to the `notify_property_changed_base` implementation.
*/
template<typename T,
typename Xaml_Data_PropertyChangedEventHandler = wil::details::Xaml_Data_PropertyChangedEventHandler,
typename Xaml_Data_PropertyChangedEventArgs = wil::details::Xaml_Data_PropertyChangedEventArgs>
struct single_threaded_notifying_property : single_threaded_rw_property<T>
{
using Type = T;
using base_type = single_threaded_rw_property<T>;
using base_type::operator();
template<typename Q> auto& operator()(Q&& q)
{
return *this = std::forward<Q>(q);
}
template<typename Q> auto& operator=(Q&& q)
{
if (q != this->operator()())
{
static_cast<base_type&>(*this) = std::forward<Q>(q);
if (auto strong = m_sender.get(); (m_npc != nullptr) && (strong != nullptr))
{
(*m_npc)(strong, Xaml_Data_PropertyChangedEventArgs{ m_name });
}
}
return *this;
}
template<typename... TArgs>
single_threaded_notifying_property(
winrt::event<Xaml_Data_PropertyChangedEventHandler>* npc,
const winrt::Windows::Foundation::IInspectable& sender,
std::wstring_view name,
TArgs&&... args) :
single_threaded_rw_property<T>(std::forward<TArgs...>(args)...),
m_name(name),
m_npc(npc),
m_sender(sender)
{}
single_threaded_notifying_property(const single_threaded_notifying_property&) = default;
single_threaded_notifying_property(single_threaded_notifying_property&&) = default;
std::wstring_view Name() const noexcept { return m_name; }
private:
std::wstring_view m_name;
winrt::event<Xaml_Data_PropertyChangedEventHandler>* m_npc;
winrt::weak_ref<winrt::Windows::Foundation::IInspectable> m_sender;
};
/**
* @def WIL_NOTIFYING_PROPERTY
* @brief use this to stamp out a property that calls RaisePropertyChanged upon changing its value. This is a zero-storage alternative to wil::single_threaded_notifying_property<T>
* @details You can pass an initializer list for the initial property value in the variadic arguments to this macro.
*/
#define WIL_NOTIFYING_PROPERTY(type, name, ...) \
type m_##name{__VA_ARGS__}; \
auto name() const noexcept { return m_##name; } \
auto& name(type value) \
{ \
if (m_##name != value) \
{ \
m_##name = std::move(value); \
RaisePropertyChanged(L"" #name); \
} \
return *this; \
} \
/**
* @def INIT_NOTIFYING_PROPERTY
* @brief use this to initialize a wil::single_threaded_notifying_property in your class constructor.
*/
#define INIT_NOTIFYING_PROPERTY(NAME, VALUE) \
NAME(&m_propertyChanged, *this, L"" #NAME, VALUE)
#endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H))
} // namespace wil

View file

@ -0,0 +1,352 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_CPPWINRT_HELPERS_DEFINED
#define __WIL_CPPWINRT_HELPERS_DEFINED
/// @cond
namespace wil::details
{
struct dispatcher_RunAsync
{
template<typename Dispatcher, typename... Args>
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
{
dispatcher.RunAsync(std::forward<Args>(args)...);
}
};
struct dispatcher_TryEnqueue
{
template<typename Dispatcher, typename... Args>
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
{
dispatcher.TryEnqueue(std::forward<Args>(args)...);
}
};
template<typename Dispatcher> struct dispatcher_traits;
}
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
#include <experimental/coroutine>
namespace wil::details
{
template<typename T = void> using coroutine_handle = std::experimental::coroutine_handle<T>;
}
#elif defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)
#include <coroutine>
namespace wil::details
{
template<typename T = void> using coroutine_handle = std::coroutine_handle<T>;
}
#endif
/// @endcond
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) || (defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L))
/// @cond
namespace wil::details
{
struct dispatched_handler_state
{
details::coroutine_handle<> handle{};
bool orphaned = false;
};
struct dispatcher_handler
{
dispatcher_handler(dispatched_handler_state* state) : m_state(state) { }
dispatcher_handler(dispatcher_handler&& other) noexcept : m_state(std::exchange(other.m_state, {})) {}
~dispatcher_handler()
{
if (m_state && m_state->handle)
{
m_state->orphaned = true;
Complete();
}
}
void operator()()
{
Complete();
}
void Complete()
{
auto state = std::exchange(m_state, nullptr);
std::exchange(state->handle, {}).resume();
}
dispatched_handler_state* m_state;
};
}
/// @endcond
namespace wil
{
//! Resumes coroutine execution on the thread associated with the dispatcher, or throws
//! an exception (from an arbitrary thread) if unable. Supported dispatchers are
//! Windows.System.DispatcherQueue, Microsoft.System.DispatcherQueue,
//! Microsoft.UI.Dispatching.DispatcherQueue, and Windows.UI.Core.CoreDispatcher,
//! but you must include the corresponding <winrt/Namespace.h> header before including
//! wil\cppwinrt_helpers.h. It is okay to include wil\cppwinrt_helpers.h multiple times:
//! support will be enabled for any winrt/Namespace.h headers that were included since
//! the previous inclusion of wil\cppwinrt_headers.h.
template<typename Dispatcher>
[[nodiscard]] auto resume_foreground(Dispatcher const& dispatcher,
typename details::dispatcher_traits<Dispatcher>::Priority priority = details::dispatcher_traits<Dispatcher>::Priority::Normal)
{
using Traits = details::dispatcher_traits<Dispatcher>;
using Priority = typename Traits::Priority;
using Handler = typename Traits::Handler;
struct awaitable
{
awaitable(Dispatcher const& dispatcher, Priority priority) noexcept :
m_dispatcher(dispatcher),
m_priority(priority)
{
}
bool await_ready() const noexcept { return false; }
void await_suspend(details::coroutine_handle<> handle)
{
m_state.handle = handle;
Handler handler{ details::dispatcher_handler(&m_state) };
try
{
// The return value of Schedule is not reliable. Use the dispatcher_handler destructor
// to detect whether the work item failed to run.
Traits::Scheduler::Schedule(m_dispatcher, m_priority, handler);
}
catch (...)
{
m_state.handle = nullptr; // the exception will resume the coroutine, so the handler shouldn't do it
throw;
}
}
void await_resume() const
{
if (m_state.orphaned)
{
throw winrt::hresult_error(static_cast<winrt::hresult>(0x800701ab)); // HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE)
}
}
private:
Dispatcher const& m_dispatcher;
Priority const m_priority;
details::dispatched_handler_state m_state;
};
return awaitable{ dispatcher, priority };
}
}
#endif // Coroutines are supported
#endif // __WIL_CPPWINRT_HELPERS_DEFINED
/// @cond
#if defined(WINRT_Windows_UI_Core_H) && !defined(__WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS
namespace wil::details
{
template<>
struct dispatcher_traits<winrt::Windows::UI::Core::CoreDispatcher>
{
using Priority = winrt::Windows::UI::Core::CoreDispatcherPriority;
using Handler = winrt::Windows::UI::Core::DispatchedHandler;
using Scheduler = dispatcher_RunAsync;
};
}
#endif // __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS
#if defined(WINRT_Windows_System_H) && !defined(__WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS
namespace wil::details
{
template<>
struct dispatcher_traits<winrt::Windows::System::DispatcherQueue>
{
using Priority = winrt::Windows::System::DispatcherQueuePriority;
using Handler = winrt::Windows::System::DispatcherQueueHandler;
using Scheduler = dispatcher_TryEnqueue;
};
}
#endif // __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS
#if defined(WINRT_Microsoft_System_H) && !defined(__WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS)
#define __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS
namespace wil::details
{
template<>
struct dispatcher_traits<winrt::Microsoft::System::DispatcherQueue>
{
using Priority = winrt::Microsoft::System::DispatcherQueuePriority;
using Handler = winrt::Microsoft::System::DispatcherQueueHandler;
using Scheduler = dispatcher_TryEnqueue;
};
}
#endif // __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS
#if defined(WINRT_Microsoft_UI_Dispatching_H) && !defined(__WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS)
#define __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
namespace wil::details
{
template<>
struct dispatcher_traits<winrt::Microsoft::UI::Dispatching::DispatcherQueue>
{
using Priority = winrt::Microsoft::UI::Dispatching::DispatcherQueuePriority;
using Handler = winrt::Microsoft::UI::Dispatching::DispatcherQueueHandler;
using Scheduler = dispatcher_TryEnqueue;
};
}
#endif // __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
/// @endcond
#if defined(WINRT_Windows_Foundation_Collections_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS
namespace wil
{
/// @cond
namespace details
{
template<typename T> struct is_winrt_vector_like {
private:
template <typename U,
typename = decltype(std::declval<U>().GetMany(std::declval<U>().Size(),
winrt::array_view<decltype(std::declval<U>().GetAt(0))>{}))>
static constexpr bool get_value(int) { return true; }
template <typename> static constexpr bool get_value(...) { return false; }
public:
static constexpr bool value = get_value<T>(0);
};
template<typename T> struct is_winrt_iterator_like {
private:
template <typename U,
typename = decltype(std::declval<U>().GetMany(winrt::array_view<decltype(std::declval<U>().Current())>{}))>
static constexpr bool get_value(int) { return true; }
template <typename> static constexpr bool get_value(...) { return false; }
public:
static constexpr bool value = get_value<T>(0);
};
template<typename T> constexpr T empty() noexcept
{
if constexpr (std::is_base_of_v<winrt::Windows::Foundation::IUnknown, T>)
{
return nullptr;
}
else
{
return {};
}
}
}
/// @endcond
/** Converts C++ / WinRT vectors, iterators, and iterables to std::vector by requesting the
collection's data in bulk. This can be more efficient in terms of IPC cost than iteratively
processing the collection.
~~~
winrt::IVector<winrt::hstring> collection = GetCollection();
std::vector<winrt::hstring> allData = wil::to_vector(collection); // read all data from collection
for (winrt::hstring const& item : allData)
{
// use item
}
~~~
Can be used for IVector<T>, IVectorView<T>, IIterable<T>, IIterator<T>, and any type or
interface that C++/WinRT projects those interfaces for (PropertySet, IMap<T,K>, etc.)
Iterable-only types fetch content in units of 64. When used with an iterator, the returned
vector contains the iterator's current position and any others after it.
*/
template<typename TSrc> auto to_vector(TSrc const& src)
{
if constexpr (details::is_winrt_vector_like<TSrc>::value)
{
using T = decltype(src.GetAt(0));
std::vector<T> result;
if (auto expected = src.Size())
{
result.resize(expected + 1, details::empty<T>());
auto actual = src.GetMany(0, result);
if (actual > expected)
{
throw winrt::hresult_changed_state();
}
result.resize(actual, details::empty<T>());
}
return result;
}
else if constexpr (details::is_winrt_iterator_like<TSrc>::value)
{
using T = decltype(src.Current());
std::vector<T> result;
constexpr uint32_t chunkSize = 64;
while (true)
{
auto const lastSize = result.size();
result.resize(lastSize + chunkSize, details::empty<T>());
auto fetched = src.GetMany({result.data() + lastSize, result.data() + lastSize + chunkSize });
if (fetched < chunkSize)
{
result.resize(lastSize + fetched, details::empty<T>());
break;
}
}
return result;
}
else
{
return to_vector(src.First());
}
}
}
#endif
#if defined(WINRT_Windows_UI_H) && defined(_WINDOWS_UI_INTEROP_H_) && !defined(__WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS
#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX)
#pragma push_macro("ABI")
#undef ABI
#define ABI
#endif
namespace wil
{
#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)
//! The following methods require that you include both <winrt/Windows.UI.h>
//! <Windows.UI.Interop.h> before including wil/cppwinrt_helpers.h, and that NTDDI_VERSION
//! is at least NTDDI_WIN10_CU. It is okay to include wil\cppwinrt_helpers.h multiple times:
//! support will be enabled for any headers that were included since the previous inclusion
//! of wil\cppwinrt_headers.h.
inline winrt::Windows::UI::WindowId GetWindowIdFromWindow(HWND hwnd)
{
ABI::Windows::UI::WindowId abiWindowId;
winrt::check_hresult(::GetWindowIdFromWindow(hwnd, &abiWindowId));
return winrt::Windows::UI::WindowId{ abiWindowId.Value };
}
inline HWND GetWindowFromWindowId(winrt::Windows::UI::WindowId windowId)
{
HWND hwnd;
winrt::check_hresult(::GetWindowFromWindowId({ windowId.Value }, &hwnd));
return hwnd;
}
#endif /*defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)*/
}
#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX)
#pragma pop_macro("ABI")
#endif
#endif // __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS

View file

@ -0,0 +1,74 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_CPPWINRT_WRL_INCLUDED
#define __WIL_CPPWINRT_WRL_INCLUDED
#include "cppwinrt.h"
#include <winrt\base.h>
#include "result_macros.h"
#include <wrl\module.h>
// wil::wrl_factory_for_winrt_com_class provides interopability between a
// C++/WinRT class and the WRL Module system, allowing the winrt class to be
// CoCreatable.
//
// Usage:
// - In your cpp, add:
// CoCreatableCppWinRtClass(className)
//
// - In the dll.cpp (or equivalent) for the module containing your class, add:
// CoCreatableClassWrlCreatorMapInclude(className)
//
namespace wil
{
namespace details
{
template <typename TCppWinRTClass>
class module_count_wrapper : public TCppWinRTClass
{
public:
module_count_wrapper()
{
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
{
modulePtr->IncrementObjectCount();
}
}
virtual ~module_count_wrapper()
{
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
{
modulePtr->DecrementObjectCount();
}
}
};
}
template <typename TCppWinRTClass>
class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<>
{
public:
IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void **object) noexcept try
{
*object = nullptr;
RETURN_HR_IF(CLASS_E_NOAGGREGATION, unknownOuter != nullptr);
return winrt::make<details::module_count_wrapper<TCppWinRTClass>>().as(riid, object);
}
CATCH_RETURN()
};
}
#define CoCreatableCppWinRtClass(className) CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class<className>)
#endif // __WIL_CPPWINRT_WRL_INCLUDED

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,168 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_NT_RESULTMACROS_INCLUDED
#define __WIL_NT_RESULTMACROS_INCLUDED
#include "result_macros.h"
// Helpers for return macros
#define __NT_RETURN_NTSTATUS(status, str) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return __status; } __WI_SUPPRESS_4127_E while ((void)0, 0)
#define __NT_RETURN_NTSTATUS_MSG(status, str, fmt, ...) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)); } return __status; } __WI_SUPPRESS_4127_E while ((void)0, 0)
//*****************************************************************************
// Macros for returning failures as NTSTATUS
//*****************************************************************************
// Always returns a known result (NTSTATUS) - always logs failures
#define NT_RETURN_NTSTATUS(status) __NT_RETURN_NTSTATUS(wil::verify_ntstatus(status), #status)
// Always returns a known failure (NTSTATUS) - always logs a var-arg message on failure
#define NT_RETURN_NTSTATUS_MSG(status, fmt, ...) __NT_RETURN_NTSTATUS_MSG(wil::verify_ntstatus(status), #status, fmt, ##__VA_ARGS__)
// Conditionally returns failures (NTSTATUS) - always logs failures
#define NT_RETURN_IF_NTSTATUS_FAILED(status) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS(__statusRet, #status); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
// Conditionally returns failures (NTSTATUS) - always logs a var-arg message on failure
#define NT_RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS_MSG(__statusRet, #status, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0)
//*****************************************************************************
// Macros to catch and convert exceptions on failure
//*****************************************************************************
// Use these macros *within* a catch (...) block to handle exceptions
#define NT_RETURN_CAUGHT_EXCEPTION() return __R_FN(Nt_Return_CaughtException)(__R_INFO_ONLY(nullptr))
#define NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ...) return __R_FN(Nt_Return_CaughtExceptionMsg)(__R_INFO(nullptr) __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__))
// Use these macros in place of a catch block to handle exceptions
#define NT_CATCH_RETURN() catch (...) { NT_RETURN_CAUGHT_EXCEPTION(); }
#define NT_CATCH_RETURN_MSG(fmt, ...) catch (...) { NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); }
namespace wil
{
//*****************************************************************************
// Public Helpers that catch -- mostly only enabled when exceptions are enabled
//*****************************************************************************
// StatusFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally
// it re-throws and catches the exception to convert it to an NTSTATUS. If an exception is of an unrecognized type
// the function will fail fast.
//
// try
// {
// // Code
// }
// catch (...)
// {
// status = wil::StatusFromCaughtException();
// }
_Always_(_Post_satisfies_(return < 0))
__declspec(noinline) inline NTSTATUS StatusFromCaughtException() WI_NOEXCEPT
{
bool isNormalized = false;
NTSTATUS status = STATUS_SUCCESS;
if (details::g_pfnResultFromCaughtExceptionInternal)
{
status = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized).status;
}
if (FAILED_NTSTATUS(status))
{
return status;
}
// Caller bug: an unknown exception was thrown
__WIL_PRIVATE_FAIL_FAST_HR_IF(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), g_fResultFailFastUnknownExceptions);
return wil::details::HrToNtStatus(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
}
namespace details
{
template<FailureType>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default);
template<FailureType>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList);
namespace __R_NS_NAME
{
#ifdef WIL_ENABLE_EXCEPTIONS
__R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
{
__R_FN_LOCALS;
return wil::details::ReportStatus_CaughtException<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY);
}
__R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT
{
va_list argList;
va_start(argList, formatString);
__R_FN_LOCALS;
return wil::details::ReportStatus_CaughtExceptionMsg<FailureType::Return>(__R_DIRECT_FN_CALL formatString, argList);
}
#endif
}
template<FailureType T>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported)
{
wchar_t message[2048];
message[0] = L'\0';
return ReportFailure_CaughtExceptionCommon<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status;
}
template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::FailFast>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
{
wchar_t message[2048];
message[0] = L'\0';
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::FailFast>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
}
template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::Exception>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
{
wchar_t message[2048];
message[0] = L'\0';
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::Exception>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
}
template<FailureType T>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
{
// Pre-populate the buffer with our message, the exception message will be added to it...
wchar_t message[2048];
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
return ReportFailure_CaughtExceptionCommon<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status;
}
template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::FailFast>(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
{
// Pre-populate the buffer with our message, the exception message will be added to it...
wchar_t message[2048];
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::FailFast>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status);
}
template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::Exception>(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
{
// Pre-populate the buffer with our message, the exception message will be added to it...
wchar_t message[2048];
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::Exception>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status);
}
}
}
#endif // __WIL_NT_RESULTMACROS_INCLUDED

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,126 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
// Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating
// a new error we will observe whether there is already an error payload associated with the current thread. If there is, and the HRESULTs match,
// then a new error will not be originated. Otherwise we will overwrite it with a new origination. The ABI boundary for WinRT APIs will check the
// per-thread error information. The act of checking the error clears it, so there should be minimal risk of failing to originate distinct errors
// simply because the HRESULTs match.
//
// For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if the exception is
// caught and re-thrown.
//
// For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because error conditions
// -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is originating the error because it must
// capture the entire stack and some additional data.
#ifndef __WIL_RESULT_ORIGINATE_INCLUDED
#define __WIL_RESULT_ORIGINATE_INCLUDED
#include "result.h"
#include <OleAuto.h> // RestrictedErrorInfo uses BSTRs :(
#include <winstring.h>
#include "resource.h"
#include "com.h"
#include <roerrorapi.h>
namespace wil
{
namespace details
{
// Note: The name must begin with "Raise" so that the !analyze auto-bucketing will ignore this stack frame. Otherwise this line of code gets all the blame.
inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT
{
if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception))
{
bool shouldOriginate = true;
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
{
// This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we are
// observing right now.
wil::unique_bstr descriptionUnused;
HRESULT existingHr = failure.hr;
wil::unique_bstr restrictedDescriptionUnused;
wil::unique_bstr capabilitySidUnused;
if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)))
{
shouldOriginate = (failure.hr != existingHr);
}
}
if (shouldOriginate)
{
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
wil::unique_hmodule errorModule;
if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule))
{
auto pfn = reinterpret_cast<decltype(&::RoOriginateErrorW)>(GetProcAddress(errorModule.get(), "RoOriginateErrorW"));
if (pfn != nullptr)
{
pfn(failure.hr, 0, failure.pszMessage);
}
}
#else // DESKTOP | SYSTEM
::RoOriginateErrorW(failure.hr, 0, failure.pszMessage);
#endif // DESKTOP | SYSTEM
}
else if (restrictedErrorInformation)
{
// GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was already present,
// then we need to restore the error information for later observation.
SetRestrictedErrorInfo(restrictedErrorInformation.get());
}
}
}
// This method will check for the presence of stowed exception data on the current thread. If such data exists, and the HRESULT
// matches the current failure, then we will call RoFailFastWithErrorContext. RoFailFastWithErrorContext in this situation will
// result in -VASTLY- improved crash bucketing. It is hard to express just how much better. In other cases we just return and
// the calling method fails fast the same way it always has.
inline void __stdcall FailfastWithContextCallback(wil::FailureInfo const& failure) WI_NOEXCEPT
{
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
{
wil::unique_bstr descriptionUnused;
HRESULT existingHr = failure.hr;
wil::unique_bstr restrictedDescriptionUnused;
wil::unique_bstr capabilitySidUnused;
if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)) &&
(existingHr == failure.hr))
{
// GetRestrictedErrorInfo returns ownership of the error information. We want it to be available for RoFailFastWithErrorContext
// so we must restore it via SetRestrictedErrorInfo first.
SetRestrictedErrorInfo(restrictedErrorInformation.get());
RoFailFastWithErrorContext(existingHr);
}
else
{
// The error didn't match the current failure. Put it back in thread-local storage even though we aren't failing fast
// in this method, so it is available in the debugger just-in-case.
SetRestrictedErrorInfo(restrictedErrorInformation.get());
}
}
}
} // namespace details
} // namespace wil
// Automatically call RoOriginateError upon error origination by including this file
WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, []
{
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback);
return 1;
})
#endif // __WIL_RESULT_ORIGINATE_INCLUDED

View file

@ -0,0 +1,206 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_RPC_HELPERS_INCLUDED
#define __WIL_RPC_HELPERS_INCLUDED
#include "result.h"
#include "resource.h"
#include "wistd_functional.h"
#include "wistd_type_traits.h"
namespace wil
{
/// @cond
namespace details
{
// This call-adapter template converts a void-returning 'wistd::invoke' into
// an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated
// with 'if constexpr' when C++17 is in wide use.
template<typename TReturnType> struct call_adapter
{
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
{
return wistd::invoke(wistd::forward<TArgs>(args)...);
}
};
template<> struct call_adapter<void>
{
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
{
wistd::invoke(wistd::forward<TArgs>(args)...);
return S_OK;
}
};
// Some RPC exceptions are already HRESULTs. Others are in the regular Win32
// error space. If the incoming exception code isn't an HRESULT, wrap it.
constexpr HRESULT map_rpc_exception(DWORD code)
{
return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code);
}
}
/// @endcond
/** Invokes an RPC method, mapping structured exceptions to HRESULTs
Failures encountered by the RPC infrastructure (such as server crashes, authentication
errors, client parameter issues, etc.) are emitted by raising a structured exception from
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
flow control machinery to use.
Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates
the result of the _work_. HRESULTs returned by a successful completion of the _call_ are
returned as-is.
RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_
completes successfully.
For example, consider an RPC interface method defined in idl as:
~~~
HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state);
~~~
To call this method, use:
~~~
wil::unique_rpc_binding binding = // typically gotten elsewhere;
wil::unique_midl_ptr<KittenState> state;
HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put());
RETURN_IF_FAILED(hr);
~~~
*/
template<typename... TCall> HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT
{
RpcTryExcept
{
// Note: this helper type can be removed with C++17 enabled via
// 'if constexpr(wistd::is_same_v<void, result_t>)'
using result_t = typename wistd::__invoke_of<TCall...>::type;
RETURN_IF_FAILED(details::call_adapter<result_t>::call(wistd::forward<TCall>(args)...));
return S_OK;
}
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
}
RpcEndExcept
}
/** Invokes an RPC method, mapping structured exceptions to HRESULTs
Failures encountered by the RPC infrastructure (such as server crashes, authentication
errors, client parameter issues, etc.) are emitted by raising a structured exception from
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
flow control machinery to use.
Some RPC methods return results (such as a state enumeration or other value) directly in
their signature. This adapter writes that result into a caller-provided object then
returns S_OK.
For example, consider an RPC interface method defined in idl as:
~~~
GUID GetKittenId([in, ref, string] const wchar_t* name);
~~~
To call this method, use:
~~~
wil::unique_rpc_binding binding = // typically gotten elsewhere;
GUID id;
HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy");
RETURN_IF_FAILED(hr);
~~~
*/
template<typename TResult, typename... TCall> HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT
{
RpcTryExcept
{
result = wistd::invoke(wistd::forward<TCall>(args)...);
return S_OK;
}
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
}
RpcEndExcept
}
namespace details
{
// Provides an adapter around calling the context-handle-close method on an
// RPC interface, which itself is an RPC call.
template<typename TStorage, typename close_fn_t, close_fn_t close_fn>
struct rpc_closer_t
{
static void Close(TStorage arg) WI_NOEXCEPT
{
LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg));
}
};
}
/** Manages explicit RPC context handles
Explicit RPC context handles are used in many RPC interfaces. Most interfaces with
context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets
the server close out the context handle. As the close method itself is an RPC call,
it can fail and raise a structured exception.
This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow`
helper, ensuring correct cleanup and lifecycle management.
~~~
// Assume the interface has two methods:
// HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*);
// HRESULT UseFoo([in] FOO_CONTEXT context;
// void CloseFoo([in, out] PFOO_CONTEXT);
using unique_foo_context = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseFoo), CloseFoo>;
unique_foo_context context;
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put()));
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get()));
context.reset();
~~~
*/
template<typename TContext, typename close_fn_t, close_fn_t close_fn>
using unique_rpc_context_handle = unique_any<TContext, decltype(&details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close), details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close>;
#ifdef WIL_ENABLE_EXCEPTIONS
/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_
and those returned by the _method_ are mapped to HRESULTs and thrown inside a
wil::ResultException. Using the example RPC method provided above:
~~~
wil::unique_midl_ptr<KittenState> state;
wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put());
// use 'state'
~~~
*/
template<typename... TCall> void invoke_rpc(TCall&& ... args)
{
THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward<TCall>(args)...));
}
/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the
_call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the
example RPC method provided above:
~~~
GUID id = wil::invoke_rpc_result(GetKittenId, binding.get());
// use 'id'
~~~
*/
template<typename... TCall> auto invoke_rpc_result(TCall&& ... args)
{
using result_t = typename wistd::__invoke_of<TCall...>::type;
result_t result{};
THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward<TCall>(args)...));
return result;
}
#endif
}
#endif

View file

@ -0,0 +1,369 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_SAFECAST_INCLUDED
#define __WIL_SAFECAST_INCLUDED
#include "result_macros.h"
#include <intsafe.h>
#include "wistd_config.h"
#include "wistd_type_traits.h"
namespace wil
{
namespace details
{
// Default error case for undefined conversions in intsafe.h
template<typename OldT, typename NewT> constexpr wistd::nullptr_t intsafe_conversion = nullptr;
// is_known_safe_static_cast_v determines if a conversion is known to be safe or not. Known
// safe conversions can be handled by static_cast, this includes conversions between the same
// type, when the new type is larger than the old type but is not a signed to unsigned
// conversion, and when the two types are the same size and signed/unsigned. All other
// conversions will be assumed to be potentially unsafe, and the conversion must be handled
// by intsafe and checked.
template <typename NewT, typename OldT>
constexpr bool is_known_safe_static_cast_v =
(sizeof(NewT) > sizeof(OldT) && !(wistd::is_signed_v<OldT> && wistd::is_unsigned_v<NewT>)) ||
(sizeof(NewT) == sizeof(OldT) && ((wistd::is_signed_v<NewT> && wistd::is_signed_v<OldT>) || (wistd::is_unsigned_v<NewT> && wistd::is_unsigned_v<OldT>)));
// Helper template to determine that NewT and OldT are both integral types. The safe_cast
// operation only supports conversions between integral types.
template <typename NewT, typename OldT>
constexpr bool both_integral_v = wistd::is_integral<NewT>::value && wistd::is_integral<OldT>::value;
// Note on native wchar_t (__wchar_t):
// Intsafe.h does not currently handle native wchar_t. When compiling with /Zc:wchar_t-, this is fine as wchar_t is
// typedef'd to unsigned short. However, when compiling with /Zc:wchar_t or wchar_t as a native type, the lack of
// support for native wchar_t in intsafe.h becomes an issue. To work around this, we treat native wchar_t as an
// unsigned short when passing it to intsafe.h, because the two on the Windows platform are the same size and
// share the same range according to MSDN. If the cast is to a native wchar_t, the result from intsafe.h is cast
// to a native wchar_t.
// Intsafe does not have a defined conversion for native wchar_t
template <typename NewT, typename OldT>
constexpr bool neither_native_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
// Check to see if the cast is a conversion to native wchar_t
template <typename NewT, typename OldT>
constexpr bool is_cast_to_wchar_v = wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
// Check to see if the cast is a conversion from native wchar_t
template <typename NewT, typename OldT>
constexpr bool is_cast_from_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && wistd::is_same<OldT, __wchar_t>::value;
// Validate the conversion to be performed has a defined mapping to an intsafe conversion
template <typename NewT, typename OldT>
constexpr bool is_supported_intsafe_cast_v = intsafe_conversion<OldT, NewT> != nullptr;
// True when the conversion is between integral types and can be handled by static_cast
template <typename NewT, typename OldT>
constexpr bool is_supported_safe_static_cast_v = both_integral_v<NewT, OldT> && is_known_safe_static_cast_v<NewT, OldT>;
// True when the conversion is between integral types, does not involve native wchar, has
// a mapped intsafe conversion, and is unsafe.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_no_wchar_v =
both_integral_v<NewT, OldT> &&
!is_known_safe_static_cast_v<NewT, OldT> &&
neither_native_wchar_v<NewT, OldT> &&
is_supported_intsafe_cast_v<NewT, OldT>;
// True when the conversion is between integral types, is a cast to native wchar_t, has
// a mapped intsafe conversion, and is unsafe.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_to_wchar_v =
both_integral_v<NewT, OldT> &&
!is_known_safe_static_cast_v<NewT, OldT> &&
is_cast_to_wchar_v<NewT, OldT> &&
is_supported_intsafe_cast_v<unsigned short, OldT>;
// True when the conversion is between integral types, is a cast from native wchar_t, has
// a mapped intsafe conversion, and is unsafe.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_from_wchar_v =
both_integral_v<NewT, OldT> &&
!is_known_safe_static_cast_v<NewT, OldT> &&
is_cast_from_wchar_v<NewT, OldT> &&
is_supported_intsafe_cast_v<NewT, unsigned short>;
// True when the conversion is supported and unsafe, and may or may not involve
// native wchar_t.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_v =
is_supported_unsafe_cast_no_wchar_v<NewT, OldT> ||
is_supported_unsafe_cast_to_wchar_v<NewT, OldT> ||
is_supported_unsafe_cast_from_wchar_v<NewT, OldT>;
// True when T is any one of the primitive types that the variably sized types are defined as.
template <typename T>
constexpr bool is_potentially_variably_sized_type_v =
wistd::is_same<T, int>::value ||
wistd::is_same<T, unsigned int>::value ||
wistd::is_same<T, long>::value ||
wistd::is_same<T, unsigned long>::value ||
wistd::is_same<T, __int64>::value ||
wistd::is_same<T, unsigned __int64>::value;
// True when either type is potentialy variably sized (e.g. size_t, ptrdiff_t)
template <typename OldT, typename NewT>
constexpr bool is_potentially_variably_sized_cast_v =
is_potentially_variably_sized_type_v<OldT> ||
is_potentially_variably_sized_type_v<NewT>;
// Mappings of all conversions defined in intsafe.h to intsafe_conversion
// Note: Uppercase types (UINT, DWORD, SIZE_T, etc) and architecture dependent types resolve
// to the base types. The base types are used since they do not vary based on architecture.
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, char> = LongLongToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, int> = LongLongToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, long> = LongLongToLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, short> = LongLongToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, char> = IntToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, short> = IntToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, char> = LongToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, int> = LongToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, short> = LongToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, char> = ShortToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, signed char> = UShortToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, unsigned char> = UShortToUChar;
}
// Unsafe conversion where failure results in fail fast.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
NewT newVar;
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
return newVar;
}
// Unsafe conversion where failure results in fail fast.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
NewT newVar;
FAIL_FAST_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
return newVar;
}
// Unsafe conversion where failure results in fail fast.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
unsigned short newVar;
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
return static_cast<__wchar_t>(newVar);
}
// This conversion is always safe, therefore a static_cast is fine.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
return static_cast<NewT>(var);
}
#ifdef WIL_ENABLE_EXCEPTIONS
// Unsafe conversion where failure results in a thrown exception.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
NewT newVar;
THROW_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
return newVar;
}
// Unsafe conversion where failure results in a thrown exception.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
NewT newVar;
THROW_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
return newVar;
}
// Unsafe conversion where failure results in a thrown exception.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
unsigned short newVar;
THROW_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
return static_cast<__wchar_t>(newVar);
}
// This conversion is always safe, therefore a static_cast is fine.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
return static_cast<NewT>(var);
}
#endif
// This conversion is unsafe, therefore the two parameter version of safe_cast_nothrow must be used
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast_nothrow(const OldT /*var*/)
{
static_assert(!wistd::is_same_v<NewT, NewT>, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead");
}
// This conversion is always safe, therefore a static_cast is fine.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast_nothrow(const OldT var)
{
return static_cast<NewT>(var);
}
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
return details::intsafe_conversion<OldT, NewT>(var, newTResult);
}
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
return details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), newTResult);
}
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
return details::intsafe_conversion<OldT, unsigned short>(var, reinterpret_cast<unsigned short *>(newTResult));
}
// This conversion is always safe, therefore a static_cast is fine. If it can be determined the conversion
// does not involve a variably sized type, then the compilation will fail and say the single parameter version
// of safe_cast_nothrow should be used instead.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
static_assert(details::is_potentially_variably_sized_cast_v<OldT, NewT>, "This cast is always safe; use safe_cast_nothrow<T>(value) to avoid unnecessary error handling.");
*newTResult = static_cast<NewT>(var);
return S_OK;
}
}
#endif // __WIL_SAFECAST_INCLUDED

View file

@ -0,0 +1,196 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_STL_INCLUDED
#define __WIL_STL_INCLUDED
#include "common.h"
#include "resource.h"
#include <memory>
#include <string>
#include <vector>
#include <utility>
#if _HAS_CXX17
#include <string_view>
#endif
#ifndef WI_STL_FAIL_FAST_IF
#define WI_STL_FAIL_FAST_IF FAIL_FAST_IF
#endif
#if defined(WIL_ENABLE_EXCEPTIONS)
namespace wil
{
/** Secure allocator for STL containers.
The `wil::secure_allocator` allocator calls `SecureZeroMemory` before deallocating
memory. This provides a mechanism for secure STL containers such as `wil::secure_vector`,
`wil::secure_string`, and `wil::secure_wstring`. */
template <typename T>
struct secure_allocator
: public std::allocator<T>
{
template<typename Other>
struct rebind
{
using other = secure_allocator<Other>;
};
secure_allocator()
: std::allocator<T>()
{
}
~secure_allocator() = default;
secure_allocator(const secure_allocator& a)
: std::allocator<T>(a)
{
}
template <class U>
secure_allocator(const secure_allocator<U>& a)
: std::allocator<T>(a)
{
}
T* allocate(size_t n)
{
return std::allocator<T>::allocate(n);
}
void deallocate(T* p, size_t n)
{
SecureZeroMemory(p, sizeof(T) * n);
std::allocator<T>::deallocate(p, n);
}
};
//! `wil::secure_vector` will be securely zeroed before deallocation.
template <typename Type>
using secure_vector = std::vector<Type, secure_allocator<Type>>;
//! `wil::secure_wstring` will be securely zeroed before deallocation.
using secure_wstring = std::basic_string<wchar_t, std::char_traits<wchar_t>, wil::secure_allocator<wchar_t>>;
//! `wil::secure_string` will be securely zeroed before deallocation.
using secure_string = std::basic_string<char, std::char_traits<char>, wil::secure_allocator<char>>;
/// @cond
namespace details
{
template<> struct string_maker<std::wstring>
{
HRESULT make(_In_reads_opt_(length) PCWSTR source, size_t length) WI_NOEXCEPT try
{
m_value = source ? std::wstring(source, length) : std::wstring(length, L'\0');
return S_OK;
}
catch (...)
{
return E_OUTOFMEMORY;
}
wchar_t* buffer() { return &m_value[0]; }
HRESULT trim_at_existing_null(size_t length) { m_value.erase(length); return S_OK; }
std::wstring release() { return std::wstring(std::move(m_value)); }
static PCWSTR get(const std::wstring& value) { return value.c_str(); }
private:
std::wstring m_value;
};
}
/// @endcond
// str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer.
// This is the overload for std::wstring. Other overloads available in resource.h.
inline PCWSTR str_raw_ptr(const std::wstring& str)
{
return str.c_str();
}
#if _HAS_CXX17
/**
zstring_view. A zstring_view is identical to a std::string_view except it is always nul-terminated (unless empty).
* zstring_view can be used for storing string literals without "forgetting" the length or that it is nul-terminated.
* A zstring_view can be converted implicitly to a std::string_view because it is always safe to use a nul-terminated
string_view as a plain string view.
* A zstring_view can be constructed from a std::string because the data in std::string is nul-terminated.
*/
template<class TChar>
class basic_zstring_view : public std::basic_string_view<TChar>
{
using size_type = typename std::basic_string_view<TChar>::size_type;
public:
constexpr basic_zstring_view() noexcept = default;
constexpr basic_zstring_view(const basic_zstring_view&) noexcept = default;
constexpr basic_zstring_view& operator=(const basic_zstring_view&) noexcept = default;
constexpr basic_zstring_view(const TChar* pStringData, size_type stringLength) noexcept
: std::basic_string_view<TChar>(pStringData, stringLength)
{
if (pStringData[stringLength] != 0) { WI_STL_FAIL_FAST_IF(true); }
}
template<size_t stringArrayLength>
constexpr basic_zstring_view(const TChar(&stringArray)[stringArrayLength]) noexcept
: std::basic_string_view<TChar>(&stringArray[0], length_n(&stringArray[0], stringArrayLength))
{
}
// Construct from nul-terminated char ptr. To prevent this from overshadowing array construction,
// we disable this constructor if the value is an array (including string literal).
template<typename TPtr, std::enable_if_t<
std::is_convertible<TPtr, const TChar*>::value && !std::is_array<TPtr>::value>* = nullptr>
constexpr basic_zstring_view(TPtr&& pStr) noexcept
: std::basic_string_view<TChar>(std::forward<TPtr>(pStr)) {}
constexpr basic_zstring_view(const std::basic_string<TChar>& str) noexcept
: std::basic_string_view<TChar>(&str[0], str.size()) {}
// basic_string_view [] precondition won't let us read view[view.size()]; so we define our own.
WI_NODISCARD constexpr const TChar& operator[](size_type idx) const noexcept
{
WI_ASSERT(idx <= this->size() && this->data() != nullptr);
return this->data()[idx];
}
WI_NODISCARD constexpr const TChar* c_str() const noexcept
{
WI_ASSERT(this->data() == nullptr || this->data()[this->size()] == 0);
return this->data();
}
private:
// Bounds-checked version of char_traits::length, like strnlen. Requires that the input contains a null terminator.
static constexpr size_type length_n(_In_reads_opt_(buf_size) const TChar* str, size_type buf_size) noexcept
{
const std::basic_string_view<TChar> view(str, buf_size);
auto pos = view.find_first_of(TChar());
if (pos == view.npos) { WI_STL_FAIL_FAST_IF(true); }
return pos;
}
// The following basic_string_view methods must not be allowed because they break the nul-termination.
using std::basic_string_view<TChar>::swap;
using std::basic_string_view<TChar>::remove_suffix;
};
using zstring_view = basic_zstring_view<char>;
using zwstring_view = basic_zstring_view<wchar_t>;
#endif // _HAS_CXX17
} // namespace wil
#endif // WIL_ENABLE_EXCEPTIONS
#endif // __WIL_STL_INCLUDED

View file

@ -0,0 +1,613 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_TOKEN_HELPERS_INCLUDED
#define __WIL_TOKEN_HELPERS_INCLUDED
#ifdef _KERNEL_MODE
#error This header is not supported in kernel-mode.
#endif
#include "resource.h"
#include <new>
#include <lmcons.h> // for UNLEN and DNLEN
#include <processthreadsapi.h>
// for GetUserNameEx()
#ifndef SECURITY_WIN32
#define SECURITY_WIN32
#endif
#include <Security.h>
namespace wil
{
/// @cond
namespace details
{
// Template specialization for TOKEN_INFORMATION_CLASS, add more mappings here as needed
// TODO: The mapping should be reversed to be MapTokenInfoClassToStruct since there may
// be an info class value that uses the same structure. That is the case for the file
// system information.
template<typename T> struct MapTokenStructToInfoClass;
template<> struct MapTokenStructToInfoClass<TOKEN_ACCESS_INFORMATION> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_APPCONTAINER_INFORMATION> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_DEFAULT_DACL> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_GROUPS_AND_PRIVILEGES> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_LABEL> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_OWNER> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_PRIMARY_GROUP> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_PRIVILEGES> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static constexpr bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_USER> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenUser; static constexpr bool FixedSize = false; };
// fixed size cases
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION_TYPE> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_POLICY> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_ORIGIN> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_SOURCE> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenSource; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_STATISTICS> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_TYPE> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenType; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<SECURITY_IMPERSONATION_LEVEL> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static constexpr bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static constexpr bool FixedSize = true; };
struct token_info_deleter
{
template<typename T> void operator()(T* p) const
{
static_assert(wistd::is_trivially_destructible_v<T>, "do not use with nontrivial types");
::operator delete(p);
}
};
}
/// @endcond
enum class OpenThreadTokenAs
{
Current,
Self
};
/** Open the active token.
Opens either the current thread token (if impersonating) or the current process token. Returns a token the caller
can use with methods like get_token_information<> below. By default, the token is opened for TOKEN_QUERY and as the
effective user.
Consider using GetCurrentThreadEffectiveToken() instead of this method when eventually calling get_token_information.
This method returns a real handle to the effective token, but GetCurrentThreadEffectiveToken() is a Pseudo-handle
and much easier to manage.
~~~~
wil::unique_handle theToken;
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken));
~~~~
Callers who want more access to the token (such as to duplicate or modify the token) can pass
any mask of the token rights.
~~~~
wil::unique_handle theToken;
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES));
~~~~
Services impersonating their clients may need to request that the active token is opened on the
behalf of the service process to perform certain operations. Opening a token for impersonation access
or privilege-adjustment are examples of uses.
~~~~
wil::unique_handle callerToken;
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_IMPERSONATE, OpenThreadTokenAs::Self));
~~~~
@param tokenHandle Receives the token opened during the operation. Must be CloseHandle'd by the caller, or
(preferably) stored in a wil::unique_handle
@param access Bits from the TOKEN_* access mask which are passed to OpenThreadToken/OpenProcessToken
@param openAs Current to use current thread security context, or Self to use process security context.
*/
inline HRESULT open_current_access_token_nothrow(_Out_ HANDLE* tokenHandle, unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
{
HRESULT hr = (OpenThreadToken(GetCurrentThread(), access, (openAs == OpenThreadTokenAs::Self), tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
if (hr == HRESULT_FROM_WIN32(ERROR_NO_TOKEN))
{
hr = (OpenProcessToken(GetCurrentProcess(), access, tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
}
return hr;
}
//! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead.
inline wil::unique_handle open_current_access_token_failfast(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
{
HANDLE rawTokenHandle;
FAIL_FAST_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs));
return wil::unique_handle(rawTokenHandle);
}
// Exception based function to open current thread/process access token and acquire pointer to it
#ifdef WIL_ENABLE_EXCEPTIONS
//! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead.
inline wil::unique_handle open_current_access_token(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
{
HANDLE rawTokenHandle;
THROW_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs));
return wil::unique_handle(rawTokenHandle);
}
#endif // WIL_ENABLE_EXCEPTIONS
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
// Returns tokenHandle or the effective thread token if tokenHandle is null.
// Note, this returns an token handle who's lifetime is managed independently
// and it may be a pseudo token, don't free it!
inline HANDLE GetCurrentThreadEffectiveTokenWithOverride(HANDLE tokenHandle)
{
return tokenHandle ? tokenHandle : GetCurrentThreadEffectiveToken();
}
/** Fetches information about a token.
See GetTokenInformation on MSDN for what this method can return. For variable sized structs the information
is returned to the caller as a wil::unique_tokeninfo_ptr<T> (like TOKEN_ORIGIN, TOKEN_USER, TOKEN_ELEVATION, etc.). For
fixed sized, the struct is returned directly.
The caller must have access to read the information from the provided token. This method works with both real
(e.g. OpenCurrentAccessToken) and pseudo (e.g. GetCurrentThreadToken) token handles.
~~~~
// Retrieve the TOKEN_USER structure for the current process
wil::unique_tokeninfo_ptr<TOKEN_USER> user;
RETURN_IF_FAILED(wil::get_token_information_nothrow(user, GetCurrentProcessToken()));
RETURN_IF_FAILED(ConsumeSid(user->User.Sid));
~~~~
Not specifying the token handle is the same as specifying 'nullptr' and retrieves information about the effective token.
~~~~
wil::unique_tokeninfo_ptr<TOKEN_PRIVILEGES> privileges;
RETURN_IF_FAILED(wil::get_token_information_nothrow(privileges));
for (auto const& privilege : wil::GetRange(privileges->Privileges, privileges->PrivilegeCount))
{
RETURN_IF_FAILED(ConsumePrivilege(privilege));
}
~~~~
@param tokenInfo Receives a pointer to a structure containing the results of GetTokenInformation for the requested
type. The type of <T> selects which TOKEN_INFORMATION_CLASS will be used.
@param tokenHandle Specifies which token will be queried. When nullptr, the thread's effective current token is used.
@return S_OK on success, a FAILED hresult containing the win32 error from querying the token otherwise.
*/
template <typename Q> using unique_tokeninfo_ptr = wistd::unique_ptr<Q, details::token_info_deleter>;
template <typename T, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
inline HRESULT get_token_information_nothrow(unique_tokeninfo_ptr<T>& tokenInfo, HANDLE tokenHandle = nullptr)
{
tokenInfo.reset();
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
DWORD tokenInfoSize = 0;
const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass;
RETURN_LAST_ERROR_IF(!((!GetTokenInformation(tokenHandle, infoClass, nullptr, 0, &tokenInfoSize)) &&
(::GetLastError() == ERROR_INSUFFICIENT_BUFFER)));
unique_tokeninfo_ptr<T> tokenInfoClose{ static_cast<T*>(::operator new(tokenInfoSize, std::nothrow)) };
RETURN_IF_NULL_ALLOC(tokenInfoClose);
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfoClose.get(), tokenInfoSize, &tokenInfoSize));
tokenInfo = wistd::move(tokenInfoClose);
return S_OK;
}
template <typename T, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
inline HRESULT get_token_information_nothrow(_Out_ T* tokenInfo, HANDLE tokenHandle = nullptr)
{
*tokenInfo = {};
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
DWORD tokenInfoSize = sizeof(T);
const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass;
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfo, tokenInfoSize, &tokenInfoSize));
return S_OK;
}
namespace details
{
template<typename T, typename policy, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
unique_tokeninfo_ptr<T> GetTokenInfoWrap(HANDLE token = nullptr)
{
unique_tokeninfo_ptr<T> temp;
policy::HResult(get_token_information_nothrow(temp, token));
return temp;
}
template<typename T, typename policy, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
T GetTokenInfoWrap(HANDLE token = nullptr)
{
T temp{};
policy::HResult(get_token_information_nothrow(&temp, token));
return temp;
}
}
//! A variant of get_token_information<T> that fails-fast on errors retrieving the token
template <typename T>
inline auto get_token_information_failfast(HANDLE token = nullptr)
{
return details::GetTokenInfoWrap<T, err_failfast_policy>(token);
}
//! Overload of GetTokenInformationNoThrow that retrieves a token linked from the provided token
inline HRESULT get_token_information_nothrow(unique_token_linked_token& tokenInfo, HANDLE tokenHandle = nullptr)
{
static_assert(sizeof(tokenInfo) == sizeof(TOKEN_LINKED_TOKEN), "confusing size mismatch");
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
DWORD tokenInfoSize = 0;
RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(tokenHandle, TokenLinkedToken,
tokenInfo.reset_and_addressof(), sizeof(tokenInfo), &tokenInfoSize));
return S_OK;
}
/** Retrieves the linked-token information for a token.
Fails-fast if the link information cannot be retrieved.
~~~~
auto link = get_linked_token_information_failfast(GetCurrentThreadToken());
auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken);
~~~~
@param token Specifies the token to query. Pass nullptr to use the current effective thread token
@return unique_token_linked_token containing a handle to the linked token
*/
inline unique_token_linked_token get_linked_token_information_failfast(HANDLE token = nullptr)
{
unique_token_linked_token tokenInfo;
FAIL_FAST_IF_FAILED(get_token_information_nothrow(tokenInfo, token));
return tokenInfo;
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** Fetches information about a token.
See get_token_information_nothrow for full details.
~~~~
auto user = wil::get_token_information<TOKEN_USER>(GetCurrentProcessToken());
ConsumeSid(user->User.Sid);
~~~~
Pass 'nullptr' (or omit the parameter) as tokenHandle to retrieve information about the effective token.
~~~~
auto privs = wil::get_token_information<TOKEN_PRIVILEGES>(privileges);
for (auto& priv : wil::make_range(privs->Privileges, privs->Privilieges + privs->PrivilegeCount))
{
if (priv.Attributes & SE_PRIVILEGE_ENABLED)
{
// ...
}
}
~~~~
@return A pointer to a structure containing the results of GetTokenInformation for the requested type. The type of
<T> selects which TOKEN_INFORMATION_CLASS will be used.
@param token Specifies which token will be queried. When nullptr or not set, the thread's effective current token is used.
*/
template <typename T>
inline auto get_token_information(HANDLE token = nullptr)
{
return details::GetTokenInfoWrap<T, err_exception_policy>(token);
}
/** Retrieves the linked-token information for a token.
Throws an exception if the link information cannot be retrieved.
~~~~
auto link = get_linked_token_information(GetCurrentThreadToken());
auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken);
~~~~
@param token Specifies the token to query. Pass nullptr to use the current effective thread token
@return unique_token_linked_token containing a handle to the linked token
*/
inline unique_token_linked_token get_linked_token_information(HANDLE token = nullptr)
{
unique_token_linked_token tokenInfo;
THROW_IF_FAILED(get_token_information_nothrow(tokenInfo, token));
return tokenInfo;
}
#endif
#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8
/// @cond
namespace details
{
inline void RevertImpersonateToken(_In_ _Post_ptr_invalid_ HANDLE oldToken)
{
FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken));
if (oldToken)
{
::CloseHandle(oldToken);
}
}
}
/// @endcond
using unique_token_reverter = wil::unique_any<
HANDLE,
decltype(&details::RevertImpersonateToken),
details::RevertImpersonateToken,
details::pointer_access_none,
HANDLE,
INT_PTR,
-1,
HANDLE>;
/** Temporarily impersonates a token on this thread.
This method sets a new token on a thread, restoring the current token when the returned object
is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services.
~~~~
HRESULT OpenFileAsSessionuser(PCWSTR filePath, DWORD session, _Out_ HANDLE* opened)
{
wil::unique_handle userToken;
RETURN_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken));
wil::unique_token_reverter reverter;
RETURN_IF_FAILED(wil::impersonate_token_nothrow(userToken.get(), reverter));
wil::unique_hfile userFile(::CreateFile(filePath, ...));
RETURN_LAST_ERROR_IF(!userFile && (::GetLastError() != ERROR_FILE_NOT_FOUND));
*opened = userFile.release();
return S_OK;
}
~~~~
@param token A token to impersonate, or 'nullptr' to run as the process identity.
*/
inline HRESULT impersonate_token_nothrow(HANDLE token, unique_token_reverter& reverter)
{
wil::unique_handle currentToken;
// Get the token for the current thread. If there wasn't one, the reset will clear it as well
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &currentToken))
{
RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_NO_TOKEN);
}
// Update the current token
RETURN_IF_WIN32_BOOL_FALSE(::SetThreadToken(nullptr, token));
reverter.reset(currentToken.release()); // Ownership passed
return S_OK;
}
/** Temporarily clears any impersonation on this thread.
This method resets the current thread's token to nullptr, indicating that it is not impersonating
any user. Useful for elevating to whatever identity a service or higher-privilege process might
be capable of running under.
~~~~
HRESULT DeleteFileRetryAsSelf(PCWSTR filePath)
{
if (!::DeleteFile(filePath))
{
RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_ACCESS_DENIED);
wil::unique_token_reverter reverter;
RETURN_IF_FAILED(wil::run_as_self_nothrow(reverter));
RETURN_IF_FAILED(TakeOwnershipOfFile(filePath));
RETURN_IF_FAILED(GrantDeleteAccess(filePath));
RETURN_IF_WIN32_BOOL_FALSE(::DeleteFile(filePath));
}
return S_OK;
}
~~~~
*/
inline HRESULT run_as_self_nothrow(unique_token_reverter& reverter)
{
return impersonate_token_nothrow(nullptr, reverter);
}
inline unique_token_reverter impersonate_token_failfast(HANDLE token)
{
unique_token_reverter oldToken;
FAIL_FAST_IF_FAILED(impersonate_token_nothrow(token, oldToken));
return oldToken;
}
inline unique_token_reverter run_as_self_failfast()
{
return impersonate_token_failfast(nullptr);
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** Temporarily impersonates a token on this thread.
This method sets a new token on a thread, restoring the current token when the returned object
is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services.
~~~~
wil::unique_hfile OpenFileAsSessionuser(_In_z_ const wchar_t* filePath, DWORD session)
{
wil::unique_handle userToken;
THROW_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken));
auto priorToken = wil::impersonate_token(userToken.get());
wil::unique_hfile userFile(::CreateFile(filePath, ...));
THROW_LAST_ERROR_IF(::GetLastError() != ERROR_FILE_NOT_FOUND);
return userFile;
}
~~~~
@param token A token to impersonate, or 'nullptr' to run as the process identity.
*/
inline unique_token_reverter impersonate_token(HANDLE token = nullptr)
{
unique_token_reverter oldToken;
THROW_IF_FAILED(impersonate_token_nothrow(token, oldToken));
return oldToken;
}
/** Temporarily clears any impersonation on this thread.
This method resets the current thread's token to nullptr, indicating that it is not impersonating
any user. Useful for elevating to whatever identity a service or higher-privilege process might
be capable of running under.
~~~~
void DeleteFileRetryAsSelf(_In_z_ const wchar_t* filePath)
{
if (!::DeleteFile(filePath) && (::GetLastError() == ERROR_ACCESS_DENIED))
{
auto priorToken = wil::run_as_self();
TakeOwnershipOfFile(filePath);
GrantDeleteAccess(filePath);
::DeleteFile(filePath);
}
}
~~~~
*/
inline unique_token_reverter run_as_self()
{
return impersonate_token(nullptr);
}
#endif // WIL_ENABLE_EXCEPTIONS
namespace details
{
template<size_t AuthorityCount> struct static_sid_t
{
BYTE Revision;
BYTE SubAuthorityCount;
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
DWORD SubAuthority[AuthorityCount];
PSID get()
{
return reinterpret_cast<PSID>(this);
}
template<size_t other> static_sid_t& operator=(const static_sid_t<other>& source)
{
static_assert(other <= AuthorityCount, "Cannot assign from a larger static sid to a smaller one");
if (&this->Revision != &source.Revision)
{
memcpy(this, &source, sizeof(source));
}
return *this;
}
};
}
/** Returns a structure containing a Revision 1 SID initialized with the authorities provided
Replaces AllocateAndInitializeSid by constructing a structure laid out like a PSID, but
returned like a value. The resulting object is suitable for use with any method taking PSID,
passed by "&the_sid" or via "the_sid.get()"
~~~~
// Change the owner of the key to administrators
auto systemSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
RETURN_IF_WIN32_ERROR(SetNamedSecurityInfo(keyPath, SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION, &systemSid, nullptr, nullptr, nullptr));
~~~~
*/
template<typename... Ts> constexpr auto make_static_sid(const SID_IDENTIFIER_AUTHORITY& authority, Ts&&... subAuthorities)
{
using sid_t = details::static_sid_t<sizeof...(subAuthorities)>;
static_assert(sizeof...(subAuthorities) <= SID_MAX_SUB_AUTHORITIES, "too many sub authorities");
static_assert(offsetof(sid_t, Revision) == offsetof(_SID, Revision), "layout mismatch");
static_assert(offsetof(sid_t, SubAuthorityCount) == offsetof(_SID, SubAuthorityCount), "layout mismatch");
static_assert(offsetof(sid_t, IdentifierAuthority) == offsetof(_SID, IdentifierAuthority), "layout mismatch");
static_assert(offsetof(sid_t, SubAuthority) == offsetof(_SID, SubAuthority), "layout mismatch");
return sid_t { SID_REVISION, sizeof...(subAuthorities), authority, { static_cast<DWORD>(subAuthorities)... } };
}
//! Variant of static_sid that defaults to the NT authority
template<typename... Ts> constexpr auto make_static_nt_sid(Ts&& ... subAuthorities)
{
return make_static_sid(SECURITY_NT_AUTHORITY, wistd::forward<Ts>(subAuthorities)...);
}
/** Determines whether a specified security identifier (SID) is enabled in an access token.
This function determines whether a security identifier, described by a given set of subauthorities, is enabled
in the given access token. Note that only up to eight subauthorities can be passed to this function.
~~~~
bool IsGuest()
{
return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS));
}
~~~~
@param result This will be set to true if and only if a security identifier described by the given set of subauthorities is enabled in the given access token.
@param token A handle to an access token. The handle must have TOKEN_QUERY access to the token, and must be an impersonation token. If token is nullptr, test_token_membership
uses the impersonation token of the calling thread. If the thread is not impersonating, the function duplicates the thread's primary token to create an impersonation token.
@param sidAuthority A reference to a SID_IDENTIFIER_AUTHORITY structure. This structure provides the top-level identifier authority value to set in the SID.
@param subAuthorities Up to 15 subauthority values to place in the SID (this is a systemwide limit)
@return S_OK on success, a FAILED hresult containing the win32 error from creating the SID or querying the token otherwise.
*/
template<typename... Ts> HRESULT test_token_membership_nothrow(_Out_ bool* result, _In_opt_ HANDLE token,
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
{
*result = false;
auto tempSid = make_static_sid(sidAuthority, wistd::forward<Ts>(subAuthorities)...);
BOOL isMember;
RETURN_IF_WIN32_BOOL_FALSE(CheckTokenMembership(token, &tempSid, &isMember));
*result = (isMember != FALSE);
return S_OK;
}
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
/** Determine whether a token represents an app container
This method uses the passed in token and emits a boolean indicating that
whether TokenIsAppContainer is true.
~~~~
HRESULT OnlyIfAppContainer()
{
bool isAppContainer;
RETURN_IF_FAILED(wil::get_token_is_app_container_nothrow(nullptr, isAppContainer));
RETURN_HR_IF(E_ACCESSDENIED, !isAppContainer);
RETURN_HR(...);
}
~~~~
@param token A token to get info about, or 'nullptr' to run as the current thread.
*/
inline HRESULT get_token_is_app_container_nothrow(_In_opt_ HANDLE token, bool& value)
{
DWORD isAppContainer = 0;
DWORD returnLength = 0;
RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(
token ? token : GetCurrentThreadEffectiveToken(),
TokenIsAppContainer,
&isAppContainer,
sizeof(isAppContainer),
&returnLength));
value = (isAppContainer != 0);
return S_OK;
}
//! A variant of get_token_is_app_container_nothrow that fails-fast on errors retrieving the token information
inline bool get_token_is_app_container_failfast(HANDLE token = nullptr)
{
bool value = false;
FAIL_FAST_IF_FAILED(get_token_is_app_container_nothrow(token, value));
return value;
}
#ifdef WIL_ENABLE_EXCEPTIONS
//! A variant of get_token_is_app_container_nothrow that throws on errors retrieving the token information
inline bool get_token_is_app_container(HANDLE token = nullptr)
{
bool value = false;
THROW_IF_FAILED(get_token_is_app_container_nothrow(token, value));
return value;
}
#endif // WIL_ENABLE_EXCEPTIONS
#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8
template<typename... Ts> bool test_token_membership_failfast(_In_opt_ HANDLE token,
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
{
bool result;
FAIL_FAST_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...));
return result;
}
#ifdef WIL_ENABLE_EXCEPTIONS
template<typename... Ts> bool test_token_membership(_In_opt_ HANDLE token, const SID_IDENTIFIER_AUTHORITY& sidAuthority,
Ts&&... subAuthorities)
{
bool result;
THROW_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...));
return result;
}
#endif
} //namespace wil
#endif // __WIL_TOKEN_HELPERS_INCLUDED

View file

@ -0,0 +1,71 @@
#pragma once
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_TRACELOGGING_CONFIG_H
#define __WIL_TRACELOGGING_CONFIG_H
// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition
// in this file configures the provider as a normal (non-telemetry) provider.
#define TraceLoggingOptionMicrosoftTelemetry() \
// Empty definition for TraceLoggingOptionMicrosoftTelemetry
// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition
// in this file configures the provider as a normal (non-telemetry) provider.
#define TraceLoggingOptionWindowsCoreTelemetry() \
// Empty definition for TraceLoggingOptionWindowsCoreTelemetry
// Event privacy tags. Use the PDT macro values for the tag parameter, e.g.:
// TraceLoggingWrite(...,
// TelemetryPrivacyDataTag(PDT_BrowsingHistory | PDT_ProductAndServiceUsage),
// ...);
#define TelemetryPrivacyDataTag(tag) TraceLoggingUInt64((tag), "PartA_PrivTags")
#define PDT_BrowsingHistory 0x0000000000000002u
#define PDT_DeviceConnectivityAndConfiguration 0x0000000000000800u
#define PDT_InkingTypingAndSpeechUtterance 0x0000000000020000u
#define PDT_ProductAndServicePerformance 0x0000000001000000u
#define PDT_ProductAndServiceUsage 0x0000000002000000u
#define PDT_SoftwareSetupAndInventory 0x0000000080000000u
// Event categories specified via keywords, e.g.:
// TraceLoggingWrite(...,
// TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
// ...);
#define MICROSOFT_KEYWORD_CRITICAL_DATA 0x0000800000000000 // Bit 47
#define MICROSOFT_KEYWORD_MEASURES 0x0000400000000000 // Bit 46
#define MICROSOFT_KEYWORD_TELEMETRY 0x0000200000000000 // Bit 45
#define MICROSOFT_KEYWORD_RESERVED_44 0x0000100000000000 // Bit 44 (reserved for future assignment)
// Event categories specified via event tags, e.g.:
// TraceLoggingWrite(...,
// TraceLoggingEventTag(MICROSOFT_EVENTTAG_REALTIME_LATENCY),
// ...);
#define MICROSOFT_EVENTTAG_DROP_USER_IDS 0x00008000
#define MICROSOFT_EVENTTAG_AGGREGATE 0x00010000
#define MICROSOFT_EVENTTAG_DROP_PII_EXCEPT_IP 0x00020000
#define MICROSOFT_EVENTTAG_COSTDEFERRED_LATENCY 0x00040000
#define MICROSOFT_EVENTTAG_CORE_DATA 0x00080000
#define MICROSOFT_EVENTTAG_INJECT_XTOKEN 0x00100000
#define MICROSOFT_EVENTTAG_REALTIME_LATENCY 0x00200000
#define MICROSOFT_EVENTTAG_NORMAL_LATENCY 0x00400000
#define MICROSOFT_EVENTTAG_CRITICAL_PERSISTENCE 0x00800000
#define MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE 0x01000000
#define MICROSOFT_EVENTTAG_DROP_PII 0x02000000
#define MICROSOFT_EVENTTAG_HASH_PII 0x04000000
#define MICROSOFT_EVENTTAG_MARK_PII 0x08000000
// Field categories specified via field tags, e.g.:
// TraceLoggingWrite(...,
// TraceLoggingString(szUser, "UserName", "User's name", MICROSOFT_FIELDTAG_HASH_PII),
// ...);
#define MICROSOFT_FIELDTAG_DROP_PII 0x04000000
#define MICROSOFT_FIELDTAG_HASH_PII 0x08000000
#endif // __WIL_TRACELOGGING_CONFIG_H

View file

@ -0,0 +1,897 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WIN32_HELPERS_INCLUDED
#define __WIL_WIN32_HELPERS_INCLUDED
#include <minwindef.h> // FILETIME, HINSTANCE
#include <sysinfoapi.h> // GetSystemTimeAsFileTime
#include <libloaderapi.h> // GetProcAddress
#include <Psapi.h> // GetModuleFileNameExW (macro), K32GetModuleFileNameExW
#include <winreg.h>
#include <objbase.h>
// detect std::bit_cast
#ifdef __has_include
# if (__cplusplus >= 202002L || _MSVC_LANG >= 202002L) && __has_include(<bit>)
# include <bit>
# endif
#endif
#if __cpp_lib_bit_cast >= 201806L
# define __WI_CONSTEXPR_BIT_CAST constexpr
#else
# define __WI_CONSTEXPR_BIT_CAST inline
#endif
#include "result.h"
#include "resource.h"
#include "wistd_functional.h"
#include "wistd_type_traits.h"
#if _HAS_CXX20 && defined(_STRING_VIEW_) && defined(_COMPARE_)
// If we're using c++20, then <compare> must be included to use the string ordinal functions
# define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS
#elif !_HAS_CXX20 && defined(_STRING_VIEW_)
# define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS
#endif
namespace wistd
{
#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
#if _HAS_CXX20
using weak_ordering = std::weak_ordering;
#else // _HAS_CXX20
struct weak_ordering
{
static const weak_ordering less;
static const weak_ordering equivalent;
static const weak_ordering greater;
[[nodiscard]] friend constexpr bool operator==(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value == 0;
}
[[nodiscard]] friend constexpr bool operator!=(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value != 0;
}
[[nodiscard]] friend constexpr bool operator<(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value < 0;
}
[[nodiscard]] friend constexpr bool operator>(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value > 0;
}
[[nodiscard]] friend constexpr bool operator<=(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value <= 0;
}
[[nodiscard]] friend constexpr bool operator>=(const weak_ordering left, std::nullptr_t) noexcept
{
return left.m_value >= 0;
}
[[nodiscard]] friend constexpr bool operator==(std::nullptr_t, const weak_ordering right) noexcept
{
return right == 0;
}
[[nodiscard]] friend constexpr bool operator!=(std::nullptr_t, const weak_ordering right) noexcept
{
return right != 0;
}
[[nodiscard]] friend constexpr bool operator<(std::nullptr_t, const weak_ordering right) noexcept
{
return right > 0;
}
[[nodiscard]] friend constexpr bool operator>(std::nullptr_t, const weak_ordering right) noexcept
{
return right < 0;
}
[[nodiscard]] friend constexpr bool operator<=(std::nullptr_t, const weak_ordering right) noexcept
{
return right >= 0;
}
[[nodiscard]] friend constexpr bool operator>=(std::nullptr_t, const weak_ordering right) noexcept
{
return right <= 0;
}
signed char m_value;
};
inline constexpr weak_ordering weak_ordering::less{static_cast<signed char>(-1)};
inline constexpr weak_ordering weak_ordering::equivalent{static_cast<signed char>(0)};
inline constexpr weak_ordering weak_ordering::greater{static_cast<signed char>(1)};
#endif // !_HAS_CXX20
#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
}
namespace wil
{
//! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT.
//! CDFs has a limit of 254.
constexpr size_t max_path_segment_length = 255;
//! Character length not including the null, MAX_PATH (260) includes the null.
constexpr size_t max_path_length = 259;
//! 32743 Character length not including the null. This is a system defined limit.
//! The 24 is for the expansion of the roots from "C:" to "\Device\HarddiskVolume4"
//! It will be 25 when there are more than 9 disks.
constexpr size_t max_extended_path_length = 0x7FFF - 24;
//! For {guid} string form. Includes space for the null terminator.
constexpr size_t guid_string_buffer_length = 39;
//! For {guid} string form. Not including the null terminator.
constexpr size_t guid_string_length = 38;
#pragma region String and identifier comparisons
// Using CompareStringOrdinal functions:
//
// Indentifiers require a locale-less (ordinal), and often case-insensitive, comparison (filenames, registry keys, XML node names, etc).
// DO NOT use locale-sensitive (lexical) comparisons for resource identifiers (e.g.wcs*() functions in the CRT).
#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
namespace details
{
[[nodiscard]] inline int CompareStringOrdinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT
{
// Casting from size_t (unsigned) to int (signed) should be safe from overrun to a negative,
// merely truncating the string. CompareStringOrdinal should be resilient to negatives.
return ::CompareStringOrdinal(left.data(), static_cast<int>(left.size()), right.data(), static_cast<int>(right.size()), caseInsensitive);
}
}
[[nodiscard]] inline wistd::weak_ordering compare_string_ordinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT
{
switch (wil::details::CompareStringOrdinal(left, right, caseInsensitive))
{
case CSTR_LESS_THAN:
return wistd::weak_ordering::less;
case CSTR_GREATER_THAN:
return wistd::weak_ordering::greater;
default:
return wistd::weak_ordering::equivalent;
}
}
#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
#pragma endregion
#pragma region FILETIME helpers
// FILETIME duration values. FILETIME is in 100 nanosecond units.
namespace filetime_duration
{
long long const one_millisecond = 10000LL;
long long const one_second = 10000000LL;
long long const one_minute = 10000000LL * 60; // 600000000 or 600000000LL
long long const one_hour = 10000000LL * 60 * 60; // 36000000000 or 36000000000LL
long long const one_day = 10000000LL * 60 * 60 * 24; // 864000000000 or 864000000000LL
};
namespace filetime
{
constexpr unsigned long long to_int64(const FILETIME &ft) WI_NOEXCEPT
{
#if __cpp_lib_bit_cast >= 201806L
return std::bit_cast<unsigned long long>(ft);
#else
// Cannot reinterpret_cast FILETIME* to unsigned long long*
// due to alignment differences.
return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
#endif
}
__WI_CONSTEXPR_BIT_CAST FILETIME from_int64(unsigned long long i64) WI_NOEXCEPT
{
#if __cpp_lib_bit_cast >= 201806L
return std::bit_cast<FILETIME>(i64);
#else
static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match");
static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun");
return *reinterpret_cast<FILETIME *>(&i64);
#endif
}
__WI_CONSTEXPR_BIT_CAST FILETIME add(_In_ FILETIME const &ft, long long delta100ns) WI_NOEXCEPT
{
return from_int64(to_int64(ft) + delta100ns);
}
constexpr bool is_empty(const FILETIME &ft) WI_NOEXCEPT
{
return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
}
inline FILETIME get_system_time() WI_NOEXCEPT
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return ft;
}
/// Convert time as units of 100 nanoseconds to milliseconds. Fractional milliseconds are truncated.
constexpr unsigned long long convert_100ns_to_msec(unsigned long long time100ns) WI_NOEXCEPT
{
return time100ns / filetime_duration::one_millisecond;
}
/// Convert time as milliseconds to units of 100 nanoseconds.
constexpr unsigned long long convert_msec_to_100ns(unsigned long long timeMsec) WI_NOEXCEPT
{
return timeMsec * filetime_duration::one_millisecond;
}
#if defined(_APISETREALTIME_) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7)
/// Returns the current unbiased interrupt-time count, in units of 100 nanoseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation.
///
/// This API avoids prematurely shortcircuiting timing loops due to system sleep/hibernation.
///
/// This is equivalent to GetTickCount64() except it returns units of 100 nanoseconds instead of milliseconds, and it doesn't include time the system spends in sleep or hibernation.
/// For example
///
/// start = GetTickCount64();
/// hibernate();
/// ...wake from hibernation 30 minutes later...;
/// elapsed = GetTickCount64() - start;
/// // elapsed = 30min
///
/// Do the same using unbiased interrupt-time and elapsed is 0 (or nearly so).
///
/// @note This is identical to QueryUnbiasedInterruptTime() but returns the value as a return value (rather than an out parameter).
/// @see https://msdn.microsoft.com/en-us/library/windows/desktop/ee662307(v=vs.85).aspx
inline unsigned long long QueryUnbiasedInterruptTimeAs100ns() WI_NOEXCEPT
{
ULONGLONG now{};
QueryUnbiasedInterruptTime(&now);
return now;
}
/// Returns the current unbiased interrupt-time count, in units of milliseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation.
/// @see QueryUnbiasedInterruptTimeAs100ns
inline unsigned long long QueryUnbiasedInterruptTimeAsMSec() WI_NOEXCEPT
{
return convert_100ns_to_msec(QueryUnbiasedInterruptTimeAs100ns());
}
#endif // _APISETREALTIME_
}
#pragma endregion
#pragma region RECT helpers
template<typename rect_type>
constexpr auto rect_width(rect_type const& rect)
{
return rect.right - rect.left;
}
template<typename rect_type>
constexpr auto rect_height(rect_type const& rect)
{
return rect.bottom - rect.top;
}
template<typename rect_type>
constexpr auto rect_is_empty(rect_type const& rect)
{
return (rect.left >= rect.right) || (rect.top >= rect.bottom);
}
template<typename rect_type, typename point_type>
constexpr auto rect_contains_point(rect_type const& rect, point_type const& point)
{
return (point.x >= rect.left) && (point.x < rect.right) && (point.y >= rect.top) && (point.y < rect.bottom);
}
template<typename rect_type, typename length_type>
constexpr rect_type rect_from_size(length_type x, length_type y, length_type width, length_type height)
{
rect_type rect;
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
return rect;
}
#pragma endregion
// Use to adapt Win32 APIs that take a fixed size buffer into forms that return
// an allocated buffer. Supports many types of string representation.
// See comments below on the expected behavior of the callback.
// Adjust stackBufferLength based on typical result sizes to optimize use and
// to test the boundary cases.
template <typename string_type, size_t stackBufferLength = 256>
HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback) WI_NOEXCEPT
{
details::string_maker<string_type> maker;
wchar_t value[stackBufferLength]{};
size_t valueLengthNeededWithNull{}; // callback returns the number of characters needed including the null terminator.
RETURN_IF_FAILED_EXPECTED(callback(value, ARRAYSIZE(value), &valueLengthNeededWithNull));
WI_ASSERT(valueLengthNeededWithNull > 0);
if (valueLengthNeededWithNull <= ARRAYSIZE(value))
{
// Success case as described above, make() adds the space for the null.
RETURN_IF_FAILED(maker.make(value, valueLengthNeededWithNull - 1));
}
else
{
// Did not fit in the stack allocated buffer, need to do 2 phase construction.
// May need to loop more than once if external conditions cause the value to change.
size_t bufferLength;
do
{
bufferLength = valueLengthNeededWithNull;
// bufferLength includes the null so subtract that as make() will add space for it.
RETURN_IF_FAILED(maker.make(nullptr, bufferLength - 1));
RETURN_IF_FAILED_EXPECTED(callback(maker.buffer(), bufferLength, &valueLengthNeededWithNull));
WI_ASSERT(valueLengthNeededWithNull > 0);
// If the value shrunk, then adjust the string to trim off the excess buffer.
if (valueLengthNeededWithNull < bufferLength)
{
RETURN_IF_FAILED(maker.trim_at_existing_null(valueLengthNeededWithNull - 1));
}
}
while (valueLengthNeededWithNull > bufferLength);
}
result = maker.release();
return S_OK;
}
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
template <typename string_type, size_t stackBufferLength = 256>
HRESULT ExpandEnvironmentStringsW(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
*valueLengthNeededWithNul = ::ExpandEnvironmentStringsW(input, value, static_cast<DWORD>(valueLength));
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
return S_OK;
});
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
/** Searches for a specified file in a specified path using ExpandEnvironmentStringsW(); */
template <typename string_type, size_t stackBufferLength = 256>
HRESULT SearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, _In_opt_ PCWSTR extension, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
*valueLengthNeededWithNul = ::SearchPathW(path, fileName, extension, static_cast<DWORD>(valueLength), value, nullptr);
if (*valueLengthNeededWithNul == 0)
{
// ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
const HRESULT searchResult = HRESULT_FROM_WIN32(::GetLastError());
RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
RETURN_IF_FAILED(searchResult);
}
// AdaptFixedSizeToAllocatedResult expects that the length will always include the NUL.
// If the result is copied to the buffer, SearchPathW returns the length of copied string, WITHOUT the NUL.
// If the buffer is too small to hold the result, SearchPathW returns the length of the required buffer WITH the nul.
if (*valueLengthNeededWithNul < valueLength)
{
(*valueLengthNeededWithNul)++; // It fit, account for the null.
}
return S_OK;
});
}
template <typename string_type, size_t stackBufferLength = 256>
HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
DWORD lengthToUse = static_cast<DWORD>(valueLength);
BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse);
RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER));
// On success, return the amount used; on failure, try doubling
*valueLengthNeededWithNul = success ? (static_cast<size_t>(lengthToUse) + 1) : (static_cast<size_t>(lengthToUse) * 2);
return S_OK;
});
}
/** Expands environment strings and checks path existence with SearchPathW */
template <typename string_type, size_t stackBufferLength = 256>
HRESULT ExpandEnvAndSearchPath(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
{
wil::unique_cotaskmem_string expandedName;
RETURN_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, expandedName)));
// ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
const HRESULT searchResult = (wil::SearchPathW<string_type, stackBufferLength>(nullptr, expandedName.get(), nullptr, result));
RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
RETURN_IF_FAILED(searchResult);
return S_OK;
}
#endif
/** Looks up the environment variable 'key' and fails if it is not found. */
template <typename string_type, size_t initialBufferLength = 128>
inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
// If the function succeeds, the return value is the number of characters stored in the buffer
// pointed to by lpBuffer, not including the terminating null character.
//
// If lpBuffer is not large enough to hold the data, the return value is the buffer size, in
// characters, required to hold the string and its terminating null character and the contents of
// lpBuffer are undefined.
//
// If the function fails, the return value is zero. If the specified environment variable was not
// found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND.
::SetLastError(ERROR_SUCCESS);
*valueLengthNeededWithNul = ::GetEnvironmentVariableW(key, value, static_cast<DWORD>(valueLength));
RETURN_LAST_ERROR_IF_EXPECTED((*valueLengthNeededWithNul == 0) && (::GetLastError() != ERROR_SUCCESS));
if (*valueLengthNeededWithNul < valueLength)
{
(*valueLengthNeededWithNul)++; // It fit, account for the null.
}
return S_OK;
});
}
/** Looks up the environment variable 'key' and returns null if it is not found. */
template <typename string_type, size_t initialBufferLength = 128>
HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
{
const auto hr = wil::GetEnvironmentVariableW<string_type, initialBufferLength>(key, result);
RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND)));
return S_OK;
}
/** Retrieves the fully qualified path for the file containing the specified module loaded
by a given process. Note GetModuleFileNameExW is a macro.*/
template <typename string_type, size_t initialBufferLength = 128>
HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path) WI_NOEXCEPT
{
auto adapter = [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
DWORD copiedCount{};
size_t valueUsedWithNul{};
bool copyFailed{};
bool copySucceededWithNoTruncation{};
if (process != nullptr)
{
// GetModuleFileNameExW truncates and provides no error or other indication it has done so.
// The only way to be sure it didn't truncate is if it didn't need the whole buffer. The
// count copied to the buffer includes the nul-character as well.
copiedCount = ::GetModuleFileNameExW(process, module, value, static_cast<DWORD>(valueLength));
valueUsedWithNul = static_cast<size_t>(copiedCount) + 1;
copyFailed = (0 == copiedCount);
copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength - 1);
}
else
{
// In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
// and set the last error to ERROR_INSUFFICIENT_BUFFER. The count returned does not include
// the nul-character
copiedCount = ::GetModuleFileNameW(module, value, static_cast<DWORD>(valueLength));
valueUsedWithNul = static_cast<size_t>(copiedCount) + 1;
copyFailed = (0 == copiedCount);
copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength);
}
RETURN_LAST_ERROR_IF(copyFailed);
// When the copy truncated, request another try with more space.
*valueLengthNeededWithNul = copySucceededWithNoTruncation ? valueUsedWithNul : (valueLength * 2);
return S_OK;
};
return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(path, wistd::move(adapter));
}
/** Retrieves the fully qualified path for the file that contains the specified module.
The module must have been loaded by the current process. The path returned will use the
same format that was specified when the module was loaded. Therefore, the path can be a
long or short file name, and can have the prefix '\\?\'. */
template <typename string_type, size_t initialBufferLength = 128>
HRESULT GetModuleFileNameW(HMODULE module, string_type& path) WI_NOEXCEPT
{
return wil::GetModuleFileNameExW<string_type, initialBufferLength>(nullptr, module, path);
}
template <typename string_type, size_t stackBufferLength = 256>
HRESULT GetSystemDirectoryW(string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
*valueLengthNeededWithNul = ::GetSystemDirectoryW(value, static_cast<DWORD>(valueLength));
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
if (*valueLengthNeededWithNul < valueLength)
{
(*valueLengthNeededWithNul)++; // it fit, account for the null
}
return S_OK;
});
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
template <typename string_type, size_t stackBufferLength = 256>
HRESULT GetWindowsDirectoryW(string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
*valueLengthNeededWithNul = ::GetWindowsDirectoryW(value, static_cast<DWORD>(valueLength));
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
if (*valueLengthNeededWithNul < valueLength)
{
(*valueLengthNeededWithNul)++; // it fit, account for the null
}
return S_OK;
});
}
#endif
#ifdef WIL_ENABLE_EXCEPTIONS
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type ExpandEnvironmentStringsW(_In_ PCWSTR input)
{
string_type result{};
THROW_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, result)));
return result;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
/** Searches for a specified file in a specified path using SearchPathW*/
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type TrySearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, PCWSTR _In_opt_ extension)
{
string_type result{};
HRESULT searchHR = wil::SearchPathW<string_type, stackBufferLength>(path, fileName, extension, result);
THROW_HR_IF(searchHR, FAILED(searchHR) && (searchHR != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)));
return result;
}
#endif
/** Looks up the environment variable 'key' and fails if it is not found. */
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
string_type GetEnvironmentVariableW(_In_ PCWSTR key)
{
string_type result{};
THROW_IF_FAILED((wil::GetEnvironmentVariableW<string_type, initialBufferLength>(key, result)));
return result;
}
/** Looks up the environment variable 'key' and returns null if it is not found. */
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
{
string_type result{};
THROW_IF_FAILED((wil::TryGetEnvironmentVariableW<string_type, initialBufferLength>(key, result)));
return result;
}
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
string_type GetModuleFileNameW(HMODULE module = nullptr /* current process module */)
{
string_type result{};
THROW_IF_FAILED((wil::GetModuleFileNameW<string_type, initialBufferLength>(module, result)));
return result;
}
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
{
string_type result{};
THROW_IF_FAILED((wil::GetModuleFileNameExW<string_type, initialBufferLength>(process, module, result)));
return result;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type GetWindowsDirectoryW()
{
string_type result;
THROW_IF_FAILED((wil::GetWindowsDirectoryW<string_type, stackBufferLength>(result)));
return result;
}
#endif
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type GetSystemDirectoryW()
{
string_type result;
THROW_IF_FAILED((wil::GetSystemDirectoryW<string_type, stackBufferLength>(result)));
return result;
}
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type QueryFullProcessImageNameW(HANDLE processHandle = GetCurrentProcess(), DWORD flags = 0)
{
string_type result{};
THROW_IF_FAILED((wil::QueryFullProcessImageNameW<string_type, stackBufferLength>(processHandle, flags, result)));
return result;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
// Lookup a DWORD value under HKLM\...\Image File Execution Options\<current process name>
inline DWORD GetCurrentProcessExecutionOption(PCWSTR valueName, DWORD defaultValue = 0)
{
auto filePath = wil::GetModuleFileNameW<wil::unique_cotaskmem_string>();
if (auto lastSlash = wcsrchr(filePath.get(), L'\\'))
{
const auto fileName = lastSlash + 1;
auto keyPath = wil::str_concat<wil::unique_cotaskmem_string>(LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\)",
fileName);
DWORD value{}, sizeofValue = sizeof(value);
if (::RegGetValueW(HKEY_LOCAL_MACHINE, keyPath.get(), valueName,
#ifdef RRF_SUBKEY_WOW6464KEY
RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY,
#else
RRF_RT_REG_DWORD,
#endif
nullptr, &value, &sizeofValue) == ERROR_SUCCESS)
{
return value;
}
}
return defaultValue;
}
// Waits for a debugger to attach to the current process based on registry configuration.
//
// Example:
// HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\explorer.exe
// WaitForDebuggerPresent=1
//
// REG_DWORD value of
// missing or 0 -> don't break
// 1 -> wait for the debugger, continue execution once it is attached
// 2 -> wait for the debugger, break here once attached.
inline void WaitForDebuggerPresent(bool checkRegistryConfig = true)
{
for (;;)
{
auto configValue = checkRegistryConfig ? GetCurrentProcessExecutionOption(L"WaitForDebuggerPresent") : 1;
if (configValue == 0)
{
return; // not configured, don't wait
}
if (IsDebuggerPresent())
{
if (configValue == 2)
{
DebugBreak(); // debugger attached, SHIFT+F11 to return to the caller
}
return; // debugger now attached, continue executing
}
Sleep(500);
}
}
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
#endif
/** Retrieve the HINSTANCE for the current DLL or EXE using this symbol that
the linker provides for every module. This avoids the need for a global HINSTANCE variable
and provides access to this value for static libraries. */
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
inline HINSTANCE GetModuleInstanceHandle() WI_NOEXCEPT { return reinterpret_cast<HINSTANCE>(&__ImageBase); }
// GetModuleHandleExW was added to the app partition in version 22000 of the SDK
#if defined(NTDDI_WIN10_CO) ? \
WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) : \
WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
// Use this in threads that can outlive the object or API call that created them.
// Without this COM, or the API caller, can unload the DLL, resulting in a crash.
// It is very important that this be the first object created in the thread proc
// as when this runs down the thread exits and no destructors of objects created before
// it will run.
[[nodiscard]] inline auto get_module_reference_for_thread() noexcept
{
HMODULE thisModule{};
FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, L"", &thisModule));
return wil::scope_exit([thisModule]
{
FreeLibraryAndExitThread(thisModule, 0);
});
}
#endif
/// @cond
namespace details
{
class init_once_completer
{
INIT_ONCE& m_once;
unsigned long m_flags = INIT_ONCE_INIT_FAILED;
public:
init_once_completer(_In_ INIT_ONCE& once) WI_NOEXCEPT : m_once(once)
{
}
#pragma warning(push)
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
void success() WI_NOEXCEPT
{
m_flags = 0;
}
#pragma warning(pop)
~init_once_completer() WI_NOEXCEPT
{
::InitOnceComplete(&m_once, m_flags, nullptr);
}
};
}
/// @endcond
/** Performs one-time initialization
Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
at most once.
~~~~
INIT_ONCE g_init{};
ComPtr<IFoo> g_foo;
HRESULT MyMethod()
{
bool winner = false;
RETURN_IF_FAILED(wil::init_once_nothrow(g_init, []
{
ComPtr<IFoo> foo;
RETURN_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
RETURN_IF_FAILED(foo->Startup());
g_foo = foo;
}, &winner);
if (winner)
{
RETURN_IF_FAILED(g_foo->Another());
}
return S_OK;
}
~~~~
See MSDN for more information on `InitOnceExecuteOnce`.
@param initOnce The INIT_ONCE structure to use as context for initialization.
@param func A function that will be invoked to perform initialization. If this fails, the init call
fails and the once-init is not marked as initialized. A later caller could attempt to
initialize it a second time.
@param callerCompleted Set to 'true' if this was the call that caused initialization, false otherwise.
*/
template<typename T> HRESULT init_once_nothrow(_Inout_ INIT_ONCE& initOnce, T func, _Out_opt_ bool* callerCompleted = nullptr) WI_NOEXCEPT
{
BOOL pending = FALSE;
wil::assign_to_opt_param(callerCompleted, false);
__WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
if (pending)
{
details::init_once_completer completion(initOnce);
__WIL_PRIVATE_RETURN_IF_FAILED(func());
completion.success();
wil::assign_to_opt_param(callerCompleted, true);
}
return S_OK;
}
//! Similar to init_once_nothrow, but fails-fast if the initialization step failed. The 'callerComplete' value is
//! returned to the caller instead of being an out-parameter.
template<typename T> bool init_once_failfast(_Inout_ INIT_ONCE& initOnce, T&& func) WI_NOEXCEPT
{
bool callerCompleted;
FAIL_FAST_IF_FAILED(init_once_nothrow(initOnce, wistd::forward<T>(func), &callerCompleted));
return callerCompleted;
};
//! Returns 'true' if this `init_once` structure has finished initialization, false otherwise.
inline bool init_once_initialized(_Inout_ INIT_ONCE& initOnce) WI_NOEXCEPT
{
BOOL pending = FALSE;
return ::InitOnceBeginInitialize(&initOnce, INIT_ONCE_CHECK_ONLY, &pending, nullptr) && !pending;
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** Performs one-time initialization
Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
at most once.
~~~~
INIT_ONCE g_init{};
ComPtr<IFoo> g_foo;
void MyMethod()
{
bool winner = wil::init_once(g_init, []
{
ComPtr<IFoo> foo;
THROW_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
THROW_IF_FAILED(foo->Startup());
g_foo = foo;
});
if (winner)
{
THROW_IF_FAILED(g_foo->Another());
}
}
~~~~
See MSDN for more information on `InitOnceExecuteOnce`.
@param initOnce The INIT_ONCE structure to use as context for initialization.
@param func A function that will be invoked to perform initialization. If this fails, the init call
fails and the once-init is not marked as initialized. A later caller could attempt to
initialize it a second time.
@returns 'true' if this was the call that caused initialization, false otherwise.
*/
template<typename T> bool init_once(_Inout_ INIT_ONCE& initOnce, T func)
{
BOOL pending = FALSE;
THROW_IF_WIN32_BOOL_FALSE(::InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
if (pending)
{
details::init_once_completer completion(initOnce);
func();
completion.success();
return true;
}
else
{
return false;
}
}
#endif // WIL_ENABLE_EXCEPTIONS
}
// Macro for calling GetProcAddress(), with type safety for C++ clients
// using the type information from the specified function.
// The return value is automatically cast to match the function prototype of the input function.
//
// Sample usage:
//
// auto sendMail = GetProcAddressByFunctionDeclaration(hinstMAPI, MAPISendMailW);
// if (sendMail)
// {
// sendMail(0, 0, pmm, MAPI_USE_DEFAULT, 0);
// }
// Declaration
#define GetProcAddressByFunctionDeclaration(hinst, fn) reinterpret_cast<decltype(::fn)*>(GetProcAddress(hinst, #fn))
#endif // __WIL_WIN32_HELPERS_INCLUDED

View file

@ -0,0 +1,104 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WIN32_RESULTMACROS_INCLUDED
#define __WIL_WIN32_RESULTMACROS_INCLUDED
#include "result_macros.h"
// Helpers for return macros
#define __WIN32_RETURN_WIN32(error, str) __WI_SUPPRESS_4127_S do { const auto __error = (error); if (FAILED_WIN32(__error)) { __R_FN(Return_Win32)(__R_INFO(str) __error); } return __error; } __WI_SUPPRESS_4127_E while ((void)0, 0)
#define __WIN32_RETURN_GLE_FAIL(str) return __R_FN(Win32_Return_GetLastError)(__R_INFO_ONLY(str))
FORCEINLINE long __WIN32_FROM_HRESULT(HRESULT hr)
{
if (SUCCEEDED(hr))
{
return ERROR_SUCCESS;
}
return HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : hr;
}
//*****************************************************************************
// Macros for returning failures as WIN32 error codes
//*****************************************************************************
// Always returns a known result (WIN32 error code) - always logs failures
#define WIN32_RETURN_WIN32(error) __WIN32_RETURN_WIN32(wil::verify_win32(error), #error)
#define WIN32_RETURN_LAST_ERROR() __WIN32_RETURN_GLE_FAIL(nullptr)
// Conditionally returns failures (WIN32 error code) - always logs failures
#define WIN32_RETURN_IF_WIN32_ERROR(error) __WI_SUPPRESS_4127_S do { const auto __errorRet = wil::verify_win32(error); if (FAILED_WIN32(__errorRet)) { __WIN32_RETURN_WIN32(__errorRet, #error); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_WIN32_IF(error, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __WIN32_RETURN_WIN32(wil::verify_win32(error), #condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_WIN32_IF_NULL(error, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __WIN32_RETURN_WIN32(wil::verify_win32(error), #ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_LAST_ERROR_IF(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __WIN32_RETURN_GLE_FAIL(#condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_LAST_ERROR_IF_NULL(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __WIN32_RETURN_GLE_FAIL(#ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
// Conditionally returns failures (WIN32 error code) - use for failures that are expected in common use - failures are not logged - macros are only for control flow pattern
#define WIN32_RETURN_IF_WIN32_ERROR_EXPECTED(error) __WI_SUPPRESS_4127_S do { const auto __errorRet = wil::verify_win32(error); if (FAILED_WIN32(__errorRet)) { return __errorRet; }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_WIN32_IF_EXPECTED(error, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::verify_win32(error); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_WIN32_IF_NULL_EXPECTED(error, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::verify_win32(error); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_LAST_ERROR_IF_EXPECTED(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::verify_win32(wil::details::GetLastErrorFail()); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
#define WIN32_RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::verify_win32(wil::details::GetLastErrorFail()); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
//*****************************************************************************
// Macros to catch and convert exceptions on failure
//*****************************************************************************
// Use these macros *within* a catch (...) block to handle exceptions
#define WIN32_RETURN_CAUGHT_EXCEPTION() return __R_FN(Win32_Return_CaughtException)(__R_INFO_ONLY(nullptr))
// Use these macros in place of a catch block to handle exceptions
#define WIN32_CATCH_RETURN() catch (...) { WIN32_RETURN_CAUGHT_EXCEPTION(); }
namespace wil
{
//*****************************************************************************
// Public Helpers that catch -- mostly only enabled when exceptions are enabled
//*****************************************************************************
// Win32ErrorFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally
// it re-throws and catches the exception to convert it to a WIN32 error code. If an exception is of an unrecognized type
// the function will fail fast.
//
// try
// {
// // Code
// }
// catch (...)
// {
// status = wil::Win32ErrorFromCaughtException();
// }
_Always_(_Post_satisfies_(return > 0))
__declspec(noinline) inline long Win32ErrorFromCaughtException() WI_NOEXCEPT
{
return __WIN32_FROM_HRESULT(ResultFromCaughtException());
}
namespace details::__R_NS_NAME
{
#ifdef WIL_ENABLE_EXCEPTIONS
__R_DIRECT_METHOD(long, Win32_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
{
__R_FN_LOCALS;
return __WIN32_FROM_HRESULT(wil::details::ReportFailure_CaughtException<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY));
}
#endif
__R_DIRECT_METHOD(long, Win32_Return_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
{
__R_FN_LOCALS;
return __WIN32_FROM_HRESULT(wil::details::ReportFailure_GetLastErrorHr<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY));
}
}
}
#endif // __WIL_WIN32_RESULTMACROS_INCLUDED

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,571 @@
// -*- C++ -*-
//===--------------------------- __config ---------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// STL common functionality
//
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless
// of whether exceptions are enabled in the component. Common library code that expects to be used
// from exception-free components want these concepts, but including STL headers directly introduces
// friction as it requires components not using STL to declare their STL version. Doing so creates
// ambiguity around whether STL use is safe in a particular component and implicitly brings in
// a long list of headers (including <new>) which can create further ambiguity around throwing new
// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has
// the potential to create naming conflicts or other implied dependencies.
//
// To promote the use of these core language concepts outside of STL-based binaries, this file is
// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding
// "std::" namespace STL functions and types should be preferred over these in code that is bound to
// STL. The implementation and naming of all functions are taken directly from STL, instead using
// "wistd" (Windows Implementation std) as the namespace.
//
// Routines in this namespace should always be considered a reflection of the *current* STL implementation
// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here.
//
// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation.
// Only code that is not exception-based and libraries that expect to be utilized across both exception
// and non-exception based code should utilize this functionality.
// This header mimics libc++'s '__config' header to the extent necessary to get the wistd::* definitions compiling. Note
// that this has a few key differences since libc++'s MSVC compatability is currently not functional and a bit behind
#ifndef _WISTD_CONFIG_H_
#define _WISTD_CONFIG_H_
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
#include <cstddef> // For size_t and other necessary types
/// @cond
#if defined(_MSC_VER) && !defined(__clang__)
# if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# define __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
# endif
#endif
#ifndef __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
#pragma GCC system_header
#endif
#ifdef __GNUC__
# define __WI_GNUC_VER (__GNUC__ * 100 + __GNUC_MINOR__)
// The __WI_GNUC_VER_NEW macro better represents the new GCC versioning scheme
// introduced in GCC 5.0.
# define __WI_GNUC_VER_NEW (__WI_GNUC_VER * 10 + __GNUC_PATCHLEVEL__)
#else
# define __WI_GNUC_VER 0
# define __WI_GNUC_VER_NEW 0
#endif
// _MSVC_LANG is the more accurate way to get the C++ version in MSVC
#if defined(_MSVC_LANG) && (_MSVC_LANG > __cplusplus)
#define __WI_CPLUSPLUS _MSVC_LANG
#else
#define __WI_CPLUSPLUS __cplusplus
#endif
#ifndef __WI_LIBCPP_STD_VER
# if __WI_CPLUSPLUS <= 201103L
# define __WI_LIBCPP_STD_VER 11
# elif __WI_CPLUSPLUS <= 201402L
# define __WI_LIBCPP_STD_VER 14
# elif __WI_CPLUSPLUS <= 201703L
# define __WI_LIBCPP_STD_VER 17
# else
# define __WI_LIBCPP_STD_VER 18 // current year, or date of c++2a ratification
# endif
#endif // __WI_LIBCPP_STD_VER
#if __WI_CPLUSPLUS < 201103L
#define __WI_LIBCPP_CXX03_LANG
#endif
#if defined(__ELF__)
# define __WI_LIBCPP_OBJECT_FORMAT_ELF 1
#elif defined(__MACH__)
# define __WI_LIBCPP_OBJECT_FORMAT_MACHO 1
#elif defined(_WIN32)
# define __WI_LIBCPP_OBJECT_FORMAT_COFF 1
#elif defined(__wasm__)
# define __WI_LIBCPP_OBJECT_FORMAT_WASM 1
#else
# error Unknown object file format
#endif
#if defined(__clang__)
# define __WI_LIBCPP_COMPILER_CLANG
#elif defined(__GNUC__)
# define __WI_LIBCPP_COMPILER_GCC
#elif defined(_MSC_VER)
# define __WI_LIBCPP_COMPILER_MSVC
#elif defined(__IBMCPP__)
# define __WI_LIBCPP_COMPILER_IBM
#endif
#if defined(__WI_LIBCPP_COMPILER_MSVC)
#define __WI_PUSH_WARNINGS __pragma(warning(push))
#define __WI_POP_WARNINGS __pragma(warning(pop))
#elif defined(__WI_LIBCPP_COMPILER_CLANG)
#define __WI_PUSH_WARNINGS __pragma(clang diagnostic push)
#define __WI_POP_WARNINGS __pragma(clang diagnostic pop)
#else
#define __WI_PUSH_WARNINGS
#define __WI_POP_WARNINGS
#endif
#ifdef __WI_LIBCPP_COMPILER_MSVC
#define __WI_MSVC_DISABLE_WARNING(id) __pragma(warning(disable: id))
#else
#define __WI_MSVC_DISABLE_WARNING(id)
#endif
#ifdef __WI_LIBCPP_COMPILER_CLANG
#define __WI_CLANG_DISABLE_WARNING(warning) __pragma(clang diagnostic ignored #warning)
#else
#define __WI_CLANG_DISABLE_WARNING(warning)
#endif
// NOTE: MSVC, which is what we primarily target, is severly underrepresented in libc++ and checks such as
// __has_feature(...) are always false for MSVC, even when the feature being tested _is_ present in MSVC. Therefore, we
// instead modify all checks to be __WI_HAS_FEATURE_IS_UNION, etc., which provides the correct value for MSVC and falls
// back to the __has_feature(...), etc. value otherwise. We intentionally leave '__has_feature', etc. undefined for MSVC
// so that we don't accidentally use the incorrect behavior
#ifndef __WI_LIBCPP_COMPILER_MSVC
#ifndef __has_feature
#define __has_feature(__x) 0
#endif
// '__is_identifier' returns '0' if '__x' is a reserved identifier provided by
// the compiler and '1' otherwise.
#ifndef __is_identifier
#define __is_identifier(__x) 1
#endif
#ifndef __has_cpp_attribute
#define __has_cpp_attribute(__x) 0
#endif
#ifndef __has_attribute
#define __has_attribute(__x) 0
#endif
#ifndef __has_builtin
#define __has_builtin(__x) 0
#endif
#if __has_feature(cxx_alignas)
# define __WI_ALIGNAS_TYPE(x) alignas(x)
# define __WI_ALIGNAS(x) alignas(x)
#else
# define __WI_ALIGNAS_TYPE(x) __attribute__((__aligned__(__alignof(x))))
# define __WI_ALIGNAS(x) __attribute__((__aligned__(x)))
#endif
#if __has_feature(cxx_explicit_conversions) || defined(__IBMCPP__) || \
(!defined(__WI_LIBCPP_CXX03_LANG) && defined(__GNUC__)) // All supported GCC versions
# define __WI_LIBCPP_EXPLICIT explicit
#else
# define __WI_LIBCPP_EXPLICIT
#endif
#if __has_feature(cxx_attributes)
# define __WI_LIBCPP_NORETURN [[noreturn]]
#else
# define __WI_LIBCPP_NORETURN __attribute__ ((noreturn))
#endif
#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
// The __WI_LIBCPP_NODISCARD_ATTRIBUTE should only be used to define other
// NODISCARD macros to the correct attribute.
#if __has_cpp_attribute(nodiscard)
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]]
#elif defined(__WI_LIBCPP_COMPILER_CLANG) && !defined(__WI_LIBCPP_CXX03_LANG)
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[clang::warn_unused_result]]
#else
// We can't use GCC's [[gnu::warn_unused_result]] and
// __attribute__((warn_unused_result)), because GCC does not silence them via
// (void) cast.
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE
#endif
#define __WI_HAS_FEATURE_IS_UNION __has_feature(is_union)
#define __WI_HAS_FEATURE_IS_CLASS __has_feature(is_class)
#define __WI_HAS_FEATURE_IS_ENUM __has_feature(is_enum)
#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO __has_feature(is_convertible_to)
#define __WI_HAS_FEATURE_IS_EMPTY __has_feature(is_empty)
#define __WI_HAS_FEATURE_IS_POLYMORPHIC __has_feature(is_polymorphic)
#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR __has_feature(has_virtual_destructor)
#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS __has_feature(cxx_reference_qualified_functions)
#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE __has_feature(is_constructible)
#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE __has_feature(is_trivially_constructible)
#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE __has_feature(is_trivially_assignable)
#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR __has_feature(has_trivial_destructor)
#define __WI_HAS_FEATURE_NOEXCEPT __has_feature(cxx_noexcept)
#define __WI_HAS_FEATURE_IS_POD __has_feature(is_pod)
#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT __has_feature(is_standard_layout)
#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE __has_feature(is_trivially_copyable)
#define __WI_HAS_FEATURE_IS_TRIVIAL __has_feature(is_trivial)
#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR __has_feature(has_trivial_constructor) || (__WI_GNUC_VER >= 403)
#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR __has_feature(has_nothrow_constructor) || (__WI_GNUC_VER >= 403)
#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY __has_feature(has_nothrow_copy) || (__WI_GNUC_VER >= 403)
#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN __has_feature(has_nothrow_assign) || (__WI_GNUC_VER >= 403)
#if !(__has_feature(cxx_noexcept))
#define __WI_LIBCPP_HAS_NO_NOEXCEPT
#endif
#if !__is_identifier(__has_unique_object_representations) || __WI_GNUC_VER >= 700
#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS
#endif
#if !(__has_feature(cxx_variadic_templates))
#define __WI_LIBCPP_HAS_NO_VARIADICS
#endif
#if __has_feature(is_literal) || __WI_GNUC_VER >= 407
#define __WI_LIBCPP_IS_LITERAL(T) __is_literal(T)
#endif
#if __has_feature(underlying_type) || __WI_GNUC_VER >= 407
#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T)
#endif
#if __has_feature(is_final) || __WI_GNUC_VER >= 407
#define __WI_LIBCPP_HAS_IS_FINAL
#endif
#if __has_feature(is_base_of) || defined(__GNUC__) && __WI_GNUC_VER >= 403
#define __WI_LIBCPP_HAS_IS_BASE_OF
#endif
#if __is_identifier(__is_aggregate) && (__WI_GNUC_VER_NEW < 7001)
#define __WI_LIBCPP_HAS_NO_IS_AGGREGATE
#endif
#if !(__has_feature(cxx_rtti)) && !defined(__WI_LIBCPP_NO_RTTI)
#define __WI_LIBCPP_NO_RTTI
#endif
#if !(__has_feature(cxx_variable_templates))
#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES
#endif
#if !(__has_feature(cxx_relaxed_constexpr))
#define __WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR
#endif
#if !__has_builtin(__builtin_addressof) && _GNUC_VER < 700
#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF
#endif
#if __has_attribute(__no_sanitize__) && !defined(__WI_LIBCPP_COMPILER_GCC)
# define __WI_LIBCPP_NO_CFI __attribute__((__no_sanitize__("cfi")))
#else
# define __WI_LIBCPP_NO_CFI
#endif
#define __WI_LIBCPP_ALWAYS_INLINE __attribute__ ((__always_inline__))
#if __has_attribute(internal_linkage)
# define __WI_LIBCPP_INTERNAL_LINKAGE __attribute__ ((internal_linkage))
#else
# define __WI_LIBCPP_INTERNAL_LINKAGE __WI_LIBCPP_ALWAYS_INLINE
#endif
#else
// NOTE: Much of the following assumes a decently recent version of MSVC. Past versions can be supported, but will need
// to be updated to contain the proper _MSC_VER check
#define __WI_ALIGNAS_TYPE(x) alignas(x)
#define __WI_ALIGNAS(x) alignas(x)
#define __alignof__ __alignof
#define __WI_LIBCPP_EXPLICIT explicit
#define __WI_LIBCPP_NORETURN [[noreturn]]
#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495))
#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439))
#if __WI_LIBCPP_STD_VER > 14
#define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]]
#else
#define __WI_LIBCPP_NODISCARD_ATTRIBUTE _Check_return_
#endif
#define __WI_HAS_FEATURE_IS_UNION 1
#define __WI_HAS_FEATURE_IS_CLASS 1
#define __WI_HAS_FEATURE_IS_ENUM 1
#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO 1
#define __WI_HAS_FEATURE_IS_EMPTY 1
#define __WI_HAS_FEATURE_IS_POLYMORPHIC 1
#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR 1
#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1
#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS 1
#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE 1
#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE 1
#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE 1
#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR 1
#define __WI_HAS_FEATURE_NOEXCEPT 1
#define __WI_HAS_FEATURE_IS_POD 1
#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT 1
#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE 1
#define __WI_HAS_FEATURE_IS_TRIVIAL 1
#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR 1
#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR 1
#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY 1
#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN 1
#define __WI_HAS_FEATURE_IS_DESTRUCTIBLE 1
#if !defined(_CPPRTTI) && !defined(__WI_LIBCPP_NO_RTTI)
#define __WI_LIBCPP_NO_RTTI
#endif
#define __WI_LIBCPP_IS_LITERAL(T) __is_literal_type(T)
#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T)
#define __WI_LIBCPP_HAS_IS_FINAL
#define __WI_LIBCPP_HAS_IS_BASE_OF
#if __WI_LIBCPP_STD_VER < 14
#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES
#endif
#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF
#define __WI_LIBCPP_NO_CFI
#define __WI_LIBCPP_ALWAYS_INLINE __forceinline
#define __WI_LIBCPP_INTERNAL_LINKAGE
#endif
#ifndef _WIN32
#ifdef __LITTLE_ENDIAN__
# if __LITTLE_ENDIAN__
# define __WI_LIBCPP_LITTLE_ENDIAN
# endif // __LITTLE_ENDIAN__
#endif // __LITTLE_ENDIAN__
#ifdef __BIG_ENDIAN__
# if __BIG_ENDIAN__
# define __WI_LIBCPP_BIG_ENDIAN
# endif // __BIG_ENDIAN__
#endif // __BIG_ENDIAN__
#ifdef __BYTE_ORDER__
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define __WI_LIBCPP_LITTLE_ENDIAN
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define __WI_LIBCPP_BIG_ENDIAN
# endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#endif // __BYTE_ORDER__
#if !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN)
# include <endian.h>
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define __WI_LIBCPP_LITTLE_ENDIAN
# elif __BYTE_ORDER == __BIG_ENDIAN
# define __WI_LIBCPP_BIG_ENDIAN
# else // __BYTE_ORDER == __BIG_ENDIAN
# error unable to determine endian
# endif
#endif // !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN)
#else // _WIN32
#define __WI_LIBCPP_LITTLE_ENDIAN
#endif // _WIN32
#ifdef __WI_LIBCPP_HAS_NO_CONSTEXPR
# define __WI_LIBCPP_CONSTEXPR
#else
# define __WI_LIBCPP_CONSTEXPR constexpr
#endif
#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 constexpr
#else
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
#endif
#if __WI_LIBCPP_STD_VER > 14 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14 constexpr
#else
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14
#endif
#if __WI_LIBCPP_STD_VER > 17 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17 constexpr
#else
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17
#endif
#if !defined(__WI_LIBCPP_DISABLE_NODISCARD_AFTER_CXX17) && \
(__WI_LIBCPP_STD_VER > 17 || defined(__WI_LIBCPP_ENABLE_NODISCARD))
# define __WI_LIBCPP_NODISCARD_AFTER_CXX17 __WI_LIBCPP_NODISCARD_ATTRIBUTE
#else
# define __WI_LIBCPP_NODISCARD_AFTER_CXX17
#endif
#if __WI_LIBCPP_STD_VER > 14 && defined(__cpp_inline_variables) && (__cpp_inline_variables >= 201606L)
# define __WI_LIBCPP_INLINE_VAR inline
#else
# define __WI_LIBCPP_INLINE_VAR
#endif
#ifdef __WI_LIBCPP_CXX03_LANG
#define __WI_LIBCPP_HAS_NO_UNICODE_CHARS
#define __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES
#endif
#ifndef __SIZEOF_INT128__
#define __WI_LIBCPP_HAS_NO_INT128
#endif
#if !__WI_HAS_FEATURE_NOEXCEPT && !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT)
#define __WI_LIBCPP_HAS_NO_NOEXCEPT
#endif
#ifndef __WI_LIBCPP_HAS_NO_NOEXCEPT
# define WI_NOEXCEPT noexcept
# define __WI_NOEXCEPT_(x) noexcept(x)
#else
# define WI_NOEXCEPT throw()
# define __WI_NOEXCEPT_(x)
#endif
#if defined(__WI_LIBCPP_OBJECT_FORMAT_COFF)
#define __WI_LIBCPP_HIDDEN
#define __WI_LIBCPP_TEMPLATE_VIS
#endif // defined(__WI_LIBCPP_OBJECT_FORMAT_COFF)
#ifndef __WI_LIBCPP_HIDDEN
# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS)
# define __WI_LIBCPP_HIDDEN __attribute__ ((__visibility__("hidden")))
# else
# define __WI_LIBCPP_HIDDEN
# endif
#endif
#ifndef __WI_LIBCPP_TEMPLATE_VIS
# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS) && !defined(__WI_LIBCPP_COMPILER_MSVC)
# if __has_attribute(__type_visibility__)
# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__type_visibility__("default")))
# else
# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__visibility__("default")))
# endif
# else
# define __WI_LIBCPP_TEMPLATE_VIS
# endif
#endif
#define __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_HIDDEN __WI_LIBCPP_INTERNAL_LINKAGE
namespace wistd // ("Windows Implementation" std)
{
using nullptr_t = decltype(__nullptr);
template <class _T1, class _T2 = _T1>
struct __less
{
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T2& __y) const {return __x < __y;}
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T2& __x, const _T1& __y) const {return __x < __y;}
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T2& __x, const _T2& __y) const {return __x < __y;}
};
template <class _T1>
struct __less<_T1, _T1>
{
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
};
template <class _T1>
struct __less<const _T1, _T1>
{
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
};
template <class _T1>
struct __less<_T1, const _T1>
{
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
};
// These are added to wistd to enable use of min/max without having to use the windows.h min/max
// macros that some clients might not have access to. Note: the STL versions of these have debug
// checking for the less than operator and support for iterators that these implementations lack.
// Use the STL versions when you require use of those features.
// min
template <class _Tp, class _Compare>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(min)(const _Tp& __a, const _Tp& __b, _Compare __comp)
{
return __comp(__b, __a) ? __b : __a;
}
template <class _Tp>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(min)(const _Tp& __a, const _Tp& __b)
{
return (min)(__a, __b, __less<_Tp>());
}
// max
template <class _Tp, class _Compare>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(max)(const _Tp& __a, const _Tp& __b, _Compare __comp)
{
return __comp(__a, __b) ? __b : __a;
}
template <class _Tp>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(max)(const _Tp& __a, const _Tp& __b)
{
return (max)(__a, __b, __less<_Tp>());
}
template <class _Arg, class _Result>
struct __WI_LIBCPP_TEMPLATE_VIS unary_function
{
using argument_type = _Arg;
using result_type = _Result;
};
template <class _Arg1, class _Arg2, class _Result>
struct __WI_LIBCPP_TEMPLATE_VIS binary_function
{
using first_argument_type = _Arg1;
using second_argument_type = _Arg2;
using result_type = _Result;
};
}
/// @endcond
#endif // _WISTD_CONFIG_H_

View file

@ -0,0 +1,548 @@
// -*- C++ -*-
//===------------------------ functional ----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// STL common functionality
//
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless
// of whether exceptions are enabled in the component. Common library code that expects to be used
// from exception-free components want these concepts, but including STL headers directly introduces
// friction as it requires components not using STL to declare their STL version. Doing so creates
// ambiguity around whether STL use is safe in a particular component and implicitly brings in
// a long list of headers (including <new>) which can create further ambiguity around throwing new
// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has
// the potential to create naming conflicts or other implied dependencies.
//
// To promote the use of these core language concepts outside of STL-based binaries, this file is
// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding
// "std::" namespace STL functions and types should be preferred over these in code that is bound to
// STL. The implementation and naming of all functions are taken directly from STL, instead using
// "wistd" (Windows Implementation std) as the namespace.
//
// Routines in this namespace should always be considered a reflection of the *current* STL implementation
// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here.
//
// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation.
// Only code that is not exception-based and libraries that expect to be utilized across both exception
// and non-exception based code should utilize this functionality.
#ifndef _WISTD_FUNCTIONAL_H_
#define _WISTD_FUNCTIONAL_H_
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
#include "wistd_memory.h"
#include <intrin.h> // For __fastfail
#include <new.h> // For placement new
#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif
#pragma warning(push)
#pragma warning(disable: 4324)
#pragma warning(disable: 4800)
/// @cond
namespace wistd // ("Windows Implementation" std)
{
// wistd::function
//
// All of the code below is in direct support of wistd::function. This class is identical to std::function
// with the following exceptions:
//
// 1) It never allocates and is safe to use from exception-free code (custom allocators are not supported)
// 2) It's slightly bigger on the stack (64 bytes, rather than 24 for 32bit)
// 3) There is an explicit static-assert if a lambda becomes too large to hold in the internal buffer (rather than an allocation)
template <class _Ret>
struct __invoke_void_return_wrapper
{
#ifndef __WI_LIBCPP_CXX03_LANG
template <class ..._Args>
static _Ret __call(_Args&&... __args) {
return __invoke(wistd::forward<_Args>(__args)...);
}
#else
template <class _Fn>
static _Ret __call(_Fn __f) {
return __invoke(__f);
}
template <class _Fn, class _A0>
static _Ret __call(_Fn __f, _A0& __a0) {
return __invoke(__f, __a0);
}
template <class _Fn, class _A0, class _A1>
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1) {
return __invoke(__f, __a0, __a1);
}
template <class _Fn, class _A0, class _A1, class _A2>
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2){
return __invoke(__f, __a0, __a1, __a2);
}
#endif
};
template <>
struct __invoke_void_return_wrapper<void>
{
#ifndef __WI_LIBCPP_CXX03_LANG
template <class ..._Args>
static void __call(_Args&&... __args) {
(void)__invoke(wistd::forward<_Args>(__args)...);
}
#else
template <class _Fn>
static void __call(_Fn __f) {
__invoke(__f);
}
template <class _Fn, class _A0>
static void __call(_Fn __f, _A0& __a0) {
__invoke(__f, __a0);
}
template <class _Fn, class _A0, class _A1>
static void __call(_Fn __f, _A0& __a0, _A1& __a1) {
__invoke(__f, __a0, __a1);
}
template <class _Fn, class _A0, class _A1, class _A2>
static void __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2) {
__invoke(__f, __a0, __a1, __a2);
}
#endif
};
////////////////////////////////////////////////////////////////////////////////
// FUNCTION
//==============================================================================
// bad_function_call
__WI_LIBCPP_NORETURN inline __WI_LIBCPP_INLINE_VISIBILITY
void __throw_bad_function_call()
{
__fastfail(7); // FAST_FAIL_FATAL_APP_EXIT
}
template<class _Fp> class __WI_LIBCPP_TEMPLATE_VIS function; // undefined
namespace __function
{
template<class _Rp>
struct __maybe_derive_from_unary_function
{
};
template<class _Rp, class _A1>
struct __maybe_derive_from_unary_function<_Rp(_A1)>
: public unary_function<_A1, _Rp>
{
};
template<class _Rp>
struct __maybe_derive_from_binary_function
{
};
template<class _Rp, class _A1, class _A2>
struct __maybe_derive_from_binary_function<_Rp(_A1, _A2)>
: public binary_function<_A1, _A2, _Rp>
{
};
template <class _Fp>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(_Fp const&) { return true; }
template <class _Fp>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(_Fp* __ptr) { return __ptr; }
template <class _Ret, class _Class>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(_Ret _Class::*__ptr) { return __ptr; }
template <class _Fp>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(function<_Fp> const& __f) { return !!__f; }
} // namespace __function
#ifndef __WI_LIBCPP_CXX03_LANG
namespace __function {
template<class _Fp> class __base;
template<class _Rp, class ..._ArgTypes>
class __base<_Rp(_ArgTypes...)>
{
__base(const __base&);
__base& operator=(const __base&);
public:
__WI_LIBCPP_INLINE_VISIBILITY __base() {}
__WI_LIBCPP_INLINE_VISIBILITY virtual ~__base() {}
virtual void __clone(__base*) const = 0;
virtual void __move(__base*) = 0;
virtual void destroy() WI_NOEXCEPT = 0;
virtual _Rp operator()(_ArgTypes&& ...) = 0;
};
template<class _FD, class _FB> class __func;
template<class _Fp, class _Rp, class ..._ArgTypes>
class __func<_Fp, _Rp(_ArgTypes...)>
: public __base<_Rp(_ArgTypes...)>
{
_Fp __f_;
public:
__WI_LIBCPP_INLINE_VISIBILITY
explicit __func(_Fp&& __f)
: __f_(wistd::move(__f)) {}
__WI_LIBCPP_INLINE_VISIBILITY
explicit __func(const _Fp& __f)
: __f_(__f) {}
virtual void __clone(__base<_Rp(_ArgTypes...)>*) const;
virtual void __move(__base<_Rp(_ArgTypes...)>*);
virtual void destroy() WI_NOEXCEPT;
virtual _Rp operator()(_ArgTypes&& ... __arg);
};
template<class _Fp, class _Rp, class ..._ArgTypes>
void
__func<_Fp, _Rp(_ArgTypes...)>::__clone(__base<_Rp(_ArgTypes...)>* __p) const
{
::new (__p) __func(__f_);
}
template<class _Fp, class _Rp, class ..._ArgTypes>
void
__func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p)
{
::new (__p) __func(wistd::move(__f_));
}
template<class _Fp, class _Rp, class ..._ArgTypes>
void
__func<_Fp, _Rp(_ArgTypes...)>::destroy() WI_NOEXCEPT
{
__f_.~_Fp();
}
template<class _Fp, class _Rp, class ..._ArgTypes>
_Rp
__func<_Fp, _Rp(_ArgTypes...)>::operator()(_ArgTypes&& ... __arg)
{
typedef __invoke_void_return_wrapper<_Rp> _Invoker;
return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...);
}
// 'wistd::function' is most similar to 'inplace_function' in that it _only_ permits holding function objects
// that can fit within its internal buffer. Therefore, we expand this size to accommodate space for at least 12
// pointers (__base vtable takes an additional one).
constexpr const size_t __buffer_size = 13 * sizeof(void*);
} // __function
// NOTE: The extra 'alignas' here is to work around the x86 compiler bug mentioned in
// https://github.com/microsoft/STL/issues/1533 to force alignment on the stack
template<class _Rp, class ..._ArgTypes>
class __WI_LIBCPP_TEMPLATE_VIS __WI_ALIGNAS(typename aligned_storage<__function::__buffer_size>::type)
function<_Rp(_ArgTypes...)>
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
{
using __base = __function::__base<_Rp(_ArgTypes...)>;
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
typename aligned_storage<__function::__buffer_size>::type __buf_;
__base* __f_;
__WI_LIBCPP_NO_CFI static __base *__as_base(void *p) {
return static_cast<__base*>(p);
}
template <class _Fp, bool>
struct __callable_imp
{
static const bool value = is_same<void, _Rp>::value ||
is_convertible<typename __invoke_of<_Fp&, _ArgTypes...>::type,
_Rp>::value;
};
template <class _Fp>
struct __callable_imp<_Fp, false>
{
static constexpr bool value = false;
};
template <class _Fp>
struct __callable
{
static const bool value = __callable_imp<_Fp, __lazy_and<
integral_constant<bool, !is_same<__uncvref_t<_Fp>, function>::value>,
__invokable<_Fp&, _ArgTypes...>
>::value>::value;
};
template <class _Fp>
using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type;
public:
using result_type = _Rp;
// construct/copy/destroy:
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function() WI_NOEXCEPT : __f_(0) {}
__WI_LIBCPP_INLINE_VISIBILITY
function(nullptr_t) WI_NOEXCEPT : __f_(0) {}
function(const function&);
function(function&&);
template<class _Fp, class = _EnableIfCallable<_Fp>>
function(_Fp);
function& operator=(const function&);
function& operator=(function&&);
function& operator=(nullptr_t) WI_NOEXCEPT;
template<class _Fp, class = _EnableIfCallable<_Fp>>
function& operator=(_Fp&&);
~function();
// function modifiers:
void swap(function&);
// function capacity:
__WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY
__WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT {return __f_;}
// deleted overloads close possible hole in the type system
template<class _R2, class... _ArgTypes2>
bool operator==(const function<_R2(_ArgTypes2...)>&) const = delete;
template<class _R2, class... _ArgTypes2>
bool operator!=(const function<_R2(_ArgTypes2...)>&) const = delete;
public:
// function invocation:
_Rp operator()(_ArgTypes...) const;
// NOTE: type_info is very compiler specific, and on top of that, we're operating in a namespace other than
// 'std' so all functions requiring RTTI have been removed
};
template<class _Rp, class ..._ArgTypes>
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(const function& __f)
{
if (__f.__f_ == nullptr)
__f_ = 0;
else
{
__f_ = __as_base(&__buf_);
__f.__f_->__clone(__f_);
}
}
template<class _Rp, class ..._ArgTypes>
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(function&& __f)
{
if (__f.__f_ == nullptr)
__f_ = 0;
else
{
__f_ = __as_base(&__buf_);
__f.__f_->__move(__f_);
__f.__f_->destroy();
__f.__f_ = 0;
}
}
template<class _Rp, class ..._ArgTypes>
template <class _Fp, class>
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(_Fp __f)
: __f_(nullptr)
{
if (__function::__not_null(__f))
{
typedef __function::__func<_Fp, _Rp(_ArgTypes...)> _FF;
static_assert(sizeof(_FF) <= sizeof(__buf_),
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
__f_ = ::new(static_cast<void*>(&__buf_)) _FF(wistd::move(__f));
}
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(const function& __f)
{
*this = nullptr;
if (__f.__f_)
{
__f_ = __as_base(&__buf_);
__f.__f_->__clone(__f_);
}
return *this;
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(function&& __f)
{
*this = nullptr;
if (__f.__f_)
{
__f_ = __as_base(&__buf_);
__f.__f_->__move(__f_);
__f.__f_->destroy();
__f.__f_ = 0;
}
return *this;
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(nullptr_t) WI_NOEXCEPT
{
__base* __t = __f_;
__f_ = 0;
if (__t)
__t->destroy();
return *this;
}
template<class _Rp, class ..._ArgTypes>
template <class _Fp, class>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f)
{
*this = nullptr;
if (__function::__not_null(__f))
{
typedef __function::__func<typename decay<_Fp>::type, _Rp(_ArgTypes...)> _FF;
static_assert(sizeof(_FF) <= sizeof(__buf_),
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
__f_ = ::new(static_cast<void*>(&__buf_)) _FF(wistd::move(__f));
}
return *this;
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>::~function()
{
if (__f_)
__f_->destroy();
}
template<class _Rp, class ..._ArgTypes>
void
function<_Rp(_ArgTypes...)>::swap(function& __f)
{
if (wistd::addressof(__f) == this)
return;
if (__f_ && __f.__f_)
{
typename aligned_storage<sizeof(__buf_)>::type __tempbuf;
__base* __t = __as_base(&__tempbuf);
__f_->__move(__t);
__f_->destroy();
__f_ = 0;
__f.__f_->__move(__as_base(&__buf_));
__f.__f_->destroy();
__f.__f_ = 0;
__f_ = __as_base(&__buf_);
__t->__move(__as_base(&__f.__buf_));
__t->destroy();
__f.__f_ = __as_base(&__f.__buf_);
}
else if (__f_)
{
__f_->__move(__as_base(&__f.__buf_));
__f_->destroy();
__f_ = 0;
__f.__f_ = __as_base(&__f.__buf_);
}
else if (__f.__f_)
{
__f.__f_->__move(__as_base(&__buf_));
__f.__f_->destroy();
__f.__f_ = 0;
__f_ = __as_base(&__buf_);
}
}
template<class _Rp, class ..._ArgTypes>
_Rp
function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const
{
if (__f_ == nullptr)
__throw_bad_function_call();
return (*__f_)(wistd::forward<_ArgTypes>(__arg)...);
}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return !__f;}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator==(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return !__f;}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator!=(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return (bool)__f;}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator!=(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return (bool)__f;}
// Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
void
swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
{return __x.swap(__y);}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
void
swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
{return __x.swap(__y);}
// std::invoke
template <class _Fn, class ..._Args>
typename __invoke_of<_Fn, _Args...>::type
invoke(_Fn&& __f, _Args&&... __args)
__WI_NOEXCEPT_((__nothrow_invokable<_Fn, _Args...>::value))
{
return wistd::__invoke(wistd::forward<_Fn>(__f), wistd::forward<_Args>(__args)...);
}
#else // __WI_LIBCPP_CXX03_LANG
#error wistd::function and wistd::invoke not implemented for pre-C++11
#endif
}
/// @endcond
#pragma warning(pop)
#endif // _WISTD_FUNCTIONAL_H_

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,127 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WRL_INCLUDED
#define __WIL_WRL_INCLUDED
#include <wrl.h>
#include "result.h"
#include "common.h" // wistd type_traits helpers
#include <libloaderapi.h> // GetModuleHandleW
/// @cond
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
/// @endcond
namespace wil
{
#ifdef WIL_ENABLE_EXCEPTIONS
#pragma region Object construction helpers that throw exceptions
/** Used to construct a RuntimeClass based object that uses 2 phase construction.
Construct a RuntimeClass based object that uses 2 phase construction (by implementing
RuntimeClassInitialize() and returning error codes for failures.
~~~~
// SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize()
auto someClass = MakeAndInitializeOrThrow<SomeClass>(L"input", true);
~~~~ */
template <typename T, typename... TArgs>
Microsoft::WRL::ComPtr<T> MakeAndInitializeOrThrow(TArgs&&... args)
{
Microsoft::WRL::ComPtr<T> obj;
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<T>(&obj, Microsoft::WRL::Details::Forward<TArgs>(args)...));
return obj;
}
/** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does
not require 2 phase construction).
~~~~
// SomeClass uses exceptions for error handling in its constructor.
auto someClass = MakeOrThrow<SomeClass>(L"input", true);
~~~~ */
template <typename T, typename... TArgs>
Microsoft::WRL::ComPtr<T> MakeOrThrow(TArgs&&... args)
{
// This is how you can detect the presence of RuntimeClassInitialize() and find dangerous use.
// Unfortunately this produces false positives as all RuntimeClass derived classes have
// a RuntimeClassInitialize() method from their base class.
// static_assert(!std::is_member_function_pointer<decltype(&T::RuntimeClassInitialize)>::value,
// "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead");
auto obj = Microsoft::WRL::Make<T>(Microsoft::WRL::Details::Forward<TArgs>(args)...);
THROW_IF_NULL_ALLOC(obj.Get());
return obj;
}
#pragma endregion
#endif // WIL_ENABLE_EXCEPTIONS
/** By default WRL Callback objects are not agile, use this to make an agile one. Replace use of Callback<> with MakeAgileCallback<>.
Will return null on failure, translate that into E_OUTOFMEMORY using XXX_IF_NULL_ALLOC()
from wil\result.h to test the result. */
template<typename TDelegateInterface, typename ...Args>
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT
{
using namespace Microsoft::WRL;
return Callback<Implements<RuntimeClassFlags<ClassicCom>, TDelegateInterface, FtmBase>>(wistd::forward<Args>(args)...);
}
#ifdef WIL_ENABLE_EXCEPTIONS
template<typename TDelegateInterface, typename ...Args>
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallback(Args&&... args)
{
auto result = MakeAgileCallbackNoThrow<TDelegateInterface, Args...>(wistd::forward<Args>(args)...);
THROW_IF_NULL_ALLOC(result);
return result;
}
#endif // WIL_ENABLE_EXCEPTIONS
/** Holds a reference to the host WRL module to prevent it from being unloaded.
Normally, the reference is held implicitly because you are a member function
of a DLL-hosted COM object, or because you retain a strong reference
to some DLL-hosted COM object, but if those do not apply to you, then you
will need to hold a reference explicitly. For examples (and for the C++/WinRT
equivalent), see winrt_module_reference.
*/
struct [[nodiscard]] wrl_module_reference
{
wrl_module_reference()
{
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
{
modulePtr->IncrementObjectCount();
}
else
{
#ifdef GET_MODULE_HANDLE_EX_FLAG_PIN
// If this assertion fails, then you are using wrl_module_reference
// from a DLL that does not host WRL objects, and the module reference
// has no effect.
WI_ASSERT(reinterpret_cast<HMODULE>(&__ImageBase) == GetModuleHandleW(nullptr));
#endif
}
}
wrl_module_reference(wrl_module_reference const&) : wrl_module_reference() {}
~wrl_module_reference()
{
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
{
modulePtr->DecrementObjectCount();
}
}
};
} // namespace wil
#endif // __WIL_WRL_INCLUDED

View file

@ -0,0 +1,216 @@
`**********************************************************************`
`* This is an include template file for the tracewpp preprocessor. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// template `TemplateFile`
//
// Defines a set of macro that expand control model specified
// with WPP_CONTROL_GUIDS (example shown below)
// into an enum of trace levels and required structures that
// contain the mask of levels, logger handle and some information
// required for registration.
//
#ifndef WPP_ALREADY_INCLUDED
#define WPP_EVAL(x) x
#define WPP_STR(x) #x
#define WPP_STRINGIZE(x) WPP_STR(x)
#define WPP_GLUE(a, b) a ## b
#define WPP_GLUE3(a, b, c) a ## b ## c
#define WPP_GLUE4(a, b, c, d) a ## b ## c ## d
#define WPP_XGLUE(a, b) WPP_GLUE(a, b)
#define WPP_XGLUE3(a, b, c) WPP_GLUE3(a, b, c)
#define WPP_XGLUE4(a, b, c, d) WPP_GLUE4(a, b, c, d)
///////////////////////////////////////////////////////////////////////////////////
//
// #define WPP_CONTROL_GUIDS \
// WPP_DEFINE_CONTROL_GUID(Regular,(81b20fea,73a8,4b62,95bc,354477c97a6f), \
// WPP_DEFINE_BIT(Error) \
// WPP_DEFINE_BIT(Unusual) \
// WPP_DEFINE_BIT(Noise) \
// ) \
// WPP_DEFINE_CONTROL_GUID(HiFreq,(91b20fea,73a8,4b62,95bc,354477c97a6f), \
// WPP_DEFINE_BIT(Entry) \
// WPP_DEFINE_BIT(Exit) \
// WPP_DEFINE_BIT(ApiCalls) \
// WPP_DEFINE_BIT(RandomJunk) \
// WPP_DEFINE_BIT(LovePoem) \
// )
#ifdef __cplusplus
extern "C" {
#endif
#ifndef WPP_NO_CONTROL_GUIDS
#ifdef WPP_DEFAULT_CONTROL_GUID
# ifdef WPP_CONTROL_GUIDS
# error WPP_DEFAULT_CONTROL_GUID cannot be used together with WPP_CONTROL_GUIDS.
# else // WPP_CONTROL_GUIDS
# define WPP_CONTROL_GUIDS \
WPP_DEFINE_CONTROL_GUID(Default,(WPP_DEFAULT_CONTROL_GUID), \
WPP_DEFINE_BIT(Error) \
WPP_DEFINE_BIT(Unusual) \
WPP_DEFINE_BIT(Noise) \
)
# endif // WPP_CONTROL_GUIDS
#endif // WPP_DEFAULT_CONTROL_GUID
#ifndef WPP_CONTROL_GUIDS
# pragma message(__FILE__ " : error : Please define control model via WPP_CONTROL_GUIDS or WPP_DEFAULT_CONTROL_GUID macros")
# pragma message(__FILE__ " : error : don't forget to call WPP_INIT_TRACING and WPP_CLEANUP in your main, DriverEntry or DllInit")
# pragma message(__FILE__ " : error : see tracewpp.doc for further information")
# error WPP_CONTROL_GUIDS not defined.
#endif // WPP_CONTROL_GUIDS
// a set of macro to convert a guid in a form x(81b20fea,73a8,4b62,95bc,354477c97a6f)
// into either a a struct or text string
#define _WPPW(x) WPP_GLUE(L, x)
#define WPP_GUID_NORM(l,w1,w2,w3,ll) l ## w1 ## w2 ## w3 ## ll
#define WPP_GUID_TEXT(l,w1,w2,w3,ll) #l "-" #w1 "-" #w2 "-" #w3 "-" #ll
#define WPP_GUID_WTEXT(l,w1,w2,w3,ll) _WPPW(#l) L"-" _WPPW(#w1) L"-" _WPPW(#w2) L"-" _WPPW(#w3) L"-" _WPPW(#ll)
#define WPP_EXTRACT_BYTE(val,n) (((ULONGLONG)(0x ## val) >> (8 * n)) & 0xFF)
#define WPP_GUID_STRUCT(l,w1,w2,w3,ll) {0x ## l, 0x ## w1, 0x ## w2,\
{WPP_EXTRACT_BYTE(w3, 1), WPP_EXTRACT_BYTE(w3, 0),\
WPP_EXTRACT_BYTE(ll, 5), WPP_EXTRACT_BYTE(ll, 4),\
WPP_EXTRACT_BYTE(ll, 3), WPP_EXTRACT_BYTE(ll, 2),\
WPP_EXTRACT_BYTE(ll, 1), WPP_EXTRACT_BYTE(ll, 0)} }
#ifndef WPP_FORCEINLINE
#define WPP_FORCEINLINE __forceinline
#endif
// define an enum of control block names
//////
#define WPP_DEFINE_CONTROL_GUID(Name,Guid,Bits) WPP_XGLUE(WPP_CTL_, WPP_EVAL(Name)),
enum WPP_CTL_NAMES { WPP_CONTROL_GUIDS WPP_LAST_CTL};
#undef WPP_DEFINE_CONTROL_GUID
// define control guids
//////
#define WPP_DEFINE_CONTROL_GUID(Name,Guid,Bits) \
extern __declspec(selectany) const GUID WPP_XGLUE4(WPP_, ThisDir, _CTLGUID_, WPP_EVAL(Name)) = WPP_GUID_STRUCT Guid;
WPP_CONTROL_GUIDS
#undef WPP_DEFINE_CONTROL_GUID
// define enums of individual bits
/////
#define WPP_DEFINE_CONTROL_GUID(Name,Guid,Bits) \
WPP_XGLUE(WPP_BLOCK_START_, WPP_EVAL(Name)) = WPP_XGLUE(WPP_CTL_, WPP_EVAL(Name)) * 0x10000, Bits WPP_XGLUE(WPP_BLOCK_END_, WPP_EVAL(Name)),
# define WPP_DEFINE_BIT(Name) WPP_BIT_ ## Name,
enum WPP_DEFINE_BIT_NAMES { WPP_CONTROL_GUIDS };
# undef WPP_DEFINE_BIT
#undef WPP_DEFINE_CONTROL_GUID
#define WPP_MASK(CTL) (1 << ( ((CTL)-1) & 31 ))
#define WPP_FLAG_NO(CTL) ( (0xFFFF & ((CTL)-1) ) / 32)
#define WPP_CTRL_NO(CTL) ((CTL) >> 16)
// calculate how many DWORDs we need to get the required number of bits
// upper estimate. Sometimes will be off by one
#define WPP_DEFINE_CONTROL_GUID(Name,Guid,Bits) | WPP_XGLUE(WPP_BLOCK_END_, WPP_EVAL(Name))
enum _WPP_FLAG_LEN_ENUM { WPP_FLAG_LEN = 1 | ((0 WPP_CONTROL_GUIDS) & 0xFFFF) / 32 };
#undef WPP_DEFINE_CONTROL_GUID
//
// Check that maximum number of flags does not exceed 32
//
#ifndef C_ASSERT
#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1]
#endif
#define MAX_NUMBER_OF_ETW_FLAGS 34 // 32 flags plus 2 separators
#define WPP_DEFINE_CONTROL_GUID(Name,Guid,Bits) && ((WPP_XGLUE(WPP_BLOCK_END_, WPP_EVAL(Name) & 0xFFFF)) < MAX_NUMBER_OF_ETW_FLAGS)
enum _WPP_FLAG_LEN_ENUM_MAX { WPP_MAX_FLAG_LEN_CHECK = (1 WPP_CONTROL_GUIDS) };
#undef WPP_DEFINE_CONTROL_GUID
#ifndef WPP_CB
#define WPP_CB WPP_GLOBAL_Control
#endif
#ifndef WPP_CB_TYPE
#define WPP_CB_TYPE WPP_PROJECT_CONTROL_BLOCK
#endif
#ifndef WPP_CHECK_INIT
#define WPP_CHECK_INIT (WPP_CB != (WPP_CB_TYPE*)&WPP_CB) &&
#endif
typedef union {
WPP_TRACE_CONTROL_BLOCK Control;
UCHAR ReserveSpace[ sizeof(WPP_TRACE_CONTROL_BLOCK) + sizeof(ULONG) * (WPP_FLAG_LEN - 1) ];
} WPP_CB_TYPE ;
extern __declspec(selectany) WPP_CB_TYPE *WPP_CB = (WPP_CB_TYPE*)&WPP_CB;
#if ENABLE_WPP_RECORDER
#ifndef WPP_RECORDER_CHECK_INIT
#define WPP_RECORDER_CHECK_INIT (WPP_RECORDER_INITIALIZED != (WPP_CB_TYPE*)&WPP_RECORDER_INITIALIZED) &&
#endif
// Global varaible used to track if WPP_RECORDER was initialized.
// It will be initialized on calling WPP_INIT_TRACING macro.
extern __declspec(selectany) WPP_CB_TYPE *WPP_RECORDER_INITIALIZED = (WPP_CB_TYPE*)&WPP_RECORDER_INITIALIZED;
#endif
#define WPP_CONTROL(CTL) (WPP_CB[WPP_CTRL_NO(CTL)].Control)
// Define the default WPP_LEVEL_LOGGER/WPP_LEVEL_ENABLED macros for the
// predefined DoTraceMessage(LEVEL) function.
#ifdef WPP_USE_TRACE_LEVELS
#ifndef WPP_LEVEL_LOGGER
#define WPP_LEVEL_LOGGER(lvl) (WPP_CONTROL(WPP_BIT_ ## DUMMY).Logger),
#endif
#ifndef WPP_LEVEL_ENABLED
#define WPP_LEVEL_ENABLED(lvl) (WPP_CONTROL(WPP_BIT_ ## DUMMY).Level >= lvl)
#endif
#else // WPP_USE_TRACE_LEVELS
// For historical reasons, the use of LEVEL means flags by default.
// This was a bad choice but very difficult to undo.
#ifndef WPP_LEVEL_LOGGER
# define WPP_LEVEL_LOGGER(CTL) (WPP_CONTROL(WPP_BIT_ ## CTL).Logger),
#endif
#ifndef WPP_LEVEL_ENABLED
# define WPP_LEVEL_ENABLED(CTL) (WPP_CONTROL(WPP_BIT_ ## CTL).Flags[WPP_FLAG_NO(WPP_BIT_ ## CTL)] & WPP_MASK(WPP_BIT_ ## CTL))
#endif
#endif // WPP_USE_TRACE_LEVELS
// Define default WPP_FLAG_LOGGER/WPP_FLAG_ENABLED macros for convenience in
// defining a function that takes a FLAG parameter e.g. DoTrace(FLAG).
#ifndef WPP_FLAG_LOGGER
# define WPP_FLAG_LOGGER(CTL) (WPP_CONTROL(WPP_BIT_ ## CTL).Logger),
#endif
#ifndef WPP_FLAG_ENABLED
# define WPP_FLAG_ENABLED(CTL) (WPP_CONTROL(WPP_BIT_ ## CTL).Flags[WPP_FLAG_NO(WPP_BIT_ ## CTL)] & WPP_MASK(WPP_BIT_ ## CTL))
#endif
#ifndef WPP_ENABLED
# define WPP_ENABLED() 1
#endif
#ifndef WPP_LOGGER
# define WPP_LOGGER() (WPP_CB[0].Control.Logger),
#endif
#endif // WPP_NO_CONTROL_GUIDS
#ifndef WPP_GET_LOGGER
# define WPP_GET_LOGGER Logger
#endif
#ifndef WPP_LOGGER_ARG
# define WPP_LOGGER_ARG TRACEHANDLE Logger,
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif // WPP_ALREADY_INCLUDED

View file

@ -0,0 +1,670 @@
#
# Default configuration options for tracewpp.
#
# This file provides configuration settings for tracewpp. By default, tracewpp
# uses "defaultwpp.ini" for configuration settings. An alternate INI file can
# be specified via the -defwpp parameter. Additional configuration settings can
# be specified via the -ini parameter or via "begin_wpp config" blocks in
# files specified via the -scan parameter.
#
# When parsing an INI file or a "begin_wpp config" block, tracewpp searches for
# syntax that looks like a function call, e.g. "FunctionName(arg1, ...);". For
# each function call, if the function name is one of the recognized commands
# described below (e.g. NOPREFIX, USEPREFIX, INCLUDE), that command is
# executed. Otherwise, the function call is treated as the definition of a
# trace function.
################################
# General configuration commands
################################
#
# FUNC FunctionName(ARG1, ARG2, MSG, ...);
# FUNC FunctionName{ARG1=value, ARG2=value}(ARG3, ARG4, MSG, ...);
#
# Defines a trace function and its arguments. The tracewpp tool will scan all
# source files for calls to the specified trace function. The generated TMH
# file will then define the specified name as a macro and will generate code
# to handle each call to the macro.
#
# Function definitions are often prefixed by "FUNC", e.g.
# "FUNC DoTraceMessage(LEVEL, MSG, ...);". The "FUNC" prefix is optional
# but is helpful in drawing attention to function definitions.
#
# Arguments provided in {braces} are defaulted parameters (sometimes called
# "assumed arguments"). If you define a trace function
# MyTrace{ARG1=5}(MSG, ...), then WPP will treat any use
# of MyTrace("...", a, b) as if it were MyTrace(5, "...", a, b), and WPP will
# generate macros for MyTrace that include an ARG1 parameter (for use by
# user-specified expressions in the macro definition).
#
# Note that when using TMF-based WPP, WPP will expect the user to define
# ENABLED and LOGGER macros based on the arguments for defined functions.
# For example, if you define a trace function
# MyTrace(FLAG, MYARG, MSG, ...), WPP will expect user-defined macros
# WPP_FLAG_MYARG_ENABLED(FLAG, MYARG) and WPP_FLAG_MYARG_LOGGER(FLAG, MYARG)
# to be provided by the user. These user-supplied macros are usually defined
# in terms of the corresponding WPP-supplied macros WPP_LEVEL_ENABLED(LEVEL)
# and WPP_LEVEL_LOGGER(LEVEL).
#
# Certain argument names trigger special behavior:
# - "MSG" is the format string.
# - "..." is a placeholder for the format string arguments. If present, it
# must be the last argument.
# - "PUBLIC_FILTER" marks the function as conditionally public. The function
# will be public if the WPP_PUBLIC_<Flag> macro is defined when the file is
# compiled (where <Flag> is the value of the PUBLIC_FILTER argument).
#
# Certain argument names trigger special behavior in manifest-based WPP:
# - "ControlGuid" specifies the name of the control GUID for the event. If
# there is no ControlGuid argument, tracewpp will determine the control
# GUID based on the event's level or keywords.
# - "Level" specifies the level of the event. If there is no Level argument,
# tracewpp will look for valid level names in all arguments of the event.
# If no level name is found, the level will default to "win:Verbose".
# - "Keywords" specifies the keywords (flags) of the event. If there is no
# Keywords argument, tracewpp will look for valid keyword names in all
# arguments of the event. If no keywords are found, the keyword will be
# "win:AnyKeyword" (i.e. 0).
# - "Name" specifies the event name.
# - "Id" allows manual assignment of event Id and Version attributes. Syntax
# is either "ID" or "IDvVERSION", e.g. "24" or "24v2". The specified Id
# must be a number from 0..65535 that is outside of the auto-assignment
# pool. Note that the auto-assignment pool defaults to 10..65535, so manual
# assignment generally requires adjusting the auto-assignment pool (via the
# TRACERANGE command).
# - "Func" specifies the name of the event's call site function. Note that
# when using manifest-based WPP, tracewpp is unable to automatically set
# the event's function name during manifest generation. The function name
# can be manually specified via the Func parameter or it can be included in
# the event payload via DEFAULT_FUNC_NAME_IN_EVENT/USE_FUNC_NAME_IN_EVENT.
#
# NOPREFIX(FunctionName);
#
# Removes the default trace message prefix "%!STDPREFIX!" for the specified
# trace function.
#
# USEPREFIX(FunctionName, [Prefix, ArgNames...]);
# USESUFFIX(FunctionName, [Suffix, ArgNames...]);
#
# Sets the trace message prefix or suffix for the specified trace function.
# Each time WPP sees function("message", ...), it will add the specified
# prefix or suffix to "message".
#
# PUBLIC_FILTER(FunctionName, Flag);
#
# Marks the named function as conditionally public. The function will be
# public if the WPP_PUBLIC_<Flag> macro is defined when the file is compiled.
# Public functions omit certain metadata from the TMF such as filename and
# line number. (Not applicable to manifest-based WPP.)
#
# INCLUDE(IniFileName);
#
# Immediately processes the specified file as an INI file.
#
# PROCESSFILES(SourceFile...);
#
# Processes the specified input file(s) as if they were specified on the
# tracewpp command line. Example: PROCESSFILES("MyFile.cpp", "B*.c");
#
# SCANFORMACROS(HeaderFile...);
#
# Scans the specified file(s) as if they were specified with
# /scan:HeaderFile on the tracewpp command line.
#
# SEPARATE_TRACE_GUID_PER_FILE(Enabled);
#
# If enabled (1), a new trace GUID will be generated for each input file
# scanned. This is useful for TMF-based WPP, as it means changes in one
# source file will not affect the trace GUID or event IDs of other source
# files. This is undesirable for manifest-based WPP, as it requires a
# large number of providers to be registered.
#
# This is enabled (1) by default for TMF-based WPP. This is disabled (0) by
# default for manifest-based WPP (when the /man parameter is specified).
#
# TRACERANGE(Min, Max);
#
# Configures the range of numbers that WPP will use when automatically
# assigning event IDs. Default is 10..65535. If manually assigning Ids, you
# will need to reduce this range to reserve Ids for manual assignment, e.g.
# use TRACERANGE(1000, 65535) so that Ids 1..999 can be manually assigned.
#
# WPP_FLAGS(Option...);
#
# Processes the specified options as if they were specified on the tracewpp
# command line. Example: WPP_FLAGS(noshrieks, p:MyProject);
################################
# Type definition commands
################################
#
# The following commands can be used to define data types for use with WPP
# trace functions. WPP type definitions may include the following settings:
#
# - Name: the WPP name of the type being defined. This name must be unique.
# When reading the WPP format strings from source code, WPP matches the
# requested type against this name. For example, "%d" will cause WPP to look
# for a type named "d", and "%!HRESULT!" will cause WPP to look for a type
# named "HRESULT".
# - EquivType: the name of the type in C or C++, e.g. "int". This type will be
# used for the parameter of the inline helper function in the generated
# code.
# - MofType: the MOF name of the type (shows up in the TMF file).
# - FormatSpec: the format string to use for this type in the message string.
# For example, if FormatSpec is "I64x" then a message string might look like:
# "Value = %10!I64x!".
# - Sig: the short name to use for this type when generating the names for
# helper functions. Two different WPP types might use the same signature
# because the types are compatible in terms of the helper functions (the
# underlying C/C++ types are the same and the types serialize into ETW data
# the same way).
# - Priority: the priority to give the type during argument sorting (only
# significant when the /reorder parameter is used). This value is typically
# set to the type's alignment so that more-aligned values sort first and
# remain aligned within the ETW data.
# - ManifestType: describes the how the type should be treated in
# manifest-based WPP. The ManifestType is a semicolon-separated list of type
# parameters. The first parameter is required and specifies the winmeta input
# type, e.g. "win:UInt32" (valid winmeta types are described in winmeta.xml).
# Subsequent parameters specify options in the form of a name=value pair. The
# supported options are:
# - outType: Specifies the winmeta output type (i.e. a type formatting
# qualifier), e.g. "xs:string". If not set, the type will not include an
# outType attribute in the manifest and will receive default formatting by
# the trace decoder. Example use of outType: win:Int32;outType=win:HResult
# - adapter: Specifies a function or macro that should be applied to values
# of this type before passing the value to the MC-generated EventWrite
# macro. For example, if argument a10 has no adapter and argument a11 has
# "ADAPT" as its adapter, the MC-generated EventWrite macro would be
# invoked as EventWriteMyEvent(a10, ADAPT(a11)). The adapter might perform
# a cast (e.g. cast ULONG_PTR to void*) or in the case of a Counted type
# might convert a single value into a length+pointer pair. For example, the
# adapter for a PUNICODE_STRING type might be the following macro:
# #define ADAPT_PSTR(p) p->Length/2, p->Buffer
# Example use of adapter: win:CountedUnicodeString;adapter=ADAPT_PSTR
#
# CUSTOM_TYPE(Name, BaseType(Extension), [FormatSpec]);
#
# Creates a new type by adding an extension to an existing type. Typically
# used to define enumerations, e.g.:
# ItemListByte(EnumValues...), ItemListShort(EnumValues...),
# ItemListLong(EnumValues...), ItemSetByte(EnumValues...),
# ItemSetShort(EnumValues...), ItemSetLong(EnumValues...),
# ItemEnum(EnumName), or ItemFlagsEnum(EnumName).
#
# ItemList types are used for list enums, i.e. items counting up from 0.
# ItemList types support skipping values via Value=number syntax.
#
# ItemSet types are used for bitset enums, i.e. the first item has value
# 1, the second has value 2, the third has value 4, the fourth has value 8.
#
# ItemEnum and ItemFlagsEnum types reference the name of a C/C++ enum and
# are resolved using symbol information from the PDB. (ItemEnum and
# ItemFlagsEnum do not work for manifest-based WPP because the PDB is not
# used for resolving enumerations.)
#
# TYPEMACRO(Name, BaseType(Extension), [FormatSpec]);
#
# Deprecated. Same as CUSTOM_TYPE.
#
# DEFINE_SIMPLE_TYPE(Name, EquivType, MofType, FormatSpec, Sig, Priority,
# [ManifestType]);
#
# Creates a new fixed-length type that is passed to the helper function by
# value (uses helper macro WPP_LOGTYPEVAL). Size in the event payload will
# be sizeof(EquivType).
#
# DEFINE_SIMPLE_TYPE_PTR(Name, EquivType, MofType, FormatSpec, Sig, Priority,
# [ManifestType]);
#
# Creates a new fixed-length type that is passed to the helper function by
# address (uses helper macro WPP_LOGTYPEPTR). Size in the event payload will
# be sizeof(*value).
#
# DEFINE_FLAVOR(Name, BaseType, [MofType, FormatSpec, ManifestType]);
#
# Defines a new type that is based on another similar type, potentially
# overriding the base type's MofType, FormatSpec, and manifest type. Omitted
# or blank settings will remain the same as the base type.
#
# DEFINE_CPLX_TYPE(Name, MacroName, EquivType, MofType, FormatSpec, Sig,
# Priority, [ManifestType]);
#
# Defines a complex type, i.e. one that requires special handling via a
# helper macro other than WPP_LOGTYPEVAL or WPP_LOGTYPEPTR. Any type with a
# variable length (e.g. any string type) is complex.
#
# MacroName is the name of the helper macro that WPP should invoke when
# passing this value to the helper function. The helper macro should itself
# invoke the WPP_LOGPAIR macro with the data size and the data pointer. (Note
# that helper macros are only used for TMF-based WPP, not for manifest-based
# WPP.)
################################
# Manifest definition commands
################################
#
# The following commands are used with manifest-based WPP. Many of these
# commands accept a required Name, and allow for an optional Symbol. The item
# defined by such a command can be referenced by either its name or its
# symbol. If no symbol is defined, it can only be referenced by its name. The
# symbol may be useful in cases where the desired name is not a C/C++-friendly
# identifier.
#
# MANIFEST_CONFIGURATION(ManifestFile, [HeaderFile, Prefix]);
#
# For use with manifest-based WPP. Normally specified via the command-line
# -man option, not in an INI file or via configuration blocks.
#
# MANIFEST_RESOURCES(ResourceFile, [MessageFile, Culture]);
#
# For use with manifest-based WPP. Specifies information about the installed
# location of the DLL/EXE/SYS file that contains a manifest's resource data.
# If not specified, Culture defaults to "en-us". Since the install location
# is usually not known at build time, tracewpp defaults to using dummy paths
# for the resource file and message file. The dummy paths are typically
# overridden by the /rf and /mf parameters on the wevtutil command line
# during manifest installation. Note that the ResourceFile and MessageFile
# values must be absolute paths.
#
# DEFINE_CONTROL_GUID(Name, [Guid, Symbol]);
#
# For use with manifest-based WPP. Defines a control GUID (aka a provider).
# If the Guid parameter is not specified, the GUID for the provider will be
# computed by hashing the Name using the same name hashing algorithm as is
# used by tracelog, xperf, EventSource, and other ETW tools.
#
# DEFINE_BIT(Name, [Mask, Symbol]);
#
# For use with manifest-based WPP. Adds a keyword to the most recently
# defined control guid. The default mask of the keyword is the control
# guid's lowest unused keyword bit.
#
# Mask may be specified as decimal or hexadecimal and must be a power of 2.
#
# DEFINE_KEYWORD(Name, ControlGuidId, Mask, [Symbol]);
#
# For use with manifest-based WPP. Defines a keyword associated with the
# specified control GUID. Rarely used: DEFINE_BIT is usually simpler.
#
# DEFINE_LEVEL(Name, ControlGuidId, Value, [Symbol]);
#
# For use with manifest-based WPP. Defines a level associated with the
# specified control GUID. The level value must not conflict with any
# built-in levels (level values 0 through 15 are built-in levels). Rarely
# used: the built-in levels are sufficient for most components.
#
# DEFINE_BUILTIN_KEYWORD(Name, Mask, [Symbol, Symbol...]);
#
# For use with manifest-based WPP. Normally used in the defaultwpp.ini file,
# not in per-project INI files or configuration blocks. Defines a global
# (Microsoft-defined) keyword value. These keywords can be used with any
# control GUID. These keywords will not be defined in the manifest - they
# are expected to match keyword definitions from winmeta.xml.
#
# DEFINE_BUILTIN_LEVEL(Name, Value, [Symbol, Symbol...]);
#
# For use with manifest-based WPP. Normally used in the defaultwpp.ini file,
# not in per-project INI files or configuration blocks. Defines a global
# (Microsoft-defined) level value. These levels can be used with any control
# GUID. These levels will not be defined in the manifest - they are expected
# to match level definitions from winmeta.xml.
#
# DEFAULT_FUNC_NAME_IN_EVENT(Expression, [TypeName]);
#
# For use with manifest-based WPP. When using manifest-based WPP, tracewpp is
# unable to automatically set the event's function name during manifest
# generation. The function name can be manually specified via the Func
# parameter or it can be included in the event payload via
# DEFAULT_FUNC_NAME_IN_EVENT or USE_FUNC_NAME_IN_EVENT.
# The DEFAULT_FUNC_NAME_IN_EVENT command will add a hidden string property
# named "!FUNC!" to every event (except when overridden by
# USE_FUNC_NAME_IN_EVENT). The value of the property will be taken from the
# specified Expression. The Expression will typically be set to
# "__FUNCTION__". TypeName is the WPP name of the type to use for the hidden
# argument. TypeName defaults to "hs" (i.e. nul-terminated char* string).
#
# USE_FUNC_NAME_IN_EVENT(FunctionName, Expression, [TypeName]);
#
# For use with manifest-based WPP. When using manifest-based WPP, tracewpp is
# unable to automatically set the event's function name during manifest
# generation. The function name can be manually specified via the Func
# parameter or it can be included in the event payload via
# DEFAULT_FUNC_NAME_IN_EVENT or USE_FUNC_NAME_IN_EVENT.
#
# The USE_FUNC_NAME_IN_EVENT command with a non-empty value for Expression
# will add a hidden string property named "!FUNC!" to every event generated
# by the specified trace function. The value of the property will be taken
# from the specified Expression. The Expression will typically be set to
# "__FUNCTION__". TypeName is the WPP name of the type to use for the hidden
# argument. TypeName defaults to "hs" (i.e. nul-terminated char* string).
#
# If Expression is empty (i.e. ""), the "!FUNC!" property will not be added
# to events generated by the specified trace function. A blank Expression
# can be used to override a default set by DEFAULT_FUNC_NAME_IN_EVENT.
#
################################
# Format string syntax
################################
#
# The format string is parsed to determine the type and format of event
# properties. Format string syntax is similar to printf, i.e. a '%' character
# not immediately followed by another '%' character will be treated as the
# start of a property reference.
#
# A property reference is parsed as one of the following:
#
# '%' [ '{' PropertyName '}' ] [Prefix] ShortTypeName
# '%' [ '{' PropertyName '}' ] [ArgNum] '!' [Prefix] LongTypeName '!'
#
# {PropertyName} is optional. If specified, the name must contain only
# identifier characters, i.e. a-z, 0-9, and '_'.
#
# Prefix includes flags ('-', '+', '#'), width digits, and/or '.' followed by
# precision digits. Note that prefix does not support "*" or ".*".
#
# If prefix is specified, it will be included in the event's format string
# along with the type's FormatSpec. For example, "%+02hX" would be parsed as
# Prefix="+02", ShortTypeName="hX". Type "hX" has FormatSpec="X", so the final
# WPP format string for the property would be "+02X".
#
# If the -NoShrieks command-line option is set and the prefix before a
# ShortTypeName contains only digits, tracewpp will treat the number as an
# ArgNum instead of a Width.
#
# The ArgNum before '!' is optional. It indicates the index of the argument to
# use for this property reference. If not specified, ArgNum is determined from
# the position of the property reference in the format string.
#
# By default, ShortTypeName is a length (one of "", "I", "I64", "h", "w", "l",
# or "ll") followed by a specifier (any single character). However, if the
# -NoShrieks command-line option is set, ShortTypeName is any number of
# identifier characters (a-z, A-Z, 0-9, '_') and ends at the first
# non-identifier character.
#
# LongTypeName is any non-empty sequence of characters ending at the '!'.
#
# ShortTypeName or LongTypeName must match the name of a defined WPP type (i.e.
# the type names defined below or your own custom types).
#
################################
# Default WPP configuration
################################
# our basic arithmetic types
DEFINE_SIMPLE_TYPE( XINT64, signed __int64, ItemLongLongX, "I64x", i, 8, win:HexInt64);
DEFINE_SIMPLE_TYPE( XXINT64,signed __int64, ItemLongLongXX,"I64X", i, 8, win:HexInt64);
DEFINE_SIMPLE_TYPE( OINT64, signed __int64, ItemLongLongO, "I64o", i, 8, win:HexInt64);
DEFINE_SIMPLE_TYPE( SBYTE, signed char, ItemChar, "c", c, 1, win:Int8);
DEFINE_SIMPLE_TYPE( SSHORT, signed short, ItemShort, "hd", h, 2, win:Int16);
DEFINE_SIMPLE_TYPE( SINT, signed int, ItemLong, "d", d, 4, win:Int32);
DEFINE_SIMPLE_TYPE( SLONG, signed long, ItemLong, "ld", l, 4, win:Int32);
DEFINE_SIMPLE_TYPE( SINT64, signed __int64, ItemLongLong, "I64d", i, 8, win:Int64);
DEFINE_SIMPLE_TYPE( UBYTE, unsigned char, ItemChar, "c", C, 1, win:UInt8);
DEFINE_SIMPLE_TYPE( USHORT, unsigned short, ItemShort, "hu", H, 2, win:UInt16);
DEFINE_SIMPLE_TYPE( UINT, unsigned int, ItemLong, "u", D, 4, win:UInt32);
DEFINE_SIMPLE_TYPE( ULONG, unsigned long, ItemLong, "lu", L, 4, win:UInt32);
DEFINE_SIMPLE_TYPE( UINT64, unsigned __int64,ItemULongLong, "I64u", I, 8, win:UInt64);
DEFINE_SIMPLE_TYPE( DOUBLE, double, ItemDouble, "s", g, 8, win:Double);
DEFINE_FLAVOR( UCHAR, UBYTE, ItemUChar, "c", win:UInt8; outType=xs:string);
DEFINE_FLAVOR( SCHAR, SBYTE, ItemChar, "c", win:Int8; outType=xs:string);
# arch dependent types
DEFINE_SIMPLE_TYPE( SLONGPTR, LONG_PTR, ItemPtr, "Id", p, 7, win:Pointer; adapter=WPP_ADAPTER_PVOID; outType=xs:long);
DEFINE_SIMPLE_TYPE( ULONGPTR, ULONG_PTR, ItemPtr, "Iu", P, 7, win:Pointer; adapter=WPP_ADAPTER_PVOID; outType=xs:unsignedLong);
DEFINE_SIMPLE_TYPE( PTR, const void*, ItemPtr, "p", q, 6, win:Pointer);
DEFINE_SIMPLE_TYPE( HANDLE, const void*, ItemPtr, "p", q, 6, win:Pointer);
DEFINE_SIMPLE_TYPE_PTR(GUID, LPCGUID, ItemGuid, "s", _guid_, 4, win:GUID);
# Predefined constants.
# Typically used in the function's prefix or suffix.
DEFINE_SIMPLE_TYPE( COMPNAME,,, "__COMPNAME__",,);
DEFINE_SIMPLE_TYPE( FILE,,, "__FILE__",,);
DEFINE_SIMPLE_TYPE( LINE,,, "__LINE__",,);
DEFINE_SIMPLE_TYPE( SPACE,,, " ",,);
# The following can be used in the prefix or suffix.
DEFINE_SIMPLE_TYPE( FUNC,,, "%!FUNC!",,);
DEFINE_SIMPLE_TYPE( LEVEL,,, "%!LEVEL!",,);
DEFINE_SIMPLE_TYPE( KEYWORDS,,, "%!FLAGS!",,);
DEFINE_SIMPLE_TYPE( STDPREFIX,,, "%0",,); # Only works at start of prefix. Ignored for manifest-based WPP.
DEFINE_SIMPLE_TYPE( MOD,,, "%1!s!",,);
DEFINE_SIMPLE_TYPE( TYP,,, "%2!s!",,);
DEFINE_SIMPLE_TYPE( TID,,, "%3!x!",,);
DEFINE_SIMPLE_TYPE( NOW,,, "%4!s!",,);
DEFINE_SIMPLE_TYPE( SEQ,,, "%7!u!",,);
DEFINE_SIMPLE_TYPE( PID,,, "%8!x!",,);
DEFINE_SIMPLE_TYPE( CPU,,, "%9!u!",,);
# Complex types
# Strings and SID require custom processing
DEFINE_CPLX_TYPE(ASTR, WPP_LOGASTR, LPCSTR, ItemString, "s", s, 1, win:AnsiString);
DEFINE_CPLX_TYPE(ARSTR, WPP_LOGASTR, LPCSTR, ItemRString, "s", s, 1, win:AnsiString);
DEFINE_CPLX_TYPE(ARWSTR,WPP_LOGWSTR, LPCWSTR, ItemRWString,"s", S, 2, win:UnicodeString);
DEFINE_CPLX_TYPE(WSTR, WPP_LOGWSTR, LPCWSTR, ItemWString, "s", S, 2, win:UnicodeString);
DEFINE_CPLX_TYPE(CSTR, WPP_LOGPCSTR, const CSTRING*, ItemPString, "s", z, 1, win:CountedAnsiString; adapter=WPP_ADAPTER_PCSTR);
DEFINE_CPLX_TYPE(USTR, WPP_LOGPUSTR, PCUNICODE_STRING, ItemPWString,"s", Z, 2, win:CountedUnicodeString; adapter=WPP_ADAPTER_PUSTR);
DEFINE_CPLX_TYPE(ANSTR, WPP_LOGPCSTR, const ANSI_STRING*, ItemPString, "s", aZ, 1, win:CountedAnsiString; adapter=WPP_ADAPTER_PCSTR);
DEFINE_CPLX_TYPE(sid, WPP_LOGPSID, PSID, ItemSid, "s", _sid_, 0, win:SID; adapter=WPP_ADAPTER_PSID);
# Raw binary data. Usage: DoTraceMessage(FLAG, "%!BIN!", WppBinary(pointer, size));
DEFINE_CPLX_TYPE(BIN, WPP_LOGCSTR, WPP_BINARY, ItemHexDump, "s", _zb_, 1, win:CountedBinary; adapter=WPP_ADAPTER_BIN);
# C++ string types
DEFINE_CPLX_TYPE(str, WPP_LOGCPPSTR, const std::string&, ItemPString, "s", _str_, 0, win:CountedAnsiString; adapter=WPP_ADAPTER_CPPSTR);
DEFINE_CPLX_TYPE(wstr, WPP_LOGCPPSTR, const std::wstring&, ItemPWString, "s", _wstr_, 0, win:CountedUnicodeString; adapter=WPP_ADAPTER_CPPSTR);
DEFINE_CPLX_TYPE(sv, WPP_LOGCPPVEC, const std::string_view&, ItemPString, "s", _sv_, 0, win:CountedAnsiString; adapter=WPP_ADAPTER_CPPVEC);
DEFINE_CPLX_TYPE(wsv, WPP_LOGCPPVEC, const std::wstring_view&,ItemPWString, "s", _wsv_, 0, win:CountedUnicodeString; adapter=WPP_ADAPTER_CPPVEC);
# Define printf-compatible types
DEFINE_FLAVOR(e, DOUBLE,,);
DEFINE_FLAVOR(E, DOUBLE,,);
DEFINE_FLAVOR(f, DOUBLE,,);
DEFINE_FLAVOR(g, DOUBLE,,);
DEFINE_FLAVOR(G, DOUBLE,,);
DEFINE_FLAVOR(c, SCHAR,,);
DEFINE_FLAVOR(hc, SCHAR,,);
DEFINE_FLAVOR(lc, SSHORT,,);
DEFINE_FLAVOR(wc, SSHORT,,);
DEFINE_FLAVOR(C, SSHORT,,);
DEFINE_FLAVOR(s, ASTR,,);
DEFINE_FLAVOR(hs, ASTR,,);
DEFINE_FLAVOR(S, WSTR,,);
DEFINE_FLAVOR(ws, WSTR,,);
DEFINE_FLAVOR(ls, WSTR,,);
DEFINE_FLAVOR(hi, SSHORT,,);
DEFINE_FLAVOR(hd, SSHORT,,);
DEFINE_FLAVOR(hu, USHORT,,"u");
DEFINE_FLAVOR(hx, USHORT,,"x", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(hX, USHORT,,"X", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(ho, USHORT,,"o", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(Id, ULONGPTR,,"Id");
DEFINE_FLAVOR(Iu, ULONGPTR,,"Iu");
DEFINE_FLAVOR(Ix, ULONGPTR,,"Ix", win:Pointer; adapter=WPP_ADAPTER_PVOID);
DEFINE_FLAVOR(IX, ULONGPTR,,"IX", win:Pointer; adapter=WPP_ADAPTER_PVOID);
DEFINE_FLAVOR(Io, ULONGPTR,,"Io", win:Pointer; adapter=WPP_ADAPTER_PVOID);
DEFINE_FLAVOR(i, SINT,,);
DEFINE_FLAVOR(d, SINT,,);
DEFINE_FLAVOR(u, UINT,,"u");
DEFINE_FLAVOR(x, UINT,,"x", win:HexInt32);
DEFINE_FLAVOR(X, UINT,,"X", win:HexInt32);
DEFINE_FLAVOR(o, UINT,,"o", win:HexInt32);
DEFINE_FLAVOR(cccc, SINT, ItemChar4, "s", win:AnsiString; length=4; adapter=WPP_ADAPTER_CCCC);
DEFINE_FLAVOR(li, SLONG,,);
DEFINE_FLAVOR(ld, SLONG,,);
DEFINE_FLAVOR(lu, ULONG,,"u");
DEFINE_FLAVOR(lx, ULONG,,"x", win:HexInt32);
DEFINE_FLAVOR(lX, ULONG,,"X", win:HexInt32);
DEFINE_FLAVOR(lo, ULONG,,"o", win:HexInt32);
DEFINE_FLAVOR(I64d, SINT64,,);
DEFINE_FLAVOR(I64u, UINT64,,);
DEFINE_FLAVOR(I64x, XINT64,,"I64x");
DEFINE_FLAVOR(I64X, XXINT64,,"I64X");
DEFINE_FLAVOR(I64o, OINT64,,"I64o");
DEFINE_FLAVOR(lld, SINT64,,);
DEFINE_FLAVOR(llu, UINT64,,);
DEFINE_FLAVOR(llx, XINT64,,"I64x");
DEFINE_FLAVOR(llX, XXINT64,,"I64X");
DEFINE_FLAVOR(llo, OINT64,,"I64o");
DEFINE_FLAVOR(p, PTR,,);
DEFINE_FLAVOR(Z, ANSTR,,);
DEFINE_FLAVOR(wZ, USTR,,);
DEFINE_FLAVOR(z, CSTR,,);
DEFINE_FLAVOR(hZ, CSTR,,);
# default formats for those who don't care to provide their own strings
DEFINE_FLAVOR(XBYTE, SBYTE,, "02x", win:UInt8; outType=win:HexInt8);
DEFINE_FLAVOR(OBYTE, SBYTE,, "o", win:UInt8; outType=win:HexInt8);
DEFINE_FLAVOR(XSHORT, SSHORT,, "04hX", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(OSHORT, SSHORT,, "ho", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(XINT, SINT,, "08x", win:HexInt32);
DEFINE_FLAVOR(OINT, SINT,, "o", win:HexInt32);
DEFINE_FLAVOR(XLONG, SLONG,, "08lX", win:HexInt32);
DEFINE_FLAVOR(OLONG, SLONG,, "lo", win:HexInt32);
DEFINE_FLAVOR(XLONGPTR, SLONGPTR,,"Ix", win:Pointer; adapter=WPP_ADAPTER_PVOID);
DEFINE_FLAVOR(OLONGPTR, SLONGPTR,,"Io", win:Pointer; adapter=WPP_ADAPTER_PVOID);
# special formats
DEFINE_FLAVOR(IPADDR, UINT, ItemIPAddr, "s", win:UInt32; outType=win:IPv4);
DEFINE_FLAVOR(PORT, USHORT, ItemPort, "s", win:UInt16; outType=win:Port);
DEFINE_FLAVOR(STATUS, SINT, ItemNTSTATUS, "s", win:HexInt32; outType=win:NTSTATUS);
DEFINE_FLAVOR(WINERROR, UINT, ItemWINERROR, "s", win:UInt32; outType=win:Win32Error);
DEFINE_FLAVOR(HRESULT, SINT, ItemHRESULT, "s", win:Int32; outType=win:HResult);
# Note: The %!NDIS_STATUS! and %!NDIS_OID! types work for for TMF-based WPP but
# do not work well with manifest-based WPP. Manifest-based ETW decoders will
# treat %!NDIS_STATUS! and %!NDIS_OID! items as HexInt32 items.
DEFINE_FLAVOR(NDIS_STATUS, SINT, ItemNDIS_STATUS, "s", win:HexInt32);
DEFINE_FLAVOR(NDIS_OID, UINT, ItemNDIS_OID, "s", win:HexInt32);
DEFINE_FLAVOR(ipaddr,IPADDR,,);
DEFINE_FLAVOR(port,PORT,,);
DEFINE_FLAVOR(status,STATUS,,);
DEFINE_FLAVOR(hresult,HRESULT,,);
DEFINE_FLAVOR(winerr,WINERROR,,);
DEFINE_FLAVOR(guid,GUID,,);
# time related stuff
DEFINE_FLAVOR(TIMESTAMP, SINT64, ItemTimestamp, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
DEFINE_FLAVOR(TIME, SINT64, ItemTimestamp, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
DEFINE_FLAVOR(DATE, SINT64, ItemTimestamp, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
DEFINE_FLAVOR(WAITTIME, SINT64, ItemTimestamp, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
DEFINE_FLAVOR(due, SINT64, ItemWaitTime, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
DEFINE_FLAVOR(delta, SINT64, ItemTimeDelta, "s");
DEFINE_FLAVOR(datetime, SINT64, ItemTimestamp, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
# enumeration types
DEFINE_FLAVOR(ItemListByte, SBYTE, ItemListByte, "s", win:UInt8);
DEFINE_FLAVOR(ItemListShort, SSHORT, ItemListShort, "s", win:UInt16);
DEFINE_FLAVOR(ItemListLong, SLONG, ItemListLong, "s", win:UInt32);
DEFINE_FLAVOR(ItemSetByte, UBYTE, ItemSetByte, "s", win:UInt8; outType=win:HexInt8);
DEFINE_FLAVOR(ItemSetShort, USHORT, ItemSetShort, "s", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(ItemSetLong, ULONG, ItemSetLong, "s", win:HexInt32);
# Note: The "ItemEnum" and "ItemFlagsEnum" types work for for TMF-based WPP but
# do not work well with manifest-based WPP. Manifest-based ETW decoders will
# treat types based on "ItemEnum" as UInt32 and will treat types based on
# "ItemFlagsEnum" as HexInt32. For better results, use ItemListLong,
# ItemSetLong, or a "begin_wpp enum" block for your enumeration.
DEFINE_FLAVOR(ItemEnum, ULONG, ItemEnum, "s");
DEFINE_FLAVOR(ItemFlagsEnum, ULONG, ItemFlagsEnum, "s", win:HexInt32);
DEFINE_FLAVOR(CLSID, GUID, ItemCLSID, "s");
DEFINE_FLAVOR(LIBID, GUID, ItemLIBID, "s");
DEFINE_FLAVOR(IID, GUID, ItemIID, "s");
CUSTOM_TYPE(b1, ItemSetByte(1,2,3,4,5,6,7,8) );
CUSTOM_TYPE(b2, ItemSetShort(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16) );
CUSTOM_TYPE(b4, ItemSetLong(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32) );
CUSTOM_TYPE(bool, ItemListLong(false,true) );
CUSTOM_TYPE(bool16, ItemListShort(false,true) );
CUSTOM_TYPE(bool8, ItemListByte(false,true) );
CUSTOM_TYPE(BOOLEAN, ItemListByte(FALSE,TRUE) );
CUSTOM_TYPE(irql, ItemListByte(Low,APC,DPC) );
CUSTOM_TYPE(pnpmn, ItemListByte(IRP_MN_START_DEVICE,IRP_MN_QUERY_REMOVE_DEVICE,IRP_MN_REMOVE_DEVICE,IRP_MN_CANCEL_REMOVE_DEVICE,IRP_MN_STOP_DEVICE,IRP_MN_QUERY_STOP_DEVICE,IRP_MN_CANCEL_STOP_DEVICE,IRP_MN_QUERY_DEVICE_RELATIONS,IRP_MN_QUERY_INTERFACE,IRP_MN_QUERY_CAPABILITIES,IRP_MN_QUERY_RESOURCES,IRP_MN_QUERY_RESOURCE_REQUIREMENTS,IRP_MN_QUERY_DEVICE_TEXT,IRP_MN_FILTER_RESOURCE_REQUIREMENTS,IRP_MN_PNP_14,IRP_MN_READ_CONFIG,IRP_MN_WRITE_CONFIG,IRP_MN_EJECT,IRP_MN_SET_LOCK,IRP_MN_QUERY_ID,IRP_MN_QUERY_PNP_DEVICE_STATE,IRP_MN_QUERY_BUS_INFORMATION,IRP_MN_DEVICE_USAGE_NOTIFICATION,IRP_MN_SURPRISE_REMOVAL) );
CUSTOM_TYPE(sysctrl, ItemListByte(IRP_MN_QUERY_ALL_DATA,IRP_MN_QUERY_SINGLE_INSTANCE, IRP_MN_CHANGE_SINGLE_INSTANCE, IRP_MN_CHANGE_SINGLE_ITEM, IRP_MN_ENABLE_EVENTS, IRP_MN_DISABLE_EVENTS, IRP_MN_ENABLE_COLLECTION, IRP_MN_DISABLE_COLLECTION, IRP_MN_REGINFO, IRP_MN_EXECUTE_METHOD, IRP_MN_Reserved_0a, IRP_MN_REGINFO_EX) );
CUSTOM_TYPE(pnpmj, ItemListByte(IRP_MJ_CREATE,IRP_MJ_CREATE_NAMED_PIPE,IRP_MJ_CLOSE,IRP_MJ_READ,IRP_MJ_WRITE,IRP_MJ_QUERY_INFORMATION,IRP_MJ_SET_INFORMATION,IRP_MJ_QUERY_EA,IRP_MJ_SET_EA,IRP_MJ_FLUSH_BUFFERS,IRP_MJ_QUERY_VOLUME_INFORMATION,IRP_MJ_SET_VOLUME_INFORMATION,IRP_MJ_DIRECTORY_CONTROL,IRP_MJ_FILE_SYSTEM_CONTROL,IRP_MJ_DEVICE_CONTROL,IRP_MJ_INTERNAL_DEVICE_CONTROL,IRP_MJ_SHUTDOWN,IRP_MJ_LOCK_CONTROL,IRP_MJ_CLEANUP,IRP_MJ_CREATE_MAILSLOT,IRP_MJ_QUERY_SECURITY,IRP_MJ_SET_SECURITY,IRP_MJ_POWER,IRP_MJ_SYSTEM_CONTROL,IRP_MJ_DEVICE_CHANGE,IRP_MJ_QUERY_QUOTA,IRP_MJ_SET_QUOTA,IRP_MJ_PNP) );
# Built-in levels (for use with manifests)
DEFINE_BUILTIN_LEVEL(win:LogAlways, 0, WINEVENT_LEVEL_LOG_ALWAYS);
DEFINE_BUILTIN_LEVEL(win:Critical, 1, WINEVENT_LEVEL_CRITICAL, TRACE_LEVEL_CRITICAL);
DEFINE_BUILTIN_LEVEL(win:Error, 2, WINEVENT_LEVEL_ERROR, TRACE_LEVEL_ERROR);
DEFINE_BUILTIN_LEVEL(win:Warning, 3, WINEVENT_LEVEL_WARNING, TRACE_LEVEL_WARNING);
DEFINE_BUILTIN_LEVEL(win:Informational, 4, WINEVENT_LEVEL_INFO, TRACE_LEVEL_INFORMATION);
DEFINE_BUILTIN_LEVEL(win:Verbose, 5, WINEVENT_LEVEL_VERBOSE, TRACE_LEVEL_VERBOSE);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel6, 6, WINEVENT_LEVEL_RESERVED_6);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel7, 7, WINEVENT_LEVEL_RESERVED_7);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel8, 8, WINEVENT_LEVEL_RESERVED_8);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel9, 9, WINEVENT_LEVEL_RESERVED_9);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel10, 10, WINEVENT_LEVEL_RESERVED_10);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel11, 11, WINEVENT_LEVEL_RESERVED_11);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel12, 12, WINEVENT_LEVEL_RESERVED_12);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel13, 13, WINEVENT_LEVEL_RESERVED_13);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel14, 14, WINEVENT_LEVEL_RESERVED_14);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel15, 15, WINEVENT_LEVEL_RESERVED_15);
# Built-in keywords (for use with manifests)
DEFINE_BUILTIN_KEYWORD(win:AnyKeyword, 0x0, WINEVT_KEYWORD_ANY);
DEFINE_BUILTIN_KEYWORD(win:ResponseTime, 0x0001000000000000, WINEVENT_KEYWORD_RESPONSE_TIME);
DEFINE_BUILTIN_KEYWORD(win:WDIContext, 0x0002000000000000, WINEVENT_KEYWORD_WDI_CONTEXT);
DEFINE_BUILTIN_KEYWORD(win:WDIDiag, 0x0004000000000000, WINEVENT_KEYWORD_WDI_DIAG);
DEFINE_BUILTIN_KEYWORD(win:SQM, 0x0008000000000000, WINEVENT_KEYWORD_SQM);
DEFINE_BUILTIN_KEYWORD(win:AuditFailure, 0x0010000000000000, WINEVENT_KEYWORD_AUDIT_FAILURE);
DEFINE_BUILTIN_KEYWORD(win:AuditSuccess, 0x0020000000000000, WINEVENT_KEYWORD_AUDIT_SUCCESS);
DEFINE_BUILTIN_KEYWORD(win:CorrelationHint, 0x0040000000000000, WINEVENT_KEYWORD_CORRELATION_HINT);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword56, 0x0100000000000000, WINEVENT_KEYWORD_RESERVED_56);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword57, 0x0200000000000000, WINEVENT_KEYWORD_RESERVED_57);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword58, 0x0400000000000000, WINEVENT_KEYWORD_RESERVED_58);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword59, 0x0800000000000000, WINEVENT_KEYWORD_RESERVED_59);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword60, 0x1000000000000000, WINEVENT_KEYWORDE_RESERVED_60);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword61, 0x2000000000000000, WINEVENT_KEYWORD_RESERVED_61);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword62, 0x4000000000000000, WINEVENT_KEYWORD_RESERVED_62);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword63, 0x8000000000000000, WINEVENT_KEYWORD_RESERVED_63);
# default tracing macros
FUNC DoTraceMessage(LEVEL,MSG,...); # Uses WPP-provided ENABLED/LOGGER macros
FUNC DoDebugTrace(TRACELEVEL,MSG,...); # Requires used-defined ENABLED/LOGGER macros
# default prefix (use traceprt default)
USEPREFIX(*,"%!STDPREFIX!"); # traceprt will add standard prefix
# the source file that calls WPP_INIT_TRACING is given special treatment
WPP_FLAGS(-lookfor:WPP_INIT_TRACING);

View file

@ -0,0 +1,73 @@
`**********************************************************************`
`* This is an include template file for the tracewpp preprocessor. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// template `TemplateFile`
`* Dump definitions specified via -D on the command line to WPP *`
`FORALL def IN MacroDefinitions`
#define `def.Name` `def.Alias`
`ENDFOR`
#define WPP_THIS_FILE `SourceFile.CanonicalName`
#ifndef WPP_IsValidSid
#define WPP_IsValidSid IsValidSid
#endif
#ifndef WPP_GetLengthSid
#define WPP_GetLengthSid GetLengthSid
#endif
#if !defined(WPP_KERNEL_MODE)
# include <windows.h>
# pragma warning(disable: 4201)
# include <wmistr.h>
# include <evntrace.h>
# define WPP_TRACE TraceMessage
#else
#define WPP_TRACE WmiTraceMessage
#endif
#if !defined(WPP_PRIVATE)
# define WPP_INLINE __inline
# define WPP_SELECT_ANY extern "C" __declspec(selectany)
#else
# define WPP_INLINE static
# define WPP_SELECT_ANY static
#endif
__inline TRACEHANDLE WppQueryLogger(_In_opt_ PCWSTR LoggerName)
{
{
#if defined(WPP_KERNEL_MODE)
ULONG ReturnLength ;
NTSTATUS Status ;
TRACEHANDLE TraceHandle ;
UNICODE_STRING Buffer ;
RtlInitUnicodeString(&Buffer, LoggerName ? LoggerName : L"stdout");
if ((Status = WmiQueryTraceInformation(TraceHandleByNameClass,
(PVOID)&TraceHandle,
sizeof(TraceHandle),
&ReturnLength,
(PVOID)&Buffer)) == STATUS_SUCCESS) {
return TraceHandle ;
}
#else
ULONG status;
EVENT_TRACE_PROPERTIES LoggerInfo;
ZeroMemory(&LoggerInfo, sizeof(LoggerInfo));
LoggerInfo.Wnode.BufferSize = sizeof(LoggerInfo);
LoggerInfo.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
status = QueryTraceW(0, LoggerName ? LoggerName : L"stdout", &LoggerInfo);
if (status == ERROR_SUCCESS || status == ERROR_MORE_DATA) {
return (TRACEHANDLE) LoggerInfo.Wnode.HistoricalContext;
}
#endif
}
return 0;
}

View file

@ -0,0 +1,20 @@
`**********************************************************************`
`* This is a template file for the tracewpp preprocessor. *`
`* If you need to use a custom version of this file in your project, *`
`* please clone it from this one and point WPP to it by specifying *`
`* -gen:{yourfile}*.tmh on the RUN_WPP line in your sources file. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// `Compiler.Checksum` Generated file. Do not edit.
// File created by `Compiler.Name` compiler version `Compiler.Version`
// from template `TemplateFile`
#pragma once
`INCLUDE km-header.tpl`
`INCLUDE control.tpl`
`INCLUDE tracemacro.tpl`
`IF FOUND WPP_INIT_TRACING`
` INCLUDE km-init.tpl`
`ENDIF`

View file

@ -0,0 +1,284 @@
`**********************************************************************`
`* This is an include template file for the tracewpp preprocessor. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// template `TemplateFile`
#ifdef WPP_THIS_FILE
// included twice
# define WPP_ALREADY_INCLUDED
# undef WPP_THIS_FILE
#endif // #ifdef WPP_THIS_FILE
#define WPP_THIS_FILE `SourceFile.CanonicalName`
#ifndef WPP_ALREADY_INCLUDED
`* Dump the definitions specified via -D on the command line to WPP *`
`FORALL def IN MacroDefinitions`
#define `def.Name` `def.Alias`
`ENDFOR`
#include <evntrace.h>
#include <stddef.h>
#include <stdarg.h>
#include <wmistr.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef
LONG
(*PFN_WPPQUERYTRACEINFORMATION) (
IN TRACE_INFORMATION_CLASS TraceInformationClass,
OUT PVOID TraceInformation,
IN ULONG TraceInformationLength,
OUT PULONG RequiredLength OPTIONAL,
IN PVOID Buffer OPTIONAL
);
typedef
LONG
(*PFN_WPPTRACEMESSAGE)(
IN ULONG64 LoggerHandle,
IN ULONG MessageFlags,
IN LPCGUID MessageGuid,
IN USHORT MessageNumber,
IN ...
);
typedef enum _WPP_TRACE_API_SUITE {
WppTraceDisabledSuite,
WppTraceWin2K,
WppTraceWinXP,
WppTraceTraceLH,
WppTraceServer08,
WppTraceMaxSuite
} WPP_TRACE_API_SUITE;
_IRQL_requires_same_
typedef
VOID
(NTAPI *PETW_CLASSIC_CALLBACK)(
_In_ LPCGUID Guid,
_In_ UCHAR ControlCode,
_In_ PVOID EnableContext,
_In_opt_ PVOID CallbackContext
);
_IRQL_requires_same_
typedef
NTSTATUS
NTKERNELAPI
(FN_ETWREGISTERCLASSICPROVIDER)(
_In_ LPCGUID ProviderGuid,
_In_ ULONG Type,
_In_ PETW_CLASSIC_CALLBACK EnableCallback,
_In_opt_ PVOID CallbackContext,
_Out_ PREGHANDLE RegHandle
);
typedef FN_ETWREGISTERCLASSICPROVIDER *PFN_ETWREGISTERCLASSICPROVIDER;
typedef
BOOLEAN
NTKERNELAPI
(FN_WPPGETVERSION)(
_Out_opt_ PULONG MajorVersion,
_Out_opt_ PULONG MinorVersion,
_Out_opt_ PULONG BuildNumber,
_Out_opt_ PUNICODE_STRING CSDVersion
);
typedef FN_WPPGETVERSION *PFN_WPPGETVERSION;
typedef
NTSTATUS
NTKERNELAPI
(FN_ETWUNREGISTER)(
_In_ REGHANDLE RegHandle
);
typedef FN_ETWUNREGISTER *PFN_ETWUNREGISTER;
#pragma prefast(suppress:__WARNING_ENCODE_GLOBAL_FUNCTION_POINTER, "this pointer can not be encoded");
__declspec(selectany) PFN_WPPQUERYTRACEINFORMATION pfnWppQueryTraceInformation = NULL;
#pragma prefast(suppress:__WARNING_ENCODE_GLOBAL_FUNCTION_POINTER, "this pointer can not be encoded");
__declspec(selectany) PFN_WPPTRACEMESSAGE pfnWppTraceMessage = NULL;
#pragma prefast(suppress:__WARNING_ENCODE_GLOBAL_FUNCTION_POINTER, "this pointer can not be encoded");
__declspec(selectany) PFN_ETWUNREGISTER pfnEtwUnregister = NULL;
#pragma prefast(suppress:__WARNING_ENCODE_GLOBAL_FUNCTION_POINTER, "this pointer can not be encoded");
__declspec(selectany) PFN_ETWREGISTERCLASSICPROVIDER pfnEtwRegisterClassicProvider = NULL;
#pragma prefast(suppress:__WARNING_ENCODE_GLOBAL_FUNCTION_POINTER, "this pointer can not be encoded");
__declspec(selectany) PFN_WPPGETVERSION pfnWppGetVersion = NULL;
__declspec(selectany) WPP_TRACE_API_SUITE WPPTraceSuite = WppTraceDisabledSuite;
#if !defined(_NTRTL_)
#if !defined(_NTHAL_)
// fake RTL_TIME_ZONE_INFORMATION //
typedef int RTL_TIME_ZONE_INFORMATION;
#endif
#define _WMIKM_
#endif
#ifndef WPP_TRACE
#define WPP_TRACE pfnWppTraceMessage
#endif
#if ENABLE_WPP_RECORDER
#define _ENABLE_WPP_RECORDER TRUE
#ifndef WPP_RECORDER
#define WPP_RECORDER WppAutoLogTrace
#endif
//
// This setting is only applicable when IFR is enabled.
// Setting this to 1 will allow a WPP trace session to
// capture trace messages as usual i.e it will require the
// user to provide WPP trace ENABLED and LOGGER macro. If
// this is set to 0 by default the IFR trace filter also
// affects which trace messages land in the WPP trace session.
//
#if !defined(ENABLE_WPP_TRACE_FILTERING_WITH_WPP_RECORDER)
#define ENABLE_WPP_TRACE_FILTERING_WITH_WPP_RECORDER 0
#endif
#if !defined(WPP_RECORDER_LEVEL_FLAGS_ARGS)
#define WPP_RECORDER_LEVEL_FLAGS_ARGS(lvl, flags) WPP_CONTROL(WPP_BIT_ ## flags).AutoLogContext, lvl, WPP_BIT_ ## flags
#define WPP_RECORDER_LEVEL_FLAGS_FILTER(lvl,flags) (lvl < TRACE_LEVEL_VERBOSE || WPP_CONTROL(WPP_BIT_ ## flags).AutoLogVerboseEnabled)
#endif
#if !defined(WPP_RECORDER_LEVEL_ARGS)
#define WPP_RECORDER_LEVEL_ARGS(lvl) WPP_CONTROL(WPP_BIT_ ## lvl).AutoLogContext, 0, WPP_BIT_ ## lvl
#define WPP_RECORDER_LEVEL_FILTER(lvl) (WPP_CONTROL(WPP_BIT_ ## lvl).AutoLogVerboseEnabled)
#endif
NTSTATUS
WppAutoLogTrace(
IN PVOID AutoLogContext,
IN UCHAR MessageLevel,
IN ULONG MessageFlags,
IN LPGUID MessageGuid,
IN USHORT MessageNumber,
IN ...
);
#else
#define _ENABLE_WPP_RECORDER FALSE
#endif
VOID
WppLoadTracingSupport(
VOID
);
NTSTATUS
WppTraceCallback(
_In_ UCHAR MinorFunction,
_In_opt_ PVOID DataPath,
_In_ ULONG BufferLength,
_Inout_updates_bytes_(BufferLength) PVOID Buffer,
_Inout_ PVOID Context,
_Out_ PULONG Size
);
#if !defined(WPP_TRACE_CONTROL_NULL_GUID)
DEFINE_GUID(WPP_TRACE_CONTROL_NULL_GUID, 0x00000000L, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
#endif
#define WPP_TRACE_CONTROL(Function,Buffer,BufferSize,ReturnSize) WppTraceCallback(Function,NULL,BufferSize,Buffer,&WPP_CB[0],&ReturnSize);
__inline ULONG64 WppQueryLogger(_In_opt_ PCWSTR LoggerName)
{
if (WppTraceWinXP != WPPTraceSuite) {
return (ULONG64)0;
}
ULONG ReturnLength;
LONG Status;
ULONG64 TraceHandle;
UNICODE_STRING Buffer;
RtlInitUnicodeString(&Buffer, LoggerName ? LoggerName : L"stdout");
Status = pfnWppQueryTraceInformation(TraceHandleByNameClass,
(PVOID)&TraceHandle,
sizeof(TraceHandle),
&ReturnLength,
(PVOID)&Buffer
);
if (Status != STATUS_SUCCESS) {
return (ULONG64)0;
}
return TraceHandle;
}
typedef LONG (*WMIENTRY_NEW)(
_In_ UCHAR MinorFunction,
_In_opt_ PVOID DataPath,
_In_ ULONG BufferLength,
_Inout_updates_bytes_(BufferLength) PVOID Buffer,
_In_ PVOID Context,
_Out_ PULONG Size
);
typedef struct _WPP_TRACE_CONTROL_BLOCK
{
WMIENTRY_NEW Callback;
LPCGUID ControlGuid;
struct _WPP_TRACE_CONTROL_BLOCK *Next;
__int64 Logger;
PUNICODE_STRING RegistryPath;
UCHAR FlagsLen;
UCHAR Level;
USHORT Reserved;
ULONG Flags[1];
ULONG ReservedFlags;
REGHANDLE RegHandle;
#if ENABLE_WPP_RECORDER
PVOID AutoLogContext;
USHORT AutoLogVerboseEnabled;
USHORT AutoLogAttachToMiniDump;
#endif
} WPP_TRACE_CONTROL_BLOCK, *PWPP_TRACE_CONTROL_BLOCK;
VOID WppCleanupKm(_When_(_ENABLE_WPP_RECORDER, _In_) _When_(!_ENABLE_WPP_RECORDER, _In_opt_) PDRIVER_OBJECT DriverObject);
#define WPP_CLEANUP(DriverObject) WppCleanupKm((PDRIVER_OBJECT)DriverObject)
#define WPP_IsValidSid RtlValidSid
#define WPP_GetLengthSid RtlLengthSid
//
// Callback routine to be defined by the driver, which will be called from WPP callback
// WPP will pass current valued of : GUID, Logger, Enable, Flags, and Level
//
// To activate driver must define WPP_PRIVATE_ENABLE_CALLBACK in their code, sample below
// #define WPP_PRIVATE_ENABLE_CALLBACK MyPrivateCallback;
//
typedef
VOID
(*PFN_WPP_PRIVATE_ENABLE_CALLBACK)(
_In_ LPCGUID Guid,
_In_ __int64 Logger,
_In_ BOOLEAN Enable,
_In_ ULONG Flags,
_In_ UCHAR Level);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef WPP_ALREADY_INCLUDED

View file

@ -0,0 +1,989 @@
`**********************************************************************`
`* This is an include template file for the tracewpp preprocessor. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// template `TemplateFile`
//
// Defines a set of functions that simplifies
// kernel mode registration for tracing
//
#pragma warning(disable: 4201)
#include <ntddk.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef WPPINIT_EXPORT
#define WPPINIT_EXPORT
#endif
#ifndef WppDebug
#define WppDebug(a,b)
#endif
WPPINIT_EXPORT
VOID
WppInitGlobalLogger(
_In_ LPCGUID ControlGuid,
_Out_ PTRACEHANDLE LoggerHandle,
_Out_ PULONG Flags,
_Out_ PUCHAR Level
);
WPPINIT_EXPORT
VOID
WppInitKm(
_When_(_ENABLE_WPP_RECORDER, _In_) _When_(!_ENABLE_WPP_RECORDER, _In_opt_) PDRIVER_OBJECT DriverObject,
_When_(_ENABLE_WPP_RECORDER, _In_) _When_(!_ENABLE_WPP_RECORDER, _In_opt_) PCUNICODE_STRING RegPath
);
#if ENABLE_WPP_RECORDER
WPPINIT_EXPORT
VOID
WppAutoLogStart(
_In_ WPP_CB_TYPE * WppCb,
_In_ PDRIVER_OBJECT DrvObj,
_In_ PCUNICODE_STRING RegPath
);
VOID
WppAutoLogStop(
_In_ WPP_CB_TYPE * WppCb,
_In_ PDRIVER_OBJECT DrvObj
);
VOID
imp_WppRecorderReplay(
_In_ PVOID WppCb,
_In_ TRACEHANDLE WppTraceHandle,
_In_ ULONG EnableFlags,
_In_ UCHAR EnableLevel
);
#ifndef ENABLE_WPP_RECORDER_REPLAY
#define ENABLE_WPP_RECORDER_REPLAY 1
#endif
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, WppLoadTracingSupport)
#pragma alloc_text( PAGE, WppInitGlobalLogger)
#pragma alloc_text( PAGE, WppTraceCallback)
#pragma alloc_text( PAGE, WppInitKm)
#pragma alloc_text( PAGE, WppCleanupKm)
#endif // ALLOC_PRAGMA
// define annotation record that will carry control information to pdb (in case somebody needs it)
WPP_FORCEINLINE void WPP_CONTROL_ANNOTATION() {
#if !defined(WPP_NO_ANNOTATIONS)
#ifndef WPP_TMC_ANNOT_SUFIX
#ifdef WPP_PUBLIC_TMC
#define WPP_TMC_ANNOT_SUFIX ,L"PUBLIC_TMF:"
#else
#define WPP_TMC_ANNOT_SUFIX
#endif
#endif
# define WPP_DEFINE_CONTROL_GUID(Name,Guid,Bits) __annotation(L"TMC:", WPP_GUID_WTEXT Guid, _WPPW(WPP_STRINGIZE(Name)) Bits WPP_TMC_ANNOT_SUFIX);
# define WPP_DEFINE_BIT(Name) , _WPPW(#Name)
WPP_CONTROL_GUIDS
# undef WPP_DEFINE_BIT
# undef WPP_DEFINE_CONTROL_GUID
#endif
}
#define WPP_NEXT(Name) ((WPP_TRACE_CONTROL_BLOCK*) \
(WPP_XGLUE(WPP_CTL_, WPP_EVAL(Name)) + 1 == WPP_LAST_CTL ? 0:WPP_MAIN_CB + WPP_XGLUE(WPP_CTL_, WPP_EVAL(Name)) + 1))
#if ENABLE_WPP_RECORDER
#define INIT_WPP_RECORDER(Arr) \
Arr->Control.AutoLogContext = NULL; \
Arr->Control.AutoLogVerboseEnabled = 0x0; \
Arr->Control.AutoLogAttachToMiniDump = 0x0;
#else
#define INIT_WPP_RECORDER(Arr)
#endif
WPP_CB_TYPE WPP_MAIN_CB[WPP_LAST_CTL];
__inline void WPP_INIT_CONTROL_ARRAY(WPP_CB_TYPE* Arr) {
#define WPP_DEFINE_CONTROL_GUID(Name,Guid,Bits) \
Arr->Control.Callback = NULL; \
Arr->Control.ControlGuid = WPP_XGLUE4(&WPP_, ThisDir, _CTLGUID_, WPP_EVAL(Name)); \
Arr->Control.Next = WPP_NEXT(WPP_EVAL(Name)); \
Arr->Control.RegistryPath= NULL; \
Arr->Control.FlagsLen = WPP_FLAG_LEN; \
Arr->Control.Level = 0; \
Arr->Control.Reserved = 0; \
Arr->Control.Flags[0] = 0; \
INIT_WPP_RECORDER(Arr) \
++Arr;
#define WPP_DEFINE_BIT(BitName) L" " L ## #BitName
WPP_CONTROL_GUIDS
#undef WPP_DEFINE_BIT
#undef WPP_DEFINE_CONTROL_GUID
}
#undef WPP_INIT_STATIC_DATA
#define WPP_INIT_STATIC_DATA WPP_INIT_CONTROL_ARRAY(WPP_MAIN_CB)
// define WPP_INIT_TRACING. For performance reasons turn off during
// static analysis compilation with Static Driver Verifier (SDV).
#ifndef _SDV_
#define WPP_INIT_TRACING(DriverObject, RegPath) \
{ \
WppDebug(0,("WPP_INIT_TRACING: &WPP_CB[0] %p\n", &WPP_MAIN_CB[0])); \
WPP_INIT_STATIC_DATA; \
WppLoadTracingSupport(); \
( WPP_CONTROL_ANNOTATION(), \
WPP_MAIN_CB[0].Control.RegistryPath = NULL, \
WppInitKm( (PDRIVER_OBJECT)DriverObject, RegPath ) \
); \
}
#else
#define WPP_INIT_TRACING(DriverObject, RegPath)
#endif
#define WMIREG_FLAG_CALLBACK 0x80000000 // not exposed in DDK
#ifndef WMIREG_FLAG_TRACE_PROVIDER
#define WMIREG_FLAG_TRACE_PROVIDER 0x00010000
#endif
//
// Public routines to break down the Loggerhandle
//
#if !defined(KERNEL_LOGGER_ID)
#define KERNEL_LOGGER_ID 0xFFFF // USHORT only
#endif
typedef struct _WPP_TRACE_ENABLE_CONTEXT {
USHORT LoggerId; // Actual Id of the logger
UCHAR Level; // Enable level passed by control caller
UCHAR InternalFlag; // Reserved
ULONG EnableFlags; // Enable flags passed by control caller
} WPP_TRACE_ENABLE_CONTEXT, *PWPP_TRACE_ENABLE_CONTEXT;
#if !defined(WmiGetLoggerId)
#define WmiGetLoggerId(LoggerContext) \
(((PWPP_TRACE_ENABLE_CONTEXT) (&LoggerContext))->LoggerId == \
(USHORT)KERNEL_LOGGER_ID) ? \
KERNEL_LOGGER_ID : \
((PWPP_TRACE_ENABLE_CONTEXT) (&LoggerContext))->LoggerId
#define WmiGetLoggerEnableFlags(LoggerContext) \
((PWPP_TRACE_ENABLE_CONTEXT) (&LoggerContext))->EnableFlags
#define WmiGetLoggerEnableLevel(LoggerContext) \
((PWPP_TRACE_ENABLE_CONTEXT) (&LoggerContext))->Level
#endif
__inline int WppIsEqualGuid(_In_ const GUID* g1, _In_ const GUID* g2)
{
const ULONG* p1 = (const ULONG*)g1;
const ULONG* p2 = (const ULONG*)g2;
return p1[0] == p2[0] && p1[1] == p2[1] && p1[2] == p2[2] && p1[3] == p2[3];
}
VOID
WppLoadTracingSupport(
VOID
)
/*++
Routine Description:
This function assigns at runtime the ETW API set to be use for tracing.
Arguments:
Remarks:
At runtime determine assing the funtions pointers for the trace APIs to be use.
XP and above will use TraceMessage, and Win2K is not supported.
--*/
{
ULONG MajorVersion = 0;
UNICODE_STRING name;
PAGED_CODE();
RtlInitUnicodeString(&name, L"PsGetVersion");
pfnWppGetVersion = (PFN_WPPGETVERSION) (INT_PTR)
MmGetSystemRoutineAddress(&name);
RtlInitUnicodeString(&name, L"WmiTraceMessage");
pfnWppTraceMessage = (PFN_WPPTRACEMESSAGE) (INT_PTR)
MmGetSystemRoutineAddress(&name);
//
// WinXp
//
RtlInitUnicodeString(&name, L"WmiQueryTraceInformation");
pfnWppQueryTraceInformation = (PFN_WPPQUERYTRACEINFORMATION) (INT_PTR)
MmGetSystemRoutineAddress(&name);
WPPTraceSuite = WppTraceWinXP;
//
// Server08
//
if (pfnWppGetVersion != NULL) {
pfnWppGetVersion(&MajorVersion,
NULL,
NULL,
NULL);
}
if (MajorVersion >= 6) {
RtlInitUnicodeString(&name, L"EtwRegisterClassicProvider");
pfnEtwRegisterClassicProvider = (PFN_ETWREGISTERCLASSICPROVIDER) (INT_PTR)
MmGetSystemRoutineAddress(&name);
if (pfnEtwRegisterClassicProvider != NULL) {
//
// For Vista SP1 and later
//
RtlInitUnicodeString(&name, L"EtwUnregister");
pfnEtwUnregister = (PFN_ETWUNREGISTER) (INT_PTR)
MmGetSystemRoutineAddress(&name);
WPPTraceSuite = WppTraceServer08;
}
}
}
#ifdef WPP_GLOBALLOGGER
#define DEFAULT_GLOBAL_LOGGER_KEY L"WMI\\GlobalLogger\\"
#define WPP_TEXTGUID_LEN 38
#define GREGVALUENAMELENGTH (18 + WPP_TEXTGUID_LEN) // wslen(L"WMI\\GlobalLogger\\") + GUIDLENGTH
WPPINIT_EXPORT
VOID
WppInitGlobalLogger(
_In_ LPCGUID ControlGuid,
_Out_ PTRACEHANDLE LoggerHandle,
_Out_ PULONG Flags,
_Out_ PUCHAR Level
)
{
WCHAR GRegValueName[GREGVALUENAMELENGTH];
RTL_QUERY_REGISTRY_TABLE Parms[3];
ULONG CurrentFlags = 0;
ULONG CurrentLevel = 0;
ULONG Start = 0;
NTSTATUS Status;
ULONG Zero = 0;
UNICODE_STRING GuidString;
PAGED_CODE();
WppDebug(0,("WPP checking Global Logger\n"));
//
// Fill in the query table to find out if the Global Logger is Started
//
// Trace Flags
Parms[0].QueryRoutine = NULL;
Parms[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parms[0].Name = L"Start";
Parms[0].EntryContext = &Start;
Parms[0].DefaultType = REG_DWORD;
Parms[0].DefaultData = &Zero;
Parms[0].DefaultLength = sizeof(ULONG);
// Termination
Parms[1].QueryRoutine = NULL;
Parms[1].Flags = 0;
//
// Perform the query
//
Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL | RTL_REGISTRY_OPTIONAL,
DEFAULT_GLOBAL_LOGGER_KEY,
Parms,
NULL,
NULL);
if (!NT_SUCCESS(Status) || Start == 0 ) {
return;
}
// Fill in the query table to find out if we should use the Global logger
//
// Trace Flags
Parms[0].QueryRoutine = NULL;
Parms[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parms[0].Name = L"Flags";
Parms[0].EntryContext = &CurrentFlags;
Parms[0].DefaultType = REG_DWORD;
Parms[0].DefaultData = &Zero;
Parms[0].DefaultLength = sizeof(ULONG);
// Trace level
Parms[1].QueryRoutine = NULL;
Parms[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
Parms[1].Name = L"Level";
Parms[1].EntryContext = &CurrentLevel;
Parms[1].DefaultType = REG_DWORD;
Parms[1].DefaultData = &Zero;
Parms[1].DefaultLength = sizeof(UCHAR);
// Termination
Parms[2].QueryRoutine = NULL;
Parms[2].Flags = 0;
RtlCopyMemory(GRegValueName, DEFAULT_GLOBAL_LOGGER_KEY, (wcslen(DEFAULT_GLOBAL_LOGGER_KEY)+1) *sizeof(WCHAR));
#if defined(__cplusplus)
Status = RtlStringFromGUID(*ControlGuid, &GuidString);
#else
Status = RtlStringFromGUID(ControlGuid, &GuidString);
#endif
if( Status != STATUS_SUCCESS ) {
WppDebug(0,("WPP GlobalLogger failed RtlStringFromGUID \n"));
return;
}
if (GuidString.Length > (WPP_TEXTGUID_LEN * sizeof(WCHAR))){
WppDebug(0,("WPP GlobalLogger RtlStringFromGUID too large\n"));
RtlFreeUnicodeString(&GuidString);
return;
}
// got the GUID in form "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
// need GUID in form "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
// copy the translated GUID string
RtlCopyMemory(&GRegValueName[(ULONG)wcslen(GRegValueName)], &GuidString.Buffer[1], GuidString.Length);
GRegValueName[(ULONG)wcslen(GRegValueName) - 1] = L'\0';
RtlFreeUnicodeString(&GuidString);
//
// Perform the query
//
Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL | RTL_REGISTRY_OPTIONAL,
GRegValueName,
Parms,
NULL,
NULL);
if (NT_SUCCESS(Status)) {
if (Start==1) {
*LoggerHandle= WMI_GLOBAL_LOGGER_ID;
*Flags = CurrentFlags & 0x7FFFFFFF;
*Level = (UCHAR)(CurrentLevel & 0xFF);
WppDebug(0,("WPP Enabled via Global Logger Flags=0x%08X Level=0x%02X\n",CurrentFlags,CurrentLevel));
}
} else {
WppDebug(0,("WPP GlobalLogger has No Flags/Levels Status=%08X\n",Status));
}
}
#endif //#ifdef WPP_GLOBALLOGGER
#define WPP_MAX_COUNT_REGISTRATION_GUID 63
WPPINIT_EXPORT
NTSTATUS
WppTraceCallback(
_In_ UCHAR MinorFunction,
_In_opt_ PVOID DataPath,
_In_ ULONG BufferLength,
_Inout_updates_bytes_(BufferLength) PVOID Buffer,
_Inout_ PVOID Context,
_Out_ PULONG Size
)
/*++
Routine Description:
This function is the callback WMI calls when we register and when our
events are enabled or disabled.
Arguments:
MinorFunction - specifies the type of callback (register, event enable/disable)
DataPath - varies depending on the ActionCode
BufferLength - size of the Buffer parameter
Buffer - in/out buffer where we read from or write to depending on the type
of callback
Context - the pointer private struct WPP_TRACE_CONTROL_BLOCK
Size - output parameter to receive the amount of data written into Buffer
Return Value:
NTSTATUS code indicating success/failure
Comments:
if return value is STATUS_BUFFER_TOO_SMALL and BufferLength >= 4,
then first ulong of buffer contains required size
--*/
{
PWPP_TRACE_CONTROL_BLOCK cntl;
NTSTATUS Status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(DataPath);
PAGED_CODE();
WppDebug(0,("WppTraceCallBack 0x%08X %p\n", MinorFunction, Context));
*Size = 0;
switch(MinorFunction)
{
case IRP_MN_REGINFO:
{
PWMIREGINFOW WmiRegInfo;
PCUNICODE_STRING RegPath;
PWCHAR StringPtr;
ULONG RegistryPathOffset;
ULONG BufferNeeded;
ULONG GuidCount = 0;
//
// Initialize locals
//
cntl = (PWPP_TRACE_CONTROL_BLOCK)Context;
WmiRegInfo = (PWMIREGINFO)Buffer;
RegPath = cntl->RegistryPath;
//
// Count the number of guid to be identified.
//
while(cntl) { GuidCount++; cntl = cntl->Next; }
if (GuidCount > WPP_MAX_COUNT_REGISTRATION_GUID){
Status = STATUS_INVALID_PARAMETER;
break;
}
WppDebug(0,("WppTraceCallBack: GUID count %d\n", GuidCount));
//
// Calculate buffer size need to hold all info.
// Calculate offset to where RegistryPath parm will be copied.
//
if (RegPath == NULL)
{
RegistryPathOffset = 0;
BufferNeeded = FIELD_OFFSET(WMIREGINFOW, WmiRegGuid) +
GuidCount * sizeof(WMIREGGUIDW);
} else {
RegistryPathOffset = FIELD_OFFSET(WMIREGINFOW, WmiRegGuid) +
GuidCount * sizeof(WMIREGGUIDW);
BufferNeeded = RegistryPathOffset +
RegPath->Length + sizeof(USHORT);
}
//
// If the provided buffer is large enough, then fill with info.
//
if (BufferNeeded <= BufferLength)
{
ULONG i;
RtlZeroMemory(Buffer, BufferLength);
//
// Fill in the WMIREGINFO
//
WmiRegInfo->BufferSize = BufferNeeded;
WmiRegInfo->RegistryPath = RegistryPathOffset;
WmiRegInfo->GuidCount = GuidCount;
if (RegPath != NULL) {
StringPtr = (PWCHAR)((PUCHAR)Buffer + RegistryPathOffset);
*StringPtr++ = RegPath->Length;
RtlCopyMemory(StringPtr, RegPath->Buffer, RegPath->Length);
}
//
// Fill in the WMIREGGUID
//
cntl = (PWPP_TRACE_CONTROL_BLOCK) Context;
for (i=0; i<GuidCount; i++) {
WmiRegInfo->WmiRegGuid[i].Guid = *cntl->ControlGuid;
WmiRegInfo->WmiRegGuid[i].Flags = WMIREG_FLAG_TRACE_CONTROL_GUID |
WMIREG_FLAG_TRACED_GUID;
cntl->Level = 0;
cntl->Flags[0] = 0;
WppDebug(0,("Control GUID::%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
cntl->ControlGuid->Data1,
cntl->ControlGuid->Data2,
cntl->ControlGuid->Data3,
cntl->ControlGuid->Data4[0],
cntl->ControlGuid->Data4[1],
cntl->ControlGuid->Data4[2],
cntl->ControlGuid->Data4[3],
cntl->ControlGuid->Data4[4],
cntl->ControlGuid->Data4[5],
cntl->ControlGuid->Data4[6],
cntl->ControlGuid->Data4[7]
));
cntl = cntl->Next;
}
Status = STATUS_SUCCESS;
*Size = BufferNeeded;
} else {
Status = STATUS_BUFFER_TOO_SMALL;
if (BufferLength >= sizeof(ULONG)) {
*((PULONG)Buffer) = BufferNeeded;
*Size = sizeof(ULONG);
}
}
#ifdef WPP_GLOBALLOGGER
// Check if Global logger is active
cntl = (PWPP_TRACE_CONTROL_BLOCK) Context;
while(cntl) {
WppInitGlobalLogger(
cntl->ControlGuid,
(PTRACEHANDLE)&cntl->Logger,
&cntl->Flags[0],
&cntl->Level);
cntl = cntl->Next;
}
#endif //#ifdef WPP_GLOBALLOGGER
break;
}
case IRP_MN_ENABLE_EVENTS:
case IRP_MN_DISABLE_EVENTS:
{
PWNODE_HEADER Wnode;
ULONG Level;
ULONG ReturnLength;
ULONG index;
if (Context == NULL ) {
Status = STATUS_WMI_GUID_NOT_FOUND;
break;
}
if (BufferLength < sizeof(WNODE_HEADER)) {
Status = STATUS_INVALID_PARAMETER;
break;
}
//
// Initialize locals
//
Wnode = (PWNODE_HEADER)Buffer;
//
// Traverse this ProjectControlBlock's ControlBlock list and
// find the "cntl" ControlBlock which matches the Wnode GUID.
//
cntl = (PWPP_TRACE_CONTROL_BLOCK) Context;
index = 0;
while(cntl) {
if (WppIsEqualGuid(cntl->ControlGuid, &Wnode->Guid )) {
break;
}
index++;
cntl = cntl->Next;
}
if (cntl == NULL) {
Status = STATUS_WMI_GUID_NOT_FOUND;
break;
}
//
// Do the requested event action
//
Status = STATUS_SUCCESS;
if (MinorFunction == IRP_MN_DISABLE_EVENTS) {
WppDebug(0,("WppTraceCallBack: DISABLE_EVENTS\n"));
cntl->Level = 0;
cntl->Flags[0] = 0;
cntl->Logger = 0;
} else {
TRACEHANDLE lh;
lh = (TRACEHANDLE)( Wnode->HistoricalContext );
cntl->Logger = lh;
if (WppTraceWinXP == WPPTraceSuite) {
Status = pfnWppQueryTraceInformation( TraceEnableLevelClass,
&Level,
sizeof(Level),
&ReturnLength,
(PVOID)Wnode);
if (Status == STATUS_SUCCESS) {
cntl->Level = (UCHAR)Level;
}
Status = pfnWppQueryTraceInformation( TraceEnableFlagsClass,
&cntl->Flags[0],
sizeof(cntl->Flags[0]),
&ReturnLength,
(PVOID) Wnode );
} else {
cntl->Flags[0] = ((PWPP_TRACE_ENABLE_CONTEXT) &lh)->EnableFlags;
cntl->Level = (UCHAR) ((PWPP_TRACE_ENABLE_CONTEXT) &lh)->Level;
}
WppDebug(0,("WppTraceCallBack: ENABLE_EVENTS "
"LoggerId %d, Flags 0x%08X, Level 0x%02X\n",
(USHORT) cntl->Logger,
cntl->Flags[0],
cntl->Level));
}
#ifdef WPP_PRIVATE_ENABLE_CALLBACK
//
// Notify changes to flags, level for GUID
//
WPP_PRIVATE_ENABLE_CALLBACK( cntl->ControlGuid,
cntl->Logger,
(MinorFunction != IRP_MN_DISABLE_EVENTS) ? TRUE:FALSE,
cntl->Flags[0],
cntl->Level );
#endif
break;
}
case IRP_MN_ENABLE_COLLECTION:
case IRP_MN_DISABLE_COLLECTION:
{
Status = STATUS_SUCCESS;
break;
}
case IRP_MN_QUERY_ALL_DATA:
case IRP_MN_QUERY_SINGLE_INSTANCE:
case IRP_MN_CHANGE_SINGLE_INSTANCE:
case IRP_MN_CHANGE_SINGLE_ITEM:
case IRP_MN_EXECUTE_METHOD:
{
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
default:
{
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
}
return(Status);
}
VOID
NTAPI
WppClassicProviderCallback(
_In_ LPCGUID Guid,
_In_ UCHAR ControlCode,
_In_ PVOID EnableContext,
_Inout_ PVOID CallbackContext
)
/*++
Routine Description:
Enable callback function when EtwRegisterClassicProvider was used.
It happens in Windows Vista SP1 and newer.
Arguments:
Guid - provider guid.
ControlCode - code indicating operations request.
EnableContext - context from the ETW infrastructure.
CallbackContext - context from the user.
Return Value:
None.
--*/
{
PWPP_TRACE_CONTROL_BLOCK TraceCb = (PWPP_TRACE_CONTROL_BLOCK)CallbackContext;
PWPP_TRACE_ENABLE_CONTEXT TraceContext = (PWPP_TRACE_ENABLE_CONTEXT)EnableContext;
UNREFERENCED_PARAMETER (Guid);
WppDebug(0,("WppClassicProviderCallback %d\n", (int)ControlCode));
//
// Only handle enable and disable operations.
//
if ((ControlCode != EVENT_CONTROL_CODE_ENABLE_PROVIDER) &&
(ControlCode != EVENT_CONTROL_CODE_DISABLE_PROVIDER)) {
return;
}
if (ControlCode != EVENT_CONTROL_CODE_DISABLE_PROVIDER) {
TraceCb->Flags[0] = TraceContext->EnableFlags;
TraceCb->Level = (UCHAR)TraceContext->Level;
TraceCb->Logger = *((TRACEHANDLE*)TraceContext);
#if ENABLE_WPP_RECORDER
#if ENABLE_WPP_RECORDER_REPLAY && (NTDDI_VERSION >= NTDDI_WIN10_RS1)
imp_WppRecorderReplay(&WPP_CB[0], TraceCb->Logger, TraceContext->EnableFlags, TraceContext->Level);
#endif
#endif //#if ENABLE_WPP_RECORDER
WppDebug(0,("ENABLE: LoggerId=%d Flags=%08x Level=%02d\n", (int)TraceContext->LoggerId, TraceCb->Flags[0], TraceCb->Level));
} else {
TraceCb->Level = 0;
TraceCb->Flags[0] = 0;
TraceCb->Logger = 0;
}
#ifdef WPP_PRIVATE_ENABLE_CALLBACK
//
// Notify changes to flags, level for GUID
//
WppDebug(0,("WppClassicProviderCallback: calling private callback.\n"));
WPP_PRIVATE_ENABLE_CALLBACK(TraceCb->ControlGuid,
TraceCb->Logger,
ControlCode,
TraceCb->Flags[0],
TraceCb->Level);
#endif
}
#pragma warning(push)
#pragma warning(disable:4068)
WPPINIT_EXPORT
VOID
WppInitKm(
_When_(_ENABLE_WPP_RECORDER, _In_) _When_(!_ENABLE_WPP_RECORDER, _In_opt_) PDRIVER_OBJECT DriverObject,
_When_(_ENABLE_WPP_RECORDER, _In_) _When_(!_ENABLE_WPP_RECORDER, _In_opt_) PCUNICODE_STRING RegPath
)
/*++
Routine Description:
This function registers a driver with ETW as a provider of trace
events from the defined GUIDs.
Arguments:
DriverObject - Pointer to a driver object. This is required for WppRecorder
and is optional otherwise (not used unless it's for
WppRecorder).
RegPath - Optional pointer to registry path, needed for wpp recorder.
Remarks:
This function is called by the WPP_INIT_TRACING(DriverObject, RegPath) macro.
--*/
{
C_ASSERT(WPP_MAX_FLAG_LEN_CHECK);
NTSTATUS Status;
PWPP_TRACE_CONTROL_BLOCK WppReg = NULL;
PAGED_CODE();
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegPath);
if (WPP_CB != WPP_MAIN_CB) {
WPP_CB = WPP_MAIN_CB;
} else {
//
// WPP_INIT_TRACING already called
//
WppDebug(0,("Warning : WPP_INIT_TRACING already called, ignoring this one"));
return;
}
WppReg = &WPP_CB[0].Control;
WppDebug(0,("WPP Init.\n"));
if (WppTraceServer08 == WPPTraceSuite) {
//
// Windows version >= Vista SP1
//
while (WppReg) {
WppReg->RegHandle = 0;
Status = pfnEtwRegisterClassicProvider(
WppReg->ControlGuid,
0,
WppClassicProviderCallback,
(PVOID)WppReg,
&WppReg->RegHandle);
if (!NT_SUCCESS(Status)) {
WppDebug(0,("EtwRegisterClassicProvider Status = %d, ControlBlock = %p.\n", Status, WppReg));
}
WppReg = WppReg->Next;
}
} else if (WppTraceWinXP == WPPTraceSuite) {
WppReg -> Callback = WppTraceCallback;
#pragma prefast(suppress:__WARNING_BANNED_API_ARGUMENT_USAGE, "WPP generated, requires legacy providers");
Status = IoWMIRegistrationControl(
(PDEVICE_OBJECT)WppReg,
WMIREG_ACTION_REGISTER |
WMIREG_FLAG_CALLBACK |
WMIREG_FLAG_TRACE_PROVIDER
);
if (!NT_SUCCESS(Status)) {
WppDebug(0,("IoWMIRegistrationControl Status = %08X\n",Status));
}
}
#if ENABLE_WPP_RECORDER
WppAutoLogStart(&WPP_CB[0], DriverObject, RegPath);
WPP_RECORDER_INITIALIZED = WPP_MAIN_CB;
#endif
}
WPPINIT_EXPORT
VOID
WppCleanupKm(
_When_(_ENABLE_WPP_RECORDER, _In_) _When_(!_ENABLE_WPP_RECORDER, _In_opt_) PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This function deregisters a driver from ETW as provider of trace
events.
Arguments:
DriverObject - Pointer to a driver object. This is required for WppRecorder
and is optional otherwise (not used unless it's for
WppRecorder).
Remarks:
This function is called by the WPP_CLEANUP(DriverObject) macro.
--*/
{
UNREFERENCED_PARAMETER(DriverObject);
PAGED_CODE();
if (WPP_CB == (WPP_CB_TYPE*)&WPP_CB){
//
// WPP_INIT_TRACING macro has not been called
//
WppDebug(0,("Warning : WPP_CLEANUP already called, or called with out WPP_INIT_TRACING first"));
return;
}
if (WppTraceServer08 == WPPTraceSuite) {
PWPP_TRACE_CONTROL_BLOCK WppReg = &WPP_CB[0].Control;
while (WppReg) {
if (WppReg->RegHandle) {
pfnEtwUnregister(WppReg->RegHandle);
WppDebug(0,("EtwUnregister RegHandle = %lld.\n",WppReg->RegHandle));
WppReg->RegHandle = 0;
} else {
WppDebug(0,("WppCleanupKm: invalid RegHandle.\n"));
}
WppReg = WppReg->Next;
}
} else if (WppTraceWinXP == WPPTraceSuite) {
PWPP_TRACE_CONTROL_BLOCK WppReg = &WPP_CB[0].Control;
IoWMIRegistrationControl( (PDEVICE_OBJECT)WppReg,
WMIREG_ACTION_DEREGISTER |
WMIREG_FLAG_CALLBACK );
}
#if ENABLE_WPP_RECORDER
WppAutoLogStop(&WPP_CB[0], DriverObject);
WPP_RECORDER_INITIALIZED = (WPP_CB_TYPE*) &WPP_RECORDER_INITIALIZED;
#endif
WPP_CB = (WPP_CB_TYPE*)&WPP_CB;
}
#pragma warning(pop)
#define WPP_SYSTEMCONTROL(PDO)
#define WPP_SYSTEMCONTROL2(PDO, offset)
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -0,0 +1,20 @@
`**********************************************************************`
`* This is a template file for the tracewpp preprocessor. *`
`* If you need to use a custom version of this file in your project, *`
`* please clone it from this one and point WPP to it by specifying *`
`* -gen:{yourfile}*.tmh on the RUN_WPP line in your sources file. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// `Compiler.Checksum` Generated file. Do not edit.
// File created by `Compiler.Name` compiler version `Compiler.Version`
// from template `TemplateFile`
#pragma once
`INCLUDE km-StorHeader.tpl`
`INCLUDE control.tpl`
`INCLUDE tracemacro.tpl`
`IF FOUND WPP_INIT_TRACING`
` INCLUDE km-StorInit.tpl`
`ENDIF`

View file

@ -0,0 +1,188 @@
`**********************************************************************`
`* This is an include template file for the tracewpp preprocessor. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// template `TemplateFile`
#ifdef WPP_THIS_FILE
// included twice
# define WPP_ALREADY_INCLUDED
# undef WPP_THIS_FILE
#endif // #ifdef WPP_THIS_FILE
#define WPP_THIS_FILE `SourceFile.CanonicalName`
#ifndef WPP_ALREADY_INCLUDED
`* Dump the definitions specified via -D on the command line to WPP *`
`FORALL def IN MacroDefinitions`
#define `def.Name` `def.Alias`
`ENDFOR`
#include <stortrce.h>
#include <stddef.h>
#include <stdarg.h>
#include <wmistr.h>
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(_NTRTL_)
#if !defined(_NTHAL_)
// fake RTL_TIME_ZONE_INFORMATION //
typedef int RTL_TIME_ZONE_INFORMATION;
#endif
#define _WMIKM_
#endif
#ifndef WPP_TRACE
#define WPP_TRACE StorWmiTraceMessage
#endif
#if ENABLE_WPP_RECORDER
#error ENABLE_WPP_RECORDER not supported by km-StorDefault.tpl
#endif
///////////////////////////////////////////////////////////////////////////////
//
// B O R R O W E D D E F I N I T I O N S
//
///////////////////////////////////////////////////////////////////////////////
#if !defined(_NTDEF_)
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef const UNICODE_STRING *PCUNICODE_STRING;
#endif
#define TRACE_MESSAGE_SEQUENCE 1 // Message should include a sequence number
#define TRACE_MESSAGE_GUID 2 // Message includes a GUID
#define TRACE_MESSAGE_COMPONENTID 4 // Message has no GUID, Component ID instead
#define TRACE_MESSAGE_TIMESTAMP 8 // Message includes a timestamp
#define TRACE_MESSAGE_PERFORMANCE_TIMESTAMP 16 // *Obsolete* Clock type is controlled by
// the logger
#define TRACE_MESSAGE_SYSTEMINFO 32 // Message includes system information TID,PID
#define TRACE_MESSAGE_FLAG_MASK 0xFFFF // Only the lower 16 bits of flags are
// placed in the message those above 16
// bits are reserved for local processing
#ifndef TRACE_MESSAGE_MAXIMUM_SIZE
#define TRACE_MESSAGE_MAXIMUM_SIZE 8*1024 // the maximum size allowed for a single trace
#endif // message
#ifndef NTSTATUS
typedef LONG NTSTATUS;
#endif
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#endif
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) // ntsubauth
#define STATUS_WMI_GUID_NOT_FOUND ((NTSTATUS)0xC0000295L)
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
#define STATUS_INVALID_DEVICE_REQUEST ((NTSTATUS)0xC0000010L)
typedef ULONG64 TRACEHANDLE, *PTRACEHANDLE;
#ifndef TRACE_INFORMATION_CLASS_DEFINE
typedef enum _TRACE_INFORMATION_CLASS {
TraceIdClass,
TraceHandleClass,
TraceEnableFlagsClass,
TraceEnableLevelClass,
GlobalLoggerHandleClass,
EventLoggerHandleClass,
AllLoggerHandlesClass,
TraceHandleByNameClass
} TRACE_INFORMATION_CLASS;
#endif
//
// Action code for IoWMIRegistrationControl api
//
#define WMIREG_ACTION_REGISTER 1
#define WMIREG_ACTION_DEREGISTER 2
#define WMIREG_ACTION_REREGISTER 3
#define WMIREG_ACTION_UPDATE_GUIDS 4
#define WMIREG_ACTION_BLOCK_IRPS 5
///////////////////////////////////////////////////////////////////////////////
__inline ULONG64 WppQueryLogger(_In_opt_ PCWSTR LoggerName)
{
ULONG ReturnLength;
LONG Status;
ULONG64 TraceHandle;
UNICODE_STRING Buffer;
StorRtlInitUnicodeString(&Buffer, LoggerName ? LoggerName : L"stdout");
Status = StorWmiQueryTraceInformation(TraceHandleByNameClass,
(PVOID)&TraceHandle,
sizeof(TraceHandle),
&ReturnLength,
(PVOID)&Buffer
);
if (Status != STATUS_SUCCESS) {
return (ULONG64)0;
}
return TraceHandle;
}
typedef LONG (*WMIENTRY_NEW)(
_In_ UCHAR MinorFunction,
_In_opt_ PVOID DataPath,
_In_ ULONG BufferLength,
_Inout_updates_bytes_(BufferLength) PVOID Buffer,
_In_ PVOID Context,
_Out_ PULONG Size
);
typedef struct _WPP_TRACE_CONTROL_BLOCK
{
WMIENTRY_NEW Callback;
LPCGUID ControlGuid;
struct _WPP_TRACE_CONTROL_BLOCK *Next;
__int64 Logger;
PUNICODE_STRING RegistryPath;
UCHAR FlagsLen;
UCHAR Level;
USHORT Reserved;
ULONG Flags[1];
ULONG ReservedFlags;
} WPP_TRACE_CONTROL_BLOCK, *PWPP_TRACE_CONTROL_BLOCK;
VOID WppCleanupKm(_In_ PVOID TraceContext);
#define WPP_CLEANUP(DriverObject, TraceContext) WppCleanupKm(TraceContext)
//
// Callback routine to be defined by the driver, which will be called from WPP callback
// WPP will pass current valued of : GUID, Logger, Enable, Flags, and Level
//
// To activate driver must define WPP_PRIVATE_ENABLE_CALLBACK in their code, sample below
// #define WPP_PRIVATE_ENABLE_CALLBACK MyPrivateCallback;
//
typedef
VOID
(*PFN_WPP_PRIVATE_ENABLE_CALLBACK)(
_In_ LPCGUID Guid,
_In_ __int64 Logger,
_In_ BOOLEAN Enable,
_In_ ULONG Flags,
_In_ UCHAR Level);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // #ifndef WPP_ALREADY_INCLUDED

View file

@ -0,0 +1,571 @@
`**********************************************************************`
`* This is an include template file for the tracewpp preprocessor. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// template `TemplateFile`
//
// Defines a set of functions that simplifies
// kernel mode registration for tracing
//
#ifdef __cplusplus
extern "C" {
#endif
#ifndef WPPINIT_EXPORT
#define WPPINIT_EXPORT
#endif
#ifndef WppDebug
#define WppDebug(a,b)
#endif
WPPINIT_EXPORT
NTSTATUS
WppTraceCallback(
_In_ UCHAR MinorFunction,
_In_opt_ PVOID DataPath,
_In_ ULONG BufferLength,
_Inout_updates_bytes_(BufferLength) PVOID Buffer,
_Inout_ PVOID Context,
_Out_ PULONG Size
);
WPPINIT_EXPORT
VOID
WppInitKm(
_In_opt_ PVOID DriverObject,
_In_ PVOID InitInfo
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, WppTraceCallback)
#pragma alloc_text( PAGE, WppInitKm)
#pragma alloc_text( PAGE, WppCleanupKm)
#endif // ALLOC_PRAGMA
// define annotation record that will carry control information to pdb (in case somebody needs it)
WPP_FORCEINLINE void WPP_CONTROL_ANNOTATION() {
#if !defined(WPP_NO_ANNOTATIONS)
#ifndef WPP_TMC_ANNOT_SUFIX
#ifdef WPP_PUBLIC_TMC
#define WPP_TMC_ANNOT_SUFIX ,L"PUBLIC_TMF:"
#else
#define WPP_TMC_ANNOT_SUFIX
#endif
#endif
# define WPP_DEFINE_CONTROL_GUID(Name,Guid,Bits) __annotation(L"TMC:", WPP_GUID_WTEXT Guid, _WPPW(WPP_STRINGIZE(Name)) Bits WPP_TMC_ANNOT_SUFIX);
# define WPP_DEFINE_BIT(Name) , _WPPW(#Name)
WPP_CONTROL_GUIDS
# undef WPP_DEFINE_BIT
# undef WPP_DEFINE_CONTROL_GUID
#endif
}
#define WPP_NEXT(Name) ((WPP_TRACE_CONTROL_BLOCK*) \
(WPP_XGLUE(WPP_CTL_, WPP_EVAL(Name)) + 1 == WPP_LAST_CTL ? 0:WPP_MAIN_CB + WPP_XGLUE(WPP_CTL_, WPP_EVAL(Name)) + 1))
WPP_CB_TYPE WPP_MAIN_CB[WPP_LAST_CTL];
__inline void WPP_INIT_CONTROL_ARRAY(WPP_CB_TYPE* Arr) {
#define WPP_DEFINE_CONTROL_GUID(Name,Guid,Bits) \
Arr->Control.Callback = NULL; \
Arr->Control.ControlGuid = WPP_XGLUE4(&WPP_, ThisDir, _CTLGUID_, WPP_EVAL(Name)); \
Arr->Control.Next = WPP_NEXT(WPP_EVAL(Name)); \
Arr->Control.RegistryPath= NULL; \
Arr->Control.FlagsLen = WPP_FLAG_LEN; \
Arr->Control.Level = 0; \
Arr->Control.Reserved = 0; \
Arr->Control.Flags[0] = 0; \
++Arr;
#define WPP_DEFINE_BIT(BitName) L" " L ## #BitName
WPP_CONTROL_GUIDS
#undef WPP_DEFINE_BIT
#undef WPP_DEFINE_CONTROL_GUID
}
#undef WPP_INIT_STATIC_DATA
#define WPP_INIT_STATIC_DATA WPP_INIT_CONTROL_ARRAY(WPP_MAIN_CB)
// define WPP_INIT_TRACING. For performance reasons turn off during
// static analysis compilation with Static Driver Verifier (SDV).
#ifndef _SDV_
#define WPP_INIT_TRACING(DriverObject, RegPath, InitInfo) \
{ \
WppDebug(0,("WPP_INIT_TRACING: &WPP_CB[0] %p\n", &WPP_MAIN_CB[0])); \
WPP_INIT_STATIC_DATA; \
( WPP_CONTROL_ANNOTATION(), \
WPP_MAIN_CB[0].Control.RegistryPath = NULL, \
UNREFERENCED_PARAMETER(RegPath), \
WppInitKm( DriverObject, InitInfo ) \
); \
}
#else
#define WPP_INIT_TRACING(DriverObject, RegPath, InitInfo)
#endif
#define WMIREG_FLAG_CALLBACK 0x80000000 // not exposed in DDK
#ifndef WMIREG_FLAG_TRACE_PROVIDER
#define WMIREG_FLAG_TRACE_PROVIDER 0x00010000
#endif
__inline int WppIsEqualGuid(_In_ const GUID* g1, _In_ const GUID* g2)
{
const ULONG* p1 = (const ULONG*)g1;
const ULONG* p2 = (const ULONG*)g2;
return p1[0] == p2[0] && p1[1] == p2[1] && p1[2] == p2[2] && p1[3] == p2[3];
}
#define WPP_MAX_COUNT_REGISTRATION_GUID 63
WPPINIT_EXPORT
NTSTATUS
WppTraceCallback(
_In_ UCHAR MinorFunction,
_In_opt_ PVOID DataPath,
_In_ ULONG BufferLength,
_Inout_updates_bytes_(BufferLength) PVOID Buffer,
_Inout_ PVOID Context,
_Out_ PULONG Size
)
/*++
Routine Description:
This function is the callback WMI calls when we register and when our
events are enabled or disabled.
Arguments:
MinorFunction - specifies the type of callback (register, event enable/disable)
DataPath - varies depending on the ActionCode
BufferLength - size of the Buffer parameter
Buffer - in/out buffer where we read from or write to depending on the type
of callback
Context - the pointer private struct WPP_TRACE_CONTROL_BLOCK
Size - output parameter to receive the amount of data written into Buffer
Return Value:
NTSTATUS code indicating success/failure
Comments:
if return value is STATUS_BUFFER_TOO_SMALL and BufferLength >= 4,
then first ulong of buffer contains required size
--*/
{
PWPP_TRACE_CONTROL_BLOCK cntl;
NTSTATUS Status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(DataPath);
WppDebug(0,("WppTraceCallBack 0x%08X %p\n", MinorFunction, Context));
*Size = 0;
switch(MinorFunction)
{
case IRP_MN_REGINFO:
{
PWMIREGINFOW WmiRegInfo;
PCUNICODE_STRING RegPath;
PWCHAR StringPtr;
ULONG RegistryPathOffset;
ULONG BufferNeeded;
ULONG GuidCount = 0;
//
// Initialize locals
//
cntl = (PWPP_TRACE_CONTROL_BLOCK)Context;
WmiRegInfo = (PWMIREGINFO)Buffer;
RegPath = cntl->RegistryPath;
//
// Count the number of guid to be identified.
//
while(cntl) { GuidCount++; cntl = cntl->Next; }
if (GuidCount > WPP_MAX_COUNT_REGISTRATION_GUID){
Status = STATUS_INVALID_PARAMETER;
break;
}
WppDebug(0,("WppTraceCallBack: GUID count %d\n", GuidCount));
//
// Calculate buffer size need to hold all info.
// Calculate offset to where RegistryPath parm will be copied.
//
if (RegPath == NULL)
{
RegistryPathOffset = 0;
BufferNeeded = FIELD_OFFSET(WMIREGINFOW, WmiRegGuid) +
GuidCount * sizeof(WMIREGGUIDW);
} else {
RegistryPathOffset = FIELD_OFFSET(WMIREGINFOW, WmiRegGuid) +
GuidCount * sizeof(WMIREGGUIDW);
BufferNeeded = RegistryPathOffset +
RegPath->Length + sizeof(USHORT);
}
//
// If the provided buffer is large enough, then fill with info.
//
if (BufferNeeded <= BufferLength)
{
ULONG i;
StorMemSet(Buffer, 0, BufferLength);
//
// Fill in the WMIREGINFO
//
WmiRegInfo->BufferSize = BufferNeeded;
WmiRegInfo->RegistryPath = RegistryPathOffset;
WmiRegInfo->GuidCount = GuidCount;
if (RegPath != NULL) {
StringPtr = (PWCHAR)((PUCHAR)Buffer + RegistryPathOffset);
*StringPtr++ = RegPath->Length;
StorMoveMemory(StringPtr, RegPath->Buffer, RegPath->Length);
}
//
// Fill in the WMIREGGUID
//
cntl = (PWPP_TRACE_CONTROL_BLOCK) Context;
for (i=0; i<GuidCount; i++) {
WmiRegInfo->WmiRegGuid[i].Guid = *cntl->ControlGuid;
WmiRegInfo->WmiRegGuid[i].Flags = WMIREG_FLAG_TRACE_CONTROL_GUID |
WMIREG_FLAG_TRACED_GUID;
cntl->Level = 0;
cntl->Flags[0] = 0;
WppDebug(0,("Control GUID::%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
cntl->ControlGuid->Data1,
cntl->ControlGuid->Data2,
cntl->ControlGuid->Data3,
cntl->ControlGuid->Data4[0],
cntl->ControlGuid->Data4[1],
cntl->ControlGuid->Data4[2],
cntl->ControlGuid->Data4[3],
cntl->ControlGuid->Data4[4],
cntl->ControlGuid->Data4[5],
cntl->ControlGuid->Data4[6],
cntl->ControlGuid->Data4[7]
));
cntl = cntl->Next;
}
Status = STATUS_SUCCESS;
*Size = BufferNeeded;
} else {
Status = STATUS_BUFFER_TOO_SMALL;
if (BufferLength >= sizeof(ULONG)) {
*((PULONG)Buffer) = BufferNeeded;
*Size = sizeof(ULONG);
}
}
#ifdef WPP_GLOBALLOGGER
// Check if Global logger is active
cntl = (PWPP_TRACE_CONTROL_BLOCK) Context;
while(cntl) {
StorWppInitGlobalLogger(
cntl->ControlGuid,
(PTRACEHANDLE)&cntl->Logger,
&cntl->Flags[0],
&cntl->Level);
cntl = cntl->Next;
}
#endif //#ifdef WPP_GLOBALLOGGER
break;
}
case IRP_MN_ENABLE_EVENTS:
case IRP_MN_DISABLE_EVENTS:
{
PWNODE_HEADER Wnode;
ULONG Level;
ULONG ReturnLength;
ULONG index;
if (Context == NULL ) {
Status = STATUS_WMI_GUID_NOT_FOUND;
break;
}
if (BufferLength < sizeof(WNODE_HEADER)) {
Status = STATUS_INVALID_PARAMETER;
break;
}
//
// Initialize locals
//
Wnode = (PWNODE_HEADER)Buffer;
//
// Traverse this ProjectControlBlock's ControlBlock list and
// find the "cntl" ControlBlock which matches the Wnode GUID.
//
cntl = (PWPP_TRACE_CONTROL_BLOCK) Context;
index = 0;
while(cntl) {
if (WppIsEqualGuid(cntl->ControlGuid, &Wnode->Guid )) {
break;
}
index++;
cntl = cntl->Next;
}
if (cntl == NULL) {
Status = STATUS_WMI_GUID_NOT_FOUND;
break;
}
//
// Do the requested event action
//
Status = STATUS_SUCCESS;
if (MinorFunction == IRP_MN_DISABLE_EVENTS) {
WppDebug(0,("WppTraceCallBack: DISABLE_EVENTS\n"));
cntl->Level = 0;
cntl->Flags[0] = 0;
cntl->Logger = 0;
} else {
TRACEHANDLE lh;
lh = (TRACEHANDLE)( Wnode->HistoricalContext );
cntl->Logger = lh;
Status = StorWmiQueryTraceInformation( TraceEnableLevelClass,
&Level,
sizeof(Level),
&ReturnLength,
(PVOID)Wnode);
if (Status == STATUS_SUCCESS) {
cntl->Level = (UCHAR)Level;
}
Status = StorWmiQueryTraceInformation( TraceEnableFlagsClass,
&cntl->Flags[0],
sizeof(cntl->Flags[0]),
&ReturnLength,
(PVOID) Wnode );
WppDebug(0,("WppTraceCallBack: ENABLE_EVENTS "
"LoggerId %d, Flags 0x%08X, Level 0x%02X\n",
(USHORT) cntl->Logger,
cntl->Flags[0],
cntl->Level));
}
#ifdef WPP_PRIVATE_ENABLE_CALLBACK
//
// Notify changes to flags, level for GUID
//
WPP_PRIVATE_ENABLE_CALLBACK( cntl->ControlGuid,
cntl->Logger,
(MinorFunction != IRP_MN_DISABLE_EVENTS) ? TRUE:FALSE,
cntl->Flags[0],
cntl->Level );
#endif
break;
}
case IRP_MN_ENABLE_COLLECTION:
case IRP_MN_DISABLE_COLLECTION:
{
Status = STATUS_SUCCESS;
break;
}
case IRP_MN_QUERY_ALL_DATA:
case IRP_MN_QUERY_SINGLE_INSTANCE:
case IRP_MN_CHANGE_SINGLE_INSTANCE:
case IRP_MN_CHANGE_SINGLE_ITEM:
case IRP_MN_EXECUTE_METHOD:
{
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
default:
{
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
}
return(Status);
}
#pragma warning(push)
#pragma warning(disable:4068)
WPPINIT_EXPORT
VOID
WppInitKm(
_In_opt_ PVOID DriverObject,
_In_ PVOID InitInfo
)
/*++
Routine Description:
This function registers a driver with ETW as a provider of trace
events from the defined GUIDs.
Arguments:
DriverObject - Pointer to a driver object. This is required for WppRecorder
and is optional otherwise (not used unless it's for
WppRecorder).
InitInfo - Pointer to STORAGE_TRACE_INIT_INFO.
Remarks:
This function is called by the
WPP_INIT_TRACING(DriverObject, RegPath, InitInfo) macro.
--*/
{
C_ASSERT(WPP_MAX_FLAG_LEN_CHECK);
NTSTATUS Status;
PWPP_TRACE_CONTROL_BLOCK WppReg = NULL;
UNREFERENCED_PARAMETER(DriverObject);
if (WPP_CB != WPP_MAIN_CB) {
WPP_CB = WPP_MAIN_CB;
} else {
//
// WPP_INIT_TRACING already called
//
WppDebug(0,("Warning : WPP_INIT_TRACING already called, ignoring this one"));
return;
}
WppReg = &WPP_CB[0].Control;
WppDebug(0,("WPP Init.\n"));
if (StorInitTracing(InitInfo) == STATUS_SUCCESS) {
WppReg -> Callback = WppTraceCallback;
Status = StorIoWMIRegistrationControl(
WppReg,
WMIREG_ACTION_REGISTER |
WMIREG_FLAG_CALLBACK |
WMIREG_FLAG_TRACE_PROVIDER
);
if (!NT_SUCCESS(Status)) {
WppDebug(0,("StorIoWMIRegistrationControl Status = %08X\n",Status));
}
}
}
WPPINIT_EXPORT
VOID
WppCleanupKm(
_In_ PVOID TraceContext
)
/*++
Routine Description:
This function deregisters a driver from ETW as provider of trace
events.
Arguments:
TraceContext - The STORAGE_TRACE_INIT_INFO.TraceContext value.
Remarks:
This function is called by the WPP_CLEANUP(DriverObject, TraceContext) macro.
--*/
{
StorCleanupTracing(TraceContext);
if (WPP_CB == (WPP_CB_TYPE*)&WPP_CB){
//
// WPP_INIT_TRACING macro has not been called
//
WppDebug(0,("Warning : WPP_CLEANUP already called, or called with out WPP_INIT_TRACING first"));
return;
}
PWPP_TRACE_CONTROL_BLOCK WppReg = &WPP_CB[0].Control;
StorIoWMIRegistrationControl(WppReg,
WMIREG_ACTION_DEREGISTER |
WMIREG_FLAG_CALLBACK );
WPP_CB = (WPP_CB_TYPE*)&WPP_CB;
}
#pragma warning(pop)
#define WPP_SYSTEMCONTROL(PDO)
#define WPP_SYSTEMCONTROL2(PDO, offset)
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -0,0 +1,9 @@
`**********************************************************************`
`* This is a template file for the tracewpp preprocessor. *`
`* If you need to use a custom version of this file in your project, *`
`* please clone it from this one and point WPP to it by specifying *`
`* -gen:{yourfile}*.tmh on the RUN_WPP line in your sources file. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
`INCLUDE km-default.tpl`

View file

@ -0,0 +1,9 @@
`**********************************************************************`
`* This is a template file for the tracewpp preprocessor. *`
`* If you need to use a custom version of this file in your project, *`
`* please clone it from this one and point WPP to it by specifying *`
`* -gen:{yourfile}*.tmh on the RUN_WPP line in your sources file. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
`INCLUDE km-default.tpl`

View file

@ -0,0 +1,133 @@
<?xml version="1.0" standalone="yes"?>
<!--
`**********************************************************************`
`* This is a template file for the tracewpp preprocessor. *`
`* If you need to use a custom version of this file in your project, *`
`* please clone it from this one and point WPP to it by specifying *`
`* -gen:{yourfile}*.tmh on the RUN_WPP line in your sources file. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
Checksum="`Compiler.Checksum`" Generated file. Do not edit.
File created by `Compiler.Name` compiler version `Compiler.Version`
from template `TemplateFile`
`ENCODING UTF-8`
***************************************************************************
PREVIEW: Behavior of the -man option, the `TemplateFile` template, and the
associated configuration options may change in future versions of tracewpp.
***************************************************************************
-->
<instrumentationManifest
xmlns="http://schemas.microsoft.com/win/2004/08/events"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events"
>
<instrumentation>
<events>
`FORALL ControlGuid IN ControlGuids`
` FORALL TraceGuid IN ControlGuid.TraceGuids`
<provider
name="`ControlGuid.Name`_`TraceGuid.Normalized`"
controlGuid="{`ControlGuid.Text`}"
guid="{`TraceGuid.Text`}"
symbol="`ControlGuid.Symbol``IF TraceGuid No`_`TraceGuid.No``ENDIF`"
resourceFileName="`Manifest.ResourceFileName`"
messageFileName="`Manifest.MessageFileName`"
>
` IF ControlGuid Levels`
<levels>
` FORALL Level IN ControlGuid.Levels`
<level value="`Level.Value`" name="`Level.Name`" symbol="`Level.Symbol`" />
` ENDFOR Level`
</levels>
` ENDIF ControlGuid Levels`
` IF ControlGuid Keywords`
<keywords>
` FORALL Keyword IN ControlGuid.Keywords`
<keyword mask="`Keyword.Mask`" name="`Keyword.Name`" symbol="`Keyword.Symbol`" />
` ENDFOR Keyword`
</keywords>
` ENDIF ControlGuid Keywords`
` IF TraceGuid Maps`
<maps>
` FORALL Type IN TraceGuid.Types WHERE ItemSet`
<bitMap name="`Type.Name`">
` FORALL Item IN Type.MapItems`
<map value="0x`Item.HexValue`" message="$(string.S`Item.MessageId`)" />
` ENDFOR Item`
</bitMap>
` ENDFOR Type`
` FORALL Type IN TraceGuid.Types WHERE ItemList`
<valueMap name="`Type.Name`">
` FORALL Item IN Type.MapItems`
<map value="`Item.Value`" message="$(string.S`Item.MessageId`)" />
` ENDFOR Item`
</valueMap>
` ENDFOR Type`
</maps>
` ENDIF TraceGuid Maps`
<events>
` FORALL Message IN TraceGuid.Messages`
<event
` IF Message EventName`
name="`Message.XmlEventName`"
` ENDIF Message EventName`
attributes='`Message.Attributes`'
value="`Message.MsgNo`"
` IF Message Version`
version="`Message.Version`"
` ENDIF Message Version`
level="`Message.Level`"
keywords="`Message.Keywords`"
symbol="`Message.Name`"
message="$(string.S`Message.ManifestTextId`)"
` IF Message Arguments`
template="T`Message.TemplateId`"
` ENDIF Message Arguments`
/>
` ENDFOR Message`
</events>
<templates>
` FORALL Template IN TraceGuid.Templates`
<template tid="T`Template.Id`">
` FORALL Argument IN Template.Arguments`
<data
` IF Argument NameSpecified`
name="`Argument.XmlName`"
` ENDIF Argument NameSpecified`
` IF Argument !NameSpecified`
name="A`Argument.No`_`Argument.XmlName`"
` ENDIF Argument !NameSpecified`
inType="`Argument.InType`"
` IF Argument MapName`
map="`Argument.MapName`"
` ENDIF Argument MapName`
` IF Argument OutType`
outType="`Argument.OutType`"
` ENDIF Argument OutType`
` IF Argument Length`
length="`Argument.Length`"
` ENDIF Argument Length`
/>
` ENDFOR Argument`
</template>
` ENDFOR Template`
</templates>
</provider>
` ENDFOR TraceGuid`
`ENDFOR ControlGuid`
</events>
</instrumentation>
<localization>
<resources culture="`Manifest.Culture`">
<stringTable>
`FORALL NameId IN StringTable`
<string
id="S`NameId.Id`"
value="`NameId.XmlName`"
/>
`ENDFOR Message`
</stringTable>
</resources>
</localization>
</instrumentationManifest>

View file

@ -0,0 +1,23 @@
`**********************************************************************`
`* This is a template file for the tracewpp preprocessor. *`
`* If you need to use a custom version of this file in your project, *`
`* please clone it from this one and point WPP to it by specifying *`
`* -gen:{yourfile}*.tmh on the RUN_WPP line in your sources file. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// `Compiler.Checksum` Generated file. Do not edit.
// File created by `Compiler.Name` compiler version `Compiler.Version`
// from template `TemplateFile`
`FORALL Guid IN TraceGuids`
`Guid.Text` `Guid.Comment`
`FORALL Msg in Guid.Messages`
#typev `Msg.Name` `Msg.MsgNo` "`Msg.Text`"
{
`FORALL Arg IN Msg.Arguments`
`Arg.Name`, `Arg.MofType` // `Arg.No`
`ENDFOR Arg`
}
`ENDFOR Msg`
`ENDFOR Guid`

View file

@ -0,0 +1,254 @@
`**********************************************************************`
`* This is a template file for the tracewpp preprocessor. *`
`* If you need to use a custom version of this file in your project, *`
`* please clone it from this one and point WPP to it by specifying *`
`* -gen:{yourfile}*.tmh on the RUN_WPP line in your sources file. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// Checksum="`Compiler.Checksum`" Generated file. Do not edit.
// File created by `Compiler.Name` compiler version `Compiler.Version`
// from template `TemplateFile`
// ***************************************************************************
// PREVIEW: Behavior of the -man option, the `TemplateFile` template, and the
// associated configuration options may change in future versions of tracewpp.
// ***************************************************************************
#pragma once
// Define the macros (if any) specified on the tracewpp command line.
// -- Begin macro definitions.
`FORALL def IN MacroDefinitions`
#define `def.Name` `def.Alias`
`ENDFOR`
// -- End macro definitions.
// The "`Manifest.HeaderFileName`" file should be generated by running the
// MC.exe tool: mc.exe -um `Manifest.Arguments`
#include "`Manifest.HeaderFileName`"
`IF FOUND WPP_INIT_TRACING`
#ifndef WPP_INIT_TRACING
#define WPP_INIT_TRACING(...) WPP_INIT()
#endif
`ENDIF FOUND WPP_INIT_TRACING`
// WPP_LOG_ALWAYS:
// Called for each event: WPP_LOG_ALWAYS(EX, MSG, arg1, arg2, arg3...) Other()
// If defined, the definition needs to include a trailing comma or semicolon.
// In addition, you will need to define a WPP_EX_[args](args...) macro to
// extract any needed information from the other arguments (e.g. LEVEL).
#ifndef WPP_LOG_ALWAYS
#define WPP_LOG_ALWAYS(...)
#endif
// WPP_DEBUG:
// Called for each enabled event: WPP_DEBUG((MSG, arg1, arg2, arg3...)), Other()
// Potential definition: printf MsgArgs
// Definition should not include any trailing comma or semicolon.
#ifdef WPP_DEBUG
#define WPP_INVOKE_WPP_DEBUG(MsgArgs) WPP_DEBUG(MsgArgs)
#else // WPP_DEBUG
#define WPP_INVOKE_WPP_DEBUG(MsgArgs) (void)0
#endif // WPP_DEBUG
#ifndef WPP_FORCEINLINE
#define WPP_FORCEINLINE __forceinline
#endif
#define WPP_THIS_FILE `SourceFile.CanonicalName`
#define WPP_FLATTEN(...) __VA_ARGS__
#define WPP_INVOKE_MCMACRO(f, args) f args
#define WPP_EVAL(x) x
#define WPP_GLUE5(a, b, c, d, e) a ## b ## c ## d ## e
#define WPP_XGLUE5(a, b, c, d, e) WPP_GLUE5(a, b, c, d, e)
#define WPP_(Id) WPP_XGLUE5(WPP_, Id, _, WPP_THIS_FILE, __LINE__)
// Calls EventRegister_[ProviderName] for each provider in the generated manifest.
#define WPP_INIT() \
{ \
`FORALL ControlGuid IN ControlGuids`
` FORALL TraceGuid IN ControlGuid.TraceGuids`
EventRegister_`ControlGuid.Symbol``IF TraceGuid No`_`TraceGuid.No``ENDIF`(); \
` ENDFOR TraceGuid`
`ENDFOR ControlGuid`
}
// Calls EventUnregister_[ProviderName] for each provider in the generated manifest.
#define WPP_CLEANUP() \
{ \
`FORALL ControlGuid IN ControlGuids`
` FORALL TraceGuid IN ControlGuid.TraceGuids`
EventUnregister_`ControlGuid.Symbol``IF TraceGuid No`_`TraceGuid.No``ENDIF`(); \
` ENDFOR TraceGuid`
`ENDFOR ControlGuid`
}
// WPP_ENABLED helpers
`FORALL ControlGuid IN ControlGuids`
` FORALL TraceGuid IN ControlGuid.TraceGuids WHERE !No`
#define WPP_ENABLED_`ControlGuid.Symbol`(level, keyword) \
(`ControlGuid.Symbol`_Context.IsEnabled && McGenLevelKeywordEnabled(&`ControlGuid.Symbol`_Context, (level), (keyword)))
` ENDFOR TraceGuid`
`ENDFOR ControlGuid`
// Adapters
#ifndef WPP_ADAPTER_PUSTR
#ifdef WPP_CHECK_FOR_NULL_STRING
# define WPP_ADAPTER_PUSTR(x) \
(x) ? (x)->Length/2 : 4, \
(x) ? (x)->Buffer : L"NULL"
#else // WPP_CHECK_FOR_NULL_STRING
# define WPP_ADAPTER_PUSTR(x) (x)->Length/2, (x)->Buffer
#endif // WPP_CHECK_FOR_NULL_STRING
#endif // WPP_ADAPTER_PUSTR
#ifndef WPP_ADAPTER_PCSTR
#ifdef WPP_CHECK_FOR_NULL_STRING
# define WPP_ADAPTER_PCSTR(x) \
(x) ? (x)->Length : 4, \
(x) ? (x)->Buffer : "NULL"
#else // WPP_CHECK_FOR_NULL_STRING
# define WPP_ADAPTER_PCSTR(x) (x)->Length, (x)->Buffer
#endif // WPP_CHECK_FOR_NULL_STRING
#endif // WPP_ADAPTER_PCSTR
#ifndef WPP_ADAPTER_PSID
# define WPP_ADAPTER_PSID(pSid) (const SID*)pSid
#endif // WPP_ADAPTER_PSID
#ifndef WPP_ADAPTER_PVOID
# define WPP_ADAPTER_PVOID(intptr) (const void*)intptr
#endif // WPP_ADAPTER_PVOID
#ifndef WPP_ADAPTER_PFILETIME
# define WPP_ADAPTER_PFILETIME(i64) (const FILETIME*)&i64
#endif // WPP_ADAPTER_PFILETIME
#ifndef WPP_ADAPTER_CCCC
# define WPP_ADAPTER_CCCC(val) (const char*)&val
#endif // WPP_ADAPTER_CCCC
#ifndef WPP_ADAPTER_BIN
# define WPP_ADAPTER_BIN(x) (x).Length, (const unsigned char*)(x).Buffer
#endif // WPP_ADAPTER_BIN
#ifdef __cplusplus
#ifndef WPP_ADAPTER_CPPSTR
#define WPP_ADAPTER_CPPSTR(x) static_cast<USHORT>(x.size()), x.c_str()
#endif // WPP_ADAPTER_CPPSTR
#ifndef WPP_ADAPTER_CPPVEC
#define WPP_ADAPTER_CPPVEC(x) static_cast<USHORT>(x.size()), x.data()
#endif // WPP_ADAPTER_CPPVEC
#endif // __cplusplus
#ifndef WPP_BINARY_def
# define WPP_BINARY_def
typedef struct tagWPP_BINARY
{
_Field_size_bytes_(Length) void const* Buffer;
USHORT Length;
} WPP_BINARY;
#endif // WPP_BINARY_def
#ifndef WPP_BINARY_func
# define WPP_BINARY_func
WPP_FORCEINLINE WPP_BINARY
WppBinary(_In_reads_bytes_(Length) void const* Buffer, USHORT Length)
{
WPP_BINARY data;
data.Buffer = Buffer;
data.Length = Length;
return data;
}
#endif // WPP_BINARY_func
// Functions
`FORALL f IN Funcs WHERE !DoubleP && !MsgArgs && !NoMsg`
#undef `f.Name`
#ifdef __INTELLISENSE__
#define `f.Name`(`f.FixedArgs`MSG, ...) ((void)(MSG, ## __VA_ARGS__))
#else
#define `f.Name` WPP_(CALL)
#endif
`ENDFOR`
`FORALL f IN Funcs WHERE !DoubleP && !MsgArgs && NoMsg`
#undef `f.Name`
#ifdef __INTELLISENSE__
#define `f.Name`(`f.FixedArgs`) ((void)0)
#else
#define `f.Name` WPP_(CALL)
#endif
`ENDFOR`
`FORALL f IN Funcs WHERE DoubleP && !MsgArgs`
#undef `f.Name`
#ifdef __INTELLISENSE__
#define `f.Name`(ARGS) ((void)ARGS)
#else
#define `f.Name`(ARGS) WPP_(CALL) ARGS
#endif
`ENDFOR`
`FORALL f IN Funcs WHERE MsgArgs`
#undef `f.Name`
#ifdef __INTELLISENSE__
#define `f.Name`(`f.FixedArgs`MSGARGS) ((void)(MSGARGS))
#else
#define `f.Name`(`f.FixedArgs`MSGARGS) WPP_(CALL)(`f.FixedArgs`MSGARGS)
#endif
`ENDFOR`
`FORALL r IN Reorder`
#undef WPP_REORDER_`r.Name`
#define WPP_REORDER_`r.Name`(`r.Arguments`) `r.PermutationNoLeadingComma`
`ENDFOR`
// Messages
`FORALL i IN Messages WHERE !MsgArgs`
// `i.Name`
#ifndef WPP`i.GooId`_PRE
# define WPP`i.GooId`_PRE(`i.GooArgs`)
#endif
#ifndef WPP`i.GooId`_POST
# define WPP`i.GooId`_POST(`i.GooArgs`)
#endif
WPP_FORCEINLINE ULONG WPP_MFUNC_`i.Name`(`i.FuncArgDecls`)
{ return WPP_INVOKE_MCMACRO(`Manifest.Prefix``i.Name`_AssumeEnabled, (`i.FuncArgExprs`)); }
#define WPP_CALL_`i.Name`(`i.FixedArgs``i.MacroArgs`) \
WPP_LOG_ALWAYS(WPP_EX`i.GooId`(`i.GooVals`), `i.DbgMacroArgs`) \
WPP`i.GooId`_PRE(`i.GooVals`) \
(( \
MCGEN_EVENT_ENABLED(`i.Name`) \
? WPP_INVOKE_WPP_DEBUG((`i.DbgMacroArgs`)), \
WPP_MFUNC_`i.Name`(`i.MacroExprsNoLeadingComma`) \
: 0 \
)) \
WPP`i.GooId`_POST(`i.GooVals`)
`ENDFOR i IN Messages WHERE !MsgArgs`
`FORALL i IN Messages WHERE MsgArgs`
// `i.Name`
#ifndef WPP`i.GooId`_PRE
# define WPP`i.GooId`_PRE(`i.GooArgs`)
#endif
#ifndef WPP`i.GooId`_POST
# define WPP`i.GooId`_POST(`i.GooArgs`)
#endif
WPP_FORCEINLINE ULONG WPP_MFUNC_`i.Name`(`i.FuncArgDecls`)
{ return WPP_INVOKE_MCMACRO(`Manifest.Prefix``i.Name`_AssumeEnabled, (`i.FuncArgExprs`)); }
#define WPP_CALL_`i.Name`(`i.FixedArgs` MSGARGS) \
WPP_LOG_ALWAYS(WPP_EX`i.GooId`(`i.GooVals`), WPP_FLATTEN MSGARGS) \
WPP`i.GooId`_PRE(`i.GooVals`) \
(( \
MCGEN_EVENT_ENABLED(`i.Name`) \
? WPP_INVOKE_WPP_DEBUG(MSGARGS), \
WPP_MFUNC_`i.Name` WPP_EVAL((`i.SyntheticExprsNoLeadingComma`WPP_REORDER_`i.ReorderSig` MSGARGS)) \
: 0 \
)) \
WPP`i.GooId`_POST(`i.GooVals`)
`ENDFOR i IN Messages WHERE MsgArgs`

View file

@ -0,0 +1,32 @@
`**********************************************************************`
`* This is a template file for the tracewpp preprocessor. *`
`* If you need to use a custom version of this file in your project, *`
`* please clone it from this one and point WPP to it by specifying *`
`* -gen:{yourfile}*.tmh on the RUN_WPP line in your sources file. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// `Compiler.Checksum` Generated file. Do not edit.
// File created by `Compiler.Name` compiler version `Compiler.Version`
// from template `TemplateFile`
// PLEASE NOTE:
// Using simple.tpl for production environment without overriding
// WPP_ENABLED and WPP_LOGGER macro is NOT RECOMMENDED.
//
// If WPP_ENABLED is not provided, the logging is always on
// If WPP_LOGGER is not provided, traces always go to the logger named stdout (if it is running)
//
`INCLUDE header.tpl`
`INCLUDE stdout.tpl`
`INCLUDE tracemacro.tpl`
`IF FOUND WPP_INIT_TRACING`
#ifndef WPP_INIT_TRACING
#define WPP_INIT_TRACING(...) ((void)0)
#endif
#ifndef WPP_CLEANUP
#define WPP_CLEANUP(...) ((void)0)
#endif
`ENDIF`

View file

@ -0,0 +1,41 @@
`**********************************************************************`
`* This is an include template file for the tracewpp preprocessor. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// template `TemplateFile`
`FORALL f IN Funcs`
#ifndef WPP`f.GooId`_LOGGER
#define WPP`f.GooId`_LOGGER(`f.GooArgs`) // `f.Name`
#endif
`ENDFOR`
#ifndef WPP_LOGGER_ARG
# define WPP_LOGGER_ARG
#endif
#ifndef WPP_GET_LOGGER
#define WPP_GET_LOGGER WppGetLogger()
__inline TRACEHANDLE WppGetLogger()
{
static TRACEHANDLE Logger;
if (Logger) {return Logger;}
return Logger = WppQueryLogger(0);
}
#endif
#define WPP_GLUE(a, b) a ## b
#define _WPPW(x) WPP_GLUE(L, x)
#ifndef WPP_CHECK_INIT
#define WPP_CHECK_INIT
#endif
#ifndef WPP_ENABLED
# define WPP_ENABLED() 1
#endif
#ifndef WPP_LEVEL_ENABLED
# define WPP_LEVEL_ENABLED(LEVEL) 1
#endif

View file

@ -0,0 +1,289 @@
#
# Default configuration options for tracewpp for users who are using TCHARs
# and have UNICODE defined. This is the same as defaultwpp.ini except that:
#
# - %s is for WCHAR* instead of CHAR*.
# - %S is for CHAR* instead of WCHAR*.
# - %Z is for UNICODE_STRING* instead of ANSI_STRING*.
#
# Refer to the defaultwpp.ini file for additional information about the syntax
# of WPP INI files.
################################
# Default WPP configuration
################################
# our basic arithmetic types
DEFINE_SIMPLE_TYPE( XINT64, signed __int64, ItemLongLongX, "I64x", i, 8, win:HexInt64);
DEFINE_SIMPLE_TYPE( XXINT64,signed __int64, ItemLongLongXX,"I64X", i, 8, win:HexInt64);
DEFINE_SIMPLE_TYPE( OINT64, signed __int64, ItemLongLongO, "I64o", i, 8, win:HexInt64);
DEFINE_SIMPLE_TYPE( SBYTE, signed char, ItemChar, "c", c, 1, win:Int8);
DEFINE_SIMPLE_TYPE( SSHORT, signed short, ItemShort, "hd", h, 2, win:Int16);
DEFINE_SIMPLE_TYPE( SINT, signed int, ItemLong, "d", d, 4, win:Int32);
DEFINE_SIMPLE_TYPE( SLONG, signed long, ItemLong, "ld", l, 4, win:Int32);
DEFINE_SIMPLE_TYPE( SINT64, signed __int64, ItemLongLong, "I64d", i, 8, win:Int64);
DEFINE_SIMPLE_TYPE( UBYTE, unsigned char, ItemChar, "c", C, 1, win:UInt8);
DEFINE_SIMPLE_TYPE( USHORT, unsigned short, ItemShort, "hu", H, 2, win:UInt16);
DEFINE_SIMPLE_TYPE( UINT, unsigned int, ItemLong, "u", D, 4, win:UInt32);
DEFINE_SIMPLE_TYPE( ULONG, unsigned long, ItemLong, "lu", L, 4, win:UInt32);
DEFINE_SIMPLE_TYPE( UINT64, unsigned __int64,ItemULongLong, "I64u", I, 8, win:UInt64);
DEFINE_SIMPLE_TYPE( DOUBLE, double, ItemDouble, "s", g, 8, win:Double);
DEFINE_FLAVOR( UCHAR, UBYTE, ItemUChar, "c", win:UInt8; outType=xs:string);
DEFINE_FLAVOR( SCHAR, SBYTE, ItemChar, "c", win:Int8; outType=xs:string);
# arch dependent types
DEFINE_SIMPLE_TYPE( SLONGPTR, LONG_PTR, ItemPtr, "Id", p, 7, win:Pointer; adapter=WPP_ADAPTER_PVOID; outType=xs:long);
DEFINE_SIMPLE_TYPE( ULONGPTR, ULONG_PTR, ItemPtr, "Iu", P, 7, win:Pointer; adapter=WPP_ADAPTER_PVOID; outType=xs:unsignedLong);
DEFINE_SIMPLE_TYPE( PTR, const void*, ItemPtr, "p", q, 6, win:Pointer);
DEFINE_SIMPLE_TYPE( HANDLE, const void*, ItemPtr, "p", q, 6, win:Pointer);
DEFINE_SIMPLE_TYPE_PTR(GUID, LPCGUID, ItemGuid, "s", _guid_, 4, win:GUID);
# Predefined constants.
# Typically used in the function's prefix or suffix.
DEFINE_SIMPLE_TYPE( COMPNAME,,, "__COMPNAME__",,);
DEFINE_SIMPLE_TYPE( FILE,,, "__FILE__",,);
DEFINE_SIMPLE_TYPE( LINE,,, "__LINE__",,);
DEFINE_SIMPLE_TYPE( SPACE,,, " ",,);
# The following can be used in the prefix or suffix.
DEFINE_SIMPLE_TYPE( FUNC,,, "%!FUNC!",,);
DEFINE_SIMPLE_TYPE( LEVEL,,, "%!LEVEL!",,);
DEFINE_SIMPLE_TYPE( KEYWORDS,,, "%!FLAGS!",,);
DEFINE_SIMPLE_TYPE( STDPREFIX,,, "%0",,); # Only works at start of prefix. Ignored for manifest-based WPP.
DEFINE_SIMPLE_TYPE( MOD,,, "%1!s!",,);
DEFINE_SIMPLE_TYPE( TYP,,, "%2!s!",,);
DEFINE_SIMPLE_TYPE( TID,,, "%3!x!",,);
DEFINE_SIMPLE_TYPE( NOW,,, "%4!s!",,);
DEFINE_SIMPLE_TYPE( SEQ,,, "%7!u!",,);
DEFINE_SIMPLE_TYPE( PID,,, "%8!x!",,);
DEFINE_SIMPLE_TYPE( CPU,,, "%9!u!",,);
# Complex types
# Strings and SID require custom processing
DEFINE_CPLX_TYPE(ASTR, WPP_LOGASTR, LPCSTR, ItemString, "s", s, 1, win:AnsiString);
DEFINE_CPLX_TYPE(ARSTR, WPP_LOGASTR, LPCSTR, ItemRString, "s", s, 1, win:AnsiString);
DEFINE_CPLX_TYPE(ARWSTR,WPP_LOGWSTR, LPCWSTR, ItemRWString,"s", S, 2, win:UnicodeString);
DEFINE_CPLX_TYPE(WSTR, WPP_LOGWSTR, LPCWSTR, ItemWString, "s", S, 2, win:UnicodeString);
DEFINE_CPLX_TYPE(CSTR, WPP_LOGPCSTR, const CSTRING*, ItemPString, "s", z, 1, win:CountedAnsiString; adapter=WPP_ADAPTER_PCSTR);
DEFINE_CPLX_TYPE(USTR, WPP_LOGPUSTR, PCUNICODE_STRING, ItemPWString,"s", Z, 2, win:CountedUnicodeString; adapter=WPP_ADAPTER_PUSTR);
DEFINE_CPLX_TYPE(ANSTR, WPP_LOGPCSTR, const ANSI_STRING*, ItemPString, "s", aZ, 1, win:CountedAnsiString; adapter=WPP_ADAPTER_PCSTR);
DEFINE_CPLX_TYPE(sid, WPP_LOGPSID, PSID, ItemSid, "s", _sid_, 0, win:SID; adapter=WPP_ADAPTER_PSID);
# Raw binary data. Usage: DoTraceMessage(FLAG, "%!BIN!", WppBinary(pointer, size));
DEFINE_CPLX_TYPE(BIN, WPP_LOGCSTR, WPP_BINARY, ItemHexDump, "s", _zb_, 1, win:CountedBinary; adapter=WPP_ADAPTER_BIN);
# C++ string types
DEFINE_CPLX_TYPE(str, WPP_LOGCPPSTR, const std::string&, ItemPString, "s", _str_, 0, win:CountedAnsiString; adapter=WPP_ADAPTER_CPPSTR);
DEFINE_CPLX_TYPE(wstr, WPP_LOGCPPSTR, const std::wstring&, ItemPWString, "s", _wstr_, 0, win:CountedUnicodeString; adapter=WPP_ADAPTER_CPPSTR);
DEFINE_CPLX_TYPE(sv, WPP_LOGCPPVEC, const std::string_view&, ItemPString, "s", _sv_, 0, win:CountedAnsiString; adapter=WPP_ADAPTER_CPPVEC);
DEFINE_CPLX_TYPE(wsv, WPP_LOGCPPVEC, const std::wstring_view&,ItemPWString, "s", _wsv_, 0, win:CountedUnicodeString; adapter=WPP_ADAPTER_CPPVEC);
# Define printf-compatible types
DEFINE_FLAVOR(e, DOUBLE,,);
DEFINE_FLAVOR(E, DOUBLE,,);
DEFINE_FLAVOR(f, DOUBLE,,);
DEFINE_FLAVOR(g, DOUBLE,,);
DEFINE_FLAVOR(G, DOUBLE,,);
DEFINE_FLAVOR(c, SCHAR,,);
DEFINE_FLAVOR(hc, SCHAR,,);
DEFINE_FLAVOR(lc, SSHORT,,);
DEFINE_FLAVOR(wc, SSHORT,,);
DEFINE_FLAVOR(C, SSHORT,,);
DEFINE_FLAVOR(s, WSTR,,); # %s is WIDE with TCHARUNIwpp.ini.
DEFINE_FLAVOR(hs, ASTR,,);
DEFINE_FLAVOR(S, ASTR,,); # %S is ANSI with TCHARUNIwpp.ini.
DEFINE_FLAVOR(ws, WSTR,,);
DEFINE_FLAVOR(ls, WSTR,,);
DEFINE_FLAVOR(hi, SSHORT,,);
DEFINE_FLAVOR(hd, SSHORT,,);
DEFINE_FLAVOR(hu, USHORT,,"u");
DEFINE_FLAVOR(hx, USHORT,,"x", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(hX, USHORT,,"X", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(ho, USHORT,,"o", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(Id, ULONGPTR,,"Id");
DEFINE_FLAVOR(Iu, ULONGPTR,,"Iu");
DEFINE_FLAVOR(Ix, ULONGPTR,,"Ix", win:Pointer; adapter=WPP_ADAPTER_PVOID);
DEFINE_FLAVOR(IX, ULONGPTR,,"IX", win:Pointer; adapter=WPP_ADAPTER_PVOID);
DEFINE_FLAVOR(Io, ULONGPTR,,"Io", win:Pointer; adapter=WPP_ADAPTER_PVOID);
DEFINE_FLAVOR(i, SINT,,);
DEFINE_FLAVOR(d, SINT,,);
DEFINE_FLAVOR(u, UINT,,"u");
DEFINE_FLAVOR(x, UINT,,"x", win:HexInt32);
DEFINE_FLAVOR(X, UINT,,"X", win:HexInt32);
DEFINE_FLAVOR(o, UINT,,"o", win:HexInt32);
DEFINE_FLAVOR(cccc, SINT, ItemChar4, "s", win:AnsiString; length=4; adapter=WPP_ADAPTER_CCCC);
DEFINE_FLAVOR(li, SLONG,,);
DEFINE_FLAVOR(ld, SLONG,,);
DEFINE_FLAVOR(lu, ULONG,,"u");
DEFINE_FLAVOR(lx, ULONG,,"x", win:HexInt32);
DEFINE_FLAVOR(lX, ULONG,,"X", win:HexInt32);
DEFINE_FLAVOR(lo, ULONG,,"o", win:HexInt32);
DEFINE_FLAVOR(I64d, SINT64,,);
DEFINE_FLAVOR(I64u, UINT64,,);
DEFINE_FLAVOR(I64x, XINT64,,"I64x");
DEFINE_FLAVOR(I64X, XXINT64,,"I64X");
DEFINE_FLAVOR(I64o, OINT64,,"I64o");
DEFINE_FLAVOR(lld, SINT64,,);
DEFINE_FLAVOR(llu, UINT64,,);
DEFINE_FLAVOR(llx, XINT64,,"I64x");
DEFINE_FLAVOR(llX, XXINT64,,"I64X");
DEFINE_FLAVOR(llo, OINT64,,"I64o");
DEFINE_FLAVOR(p, PTR,,);
DEFINE_FLAVOR(Z, USTR,,); # %Z is PCUNICODE_STRING with TCHARUNIwpp.ini.
DEFINE_FLAVOR(wZ, USTR,,);
DEFINE_FLAVOR(z, CSTR,,);
DEFINE_FLAVOR(hZ, CSTR,,);
# default formats for those who don't care to provide their own strings
DEFINE_FLAVOR(XBYTE, SBYTE,, "02x", win:UInt8; outType=win:HexInt8);
DEFINE_FLAVOR(OBYTE, SBYTE,, "o", win:UInt8; outType=win:HexInt8);
DEFINE_FLAVOR(XSHORT, SSHORT,, "04hX", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(OSHORT, SSHORT,, "ho", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(XINT, SINT,, "08x", win:HexInt32);
DEFINE_FLAVOR(OINT, SINT,, "o", win:HexInt32);
DEFINE_FLAVOR(XLONG, SLONG,, "08lX", win:HexInt32);
DEFINE_FLAVOR(OLONG, SLONG,, "lo", win:HexInt32);
DEFINE_FLAVOR(XLONGPTR, SLONGPTR,,"Ix", win:Pointer; adapter=WPP_ADAPTER_PVOID);
DEFINE_FLAVOR(OLONGPTR, SLONGPTR,,"Io", win:Pointer; adapter=WPP_ADAPTER_PVOID);
# special formats
DEFINE_FLAVOR(IPADDR, UINT, ItemIPAddr, "s", win:UInt32; outType=win:IPv4);
DEFINE_FLAVOR(PORT, USHORT, ItemPort, "s", win:UInt16; outType=win:Port);
DEFINE_FLAVOR(STATUS, SINT, ItemNTSTATUS, "s", win:HexInt32; outType=win:NTSTATUS);
DEFINE_FLAVOR(WINERROR, UINT, ItemWINERROR, "s", win:UInt32; outType=win:Win32Error);
DEFINE_FLAVOR(HRESULT, SINT, ItemHRESULT, "s", win:Int32; outType=win:HResult);
# Note: The %!NDIS_STATUS! and %!NDIS_OID! types work for for TMF-based WPP but
# do not work well with manifest-based WPP. Manifest-based ETW decoders will
# treat %!NDIS_STATUS! and %!NDIS_OID! items as HexInt32 items.
DEFINE_FLAVOR(NDIS_STATUS, SINT, ItemNDIS_STATUS, "s", win:HexInt32);
DEFINE_FLAVOR(NDIS_OID, UINT, ItemNDIS_OID, "s", win:HexInt32);
DEFINE_FLAVOR(ipaddr,IPADDR,,);
DEFINE_FLAVOR(port,PORT,,);
DEFINE_FLAVOR(status,STATUS,,);
DEFINE_FLAVOR(hresult,HRESULT,,);
DEFINE_FLAVOR(winerr,WINERROR,,);
DEFINE_FLAVOR(guid,GUID,,);
# time related stuff
DEFINE_FLAVOR(TIMESTAMP, SINT64, ItemTimestamp, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
DEFINE_FLAVOR(TIME, SINT64, ItemTimestamp, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
DEFINE_FLAVOR(DATE, SINT64, ItemTimestamp, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
DEFINE_FLAVOR(WAITTIME, SINT64, ItemTimestamp, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
DEFINE_FLAVOR(due, SINT64, ItemWaitTime, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
DEFINE_FLAVOR(delta, SINT64, ItemTimeDelta, "s");
DEFINE_FLAVOR(datetime, SINT64, ItemTimestamp, "s", win:FILETIME; adapter=WPP_ADAPTER_PFILETIME);
# enumeration types
DEFINE_FLAVOR(ItemListByte, SBYTE, ItemListByte, "s", win:UInt8);
DEFINE_FLAVOR(ItemListShort, SSHORT, ItemListShort, "s", win:UInt16);
DEFINE_FLAVOR(ItemListLong, SLONG, ItemListLong, "s", win:UInt32);
DEFINE_FLAVOR(ItemSetByte, UBYTE, ItemSetByte, "s", win:UInt8; outType=win:HexInt8);
DEFINE_FLAVOR(ItemSetShort, USHORT, ItemSetShort, "s", win:UInt16; outType=win:HexInt16);
DEFINE_FLAVOR(ItemSetLong, ULONG, ItemSetLong, "s", win:HexInt32);
# Note: The "ItemEnum" and "ItemFlagsEnum" types work for for TMF-based WPP but
# do not work well with manifest-based WPP. Manifest-based ETW decoders will
# treat types based on "ItemEnum" as UInt32 and will treat types based on
# "ItemFlagsEnum" as HexInt32. For better results, use ItemListLong,
# ItemSetLong, or a "begin_wpp enum" block for your enumeration.
DEFINE_FLAVOR(ItemEnum, ULONG, ItemEnum, "s");
DEFINE_FLAVOR(ItemFlagsEnum, ULONG, ItemFlagsEnum, "s", win:HexInt32);
DEFINE_FLAVOR(CLSID, GUID, ItemCLSID, "s");
DEFINE_FLAVOR(LIBID, GUID, ItemLIBID, "s");
DEFINE_FLAVOR(IID, GUID, ItemIID, "s");
CUSTOM_TYPE(b1, ItemSetByte(1,2,3,4,5,6,7,8) );
CUSTOM_TYPE(b2, ItemSetShort(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16) );
CUSTOM_TYPE(b4, ItemSetLong(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32) );
CUSTOM_TYPE(bool, ItemListLong(false,true) );
CUSTOM_TYPE(bool16, ItemListShort(false,true) );
CUSTOM_TYPE(bool8, ItemListByte(false,true) );
CUSTOM_TYPE(BOOLEAN, ItemListByte(FALSE,TRUE) );
CUSTOM_TYPE(irql, ItemListByte(Low,APC,DPC) );
CUSTOM_TYPE(pnpmn, ItemListByte(IRP_MN_START_DEVICE,IRP_MN_QUERY_REMOVE_DEVICE,IRP_MN_REMOVE_DEVICE,IRP_MN_CANCEL_REMOVE_DEVICE,IRP_MN_STOP_DEVICE,IRP_MN_QUERY_STOP_DEVICE,IRP_MN_CANCEL_STOP_DEVICE,IRP_MN_QUERY_DEVICE_RELATIONS,IRP_MN_QUERY_INTERFACE,IRP_MN_QUERY_CAPABILITIES,IRP_MN_QUERY_RESOURCES,IRP_MN_QUERY_RESOURCE_REQUIREMENTS,IRP_MN_QUERY_DEVICE_TEXT,IRP_MN_FILTER_RESOURCE_REQUIREMENTS,IRP_MN_PNP_14,IRP_MN_READ_CONFIG,IRP_MN_WRITE_CONFIG,IRP_MN_EJECT,IRP_MN_SET_LOCK,IRP_MN_QUERY_ID,IRP_MN_QUERY_PNP_DEVICE_STATE,IRP_MN_QUERY_BUS_INFORMATION,IRP_MN_DEVICE_USAGE_NOTIFICATION,IRP_MN_SURPRISE_REMOVAL) );
CUSTOM_TYPE(sysctrl, ItemListByte(IRP_MN_QUERY_ALL_DATA,IRP_MN_QUERY_SINGLE_INSTANCE, IRP_MN_CHANGE_SINGLE_INSTANCE, IRP_MN_CHANGE_SINGLE_ITEM, IRP_MN_ENABLE_EVENTS, IRP_MN_DISABLE_EVENTS, IRP_MN_ENABLE_COLLECTION, IRP_MN_DISABLE_COLLECTION, IRP_MN_REGINFO, IRP_MN_EXECUTE_METHOD, IRP_MN_Reserved_0a, IRP_MN_REGINFO_EX) );
CUSTOM_TYPE(pnpmj, ItemListByte(IRP_MJ_CREATE,IRP_MJ_CREATE_NAMED_PIPE,IRP_MJ_CLOSE,IRP_MJ_READ,IRP_MJ_WRITE,IRP_MJ_QUERY_INFORMATION,IRP_MJ_SET_INFORMATION,IRP_MJ_QUERY_EA,IRP_MJ_SET_EA,IRP_MJ_FLUSH_BUFFERS,IRP_MJ_QUERY_VOLUME_INFORMATION,IRP_MJ_SET_VOLUME_INFORMATION,IRP_MJ_DIRECTORY_CONTROL,IRP_MJ_FILE_SYSTEM_CONTROL,IRP_MJ_DEVICE_CONTROL,IRP_MJ_INTERNAL_DEVICE_CONTROL,IRP_MJ_SHUTDOWN,IRP_MJ_LOCK_CONTROL,IRP_MJ_CLEANUP,IRP_MJ_CREATE_MAILSLOT,IRP_MJ_QUERY_SECURITY,IRP_MJ_SET_SECURITY,IRP_MJ_POWER,IRP_MJ_SYSTEM_CONTROL,IRP_MJ_DEVICE_CHANGE,IRP_MJ_QUERY_QUOTA,IRP_MJ_SET_QUOTA,IRP_MJ_PNP) );
# Built-in levels (for use with manifests)
DEFINE_BUILTIN_LEVEL(win:LogAlways, 0, WINEVENT_LEVEL_LOG_ALWAYS);
DEFINE_BUILTIN_LEVEL(win:Critical, 1, WINEVENT_LEVEL_CRITICAL, TRACE_LEVEL_CRITICAL);
DEFINE_BUILTIN_LEVEL(win:Error, 2, WINEVENT_LEVEL_ERROR, TRACE_LEVEL_ERROR);
DEFINE_BUILTIN_LEVEL(win:Warning, 3, WINEVENT_LEVEL_WARNING, TRACE_LEVEL_WARNING);
DEFINE_BUILTIN_LEVEL(win:Informational, 4, WINEVENT_LEVEL_INFO, TRACE_LEVEL_INFORMATION);
DEFINE_BUILTIN_LEVEL(win:Verbose, 5, WINEVENT_LEVEL_VERBOSE, TRACE_LEVEL_VERBOSE);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel6, 6, WINEVENT_LEVEL_RESERVED_6);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel7, 7, WINEVENT_LEVEL_RESERVED_7);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel8, 8, WINEVENT_LEVEL_RESERVED_8);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel9, 9, WINEVENT_LEVEL_RESERVED_9);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel10, 10, WINEVENT_LEVEL_RESERVED_10);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel11, 11, WINEVENT_LEVEL_RESERVED_11);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel12, 12, WINEVENT_LEVEL_RESERVED_12);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel13, 13, WINEVENT_LEVEL_RESERVED_13);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel14, 14, WINEVENT_LEVEL_RESERVED_14);
DEFINE_BUILTIN_LEVEL(win:ReservedLevel15, 15, WINEVENT_LEVEL_RESERVED_15);
# Built-in keywords (for use with manifests)
DEFINE_BUILTIN_KEYWORD(win:AnyKeyword, 0x0, WINEVT_KEYWORD_ANY);
DEFINE_BUILTIN_KEYWORD(win:ResponseTime, 0x0001000000000000, WINEVENT_KEYWORD_RESPONSE_TIME);
DEFINE_BUILTIN_KEYWORD(win:WDIContext, 0x0002000000000000, WINEVENT_KEYWORD_WDI_CONTEXT);
DEFINE_BUILTIN_KEYWORD(win:WDIDiag, 0x0004000000000000, WINEVENT_KEYWORD_WDI_DIAG);
DEFINE_BUILTIN_KEYWORD(win:SQM, 0x0008000000000000, WINEVENT_KEYWORD_SQM);
DEFINE_BUILTIN_KEYWORD(win:AuditFailure, 0x0010000000000000, WINEVENT_KEYWORD_AUDIT_FAILURE);
DEFINE_BUILTIN_KEYWORD(win:AuditSuccess, 0x0020000000000000, WINEVENT_KEYWORD_AUDIT_SUCCESS);
DEFINE_BUILTIN_KEYWORD(win:CorrelationHint, 0x0040000000000000, WINEVENT_KEYWORD_CORRELATION_HINT);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword56, 0x0100000000000000, WINEVENT_KEYWORD_RESERVED_56);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword57, 0x0200000000000000, WINEVENT_KEYWORD_RESERVED_57);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword58, 0x0400000000000000, WINEVENT_KEYWORD_RESERVED_58);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword59, 0x0800000000000000, WINEVENT_KEYWORD_RESERVED_59);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword60, 0x1000000000000000, WINEVENT_KEYWORDE_RESERVED_60);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword61, 0x2000000000000000, WINEVENT_KEYWORD_RESERVED_61);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword62, 0x4000000000000000, WINEVENT_KEYWORD_RESERVED_62);
DEFINE_BUILTIN_KEYWORD(win:ReservedKeyword63, 0x8000000000000000, WINEVENT_KEYWORD_RESERVED_63);
# default tracing macros
FUNC DoTraceMessage(LEVEL,MSG,...); # Uses WPP-provided ENABLED/LOGGER macros
FUNC DoDebugTrace(TRACELEVEL,MSG,...); # Requires used-defined ENABLED/LOGGER macros
# default prefix (use traceprt default)
USEPREFIX(*,"%!STDPREFIX!"); # traceprt will add standard prefix
# the source file that calls WPP_INIT_TRACING is given special treatment
WPP_FLAGS(-lookfor:WPP_INIT_TRACING);

View file

@ -0,0 +1,571 @@
`**********************************************************************`
`* This is an include template file for the tracewpp preprocessor. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// template `TemplateFile`
// This template expects:
// WPP_THIS_FILE defined (see header.tpl)
// WPP_LOGGER_ARG defined
// WPP_GET_LOGGER defined
// WPP_ENABLED() defined
#ifdef __cplusplus
extern "C" {
#endif
#ifdef WPP_ALREADY_INCLUDED
#undef WPP_LOCAL_TraceGuids
#undef WPP_INVOKE_WPP_DEBUG
#else // WPP_ALREADY_INCLUDED
#ifndef NO_CHECK_FOR_NULL_STRING
#ifndef WPP_CHECK_FOR_NULL_STRING
#define WPP_CHECK_FOR_NULL_STRING
#endif
#endif // NO_CHECK_FOR_NULL_STRING
#define WPP_FLATTEN(...) __VA_ARGS__
#define WPP_GLUE5(a, b, c, d, e) a ## b ## c ## d ## e
#define WPP_XGLUE5(a, b, c, d, e) WPP_GLUE5(a, b, c, d, e)
#define WPP_(Id) WPP_XGLUE5(WPP_, Id, _, WPP_THIS_FILE, __LINE__)
#ifndef WPP_INLINE
#define WPP_INLINE DECLSPEC_NOINLINE __inline
#endif
#ifndef WPP_FORCEINLINE
#define WPP_FORCEINLINE __forceinline
#endif
#endif // WPP_ALREADY_INCLUDED
#ifdef WPP_NO_ANNOTATIONS
#define WPP_ANNOTATE(x)
#else // WPP_NO_ANNOTATIONS
`FORALL Msg IN Messages WHERE MsgIsPrivate`
#ifdef WPP_PUBLIC_`Msg.FlagValue`
#define WPP_PUBLIC_ANNOT_`Msg.Name`
#endif
`ENDFOR`
`FORALL Msg IN Messages WHERE MsgIsPublic`
#define WPP_PUBLIC_ANNOT_`Msg.Name`
`ENDFOR`
#ifdef WPP_EMIT_FUNC_NAME
#define WPP_FUNC_NAME L" FUNC=" _WPPW(__FUNCTION__)
#else // WPP_EMIT_FUNC_NAME
#define WPP_FUNC_NAME
#endif // WPP_EMIT_FUNC_NAME
`FORALL Msg IN Messages`
#ifdef WPP_USER_MSG_GUID
# define WPP_ANNOTATE_`Msg.Name`_FINAL(P, File, Name, ...) __annotation( \
L ## P, \
WPP_GUID_WTEXT WPP_USER_MSG_GUID L"`CurrentDir` // SRC=" _WPPW(File) \
L" MJ=`ENV MAJORCOMP` MN=`ENV MINORCOMP`", \
L"#typev " _WPPW(Name) \
L" `Msg.MsgNo` \"`Msg.Text`\" // `Msg.Indent` `Msg.GooPairs`" \
WPP_FUNC_NAME, \
__VA_ARGS__)
#else // WPP_USER_MSG_GUID
# define WPP_ANNOTATE_`Msg.Name`_FINAL(P, File, Name, ...) __annotation( \
L ## P, \
L"`Msg.Guid` `CurrentDir` // SRC=" _WPPW(File) \
L" MJ=`ENV MAJORCOMP` MN=`ENV MINORCOMP`", \
L"#typev " _WPPW(Name) \
L" `Msg.MsgNo` \"`Msg.Text`\" // `Msg.Indent` `Msg.GooPairs`" \
WPP_FUNC_NAME, \
__VA_ARGS__)
#endif // WPP_USER_MSG_GUID
#ifdef WPP_PUBLIC_ANNOT_`Msg.Name`
# define WPP_ANNOTATE_`Msg.Name` WPP_ANNOTATE_`Msg.Name`_FINAL( \
"TMF:", \
"Unknown_cxx00", \
"Unknown_cxx00", \
L"{"`FORALL Arg IN Msg.Arguments`, \
L"Arg, `Arg.MofType` -- `Arg.No`" `ENDFOR Arg`, \
L"}", \
L"PUBLIC_TMF:")
# ifndef WPP_PUBLIC_TMC
# define WPP_PUBLIC_TMC // Adds "PUBLIC_TMF:" to the control guid annotation
# endif
#else // WPP_PUBLIC_ANNOT_`Msg.Name`
# define WPP_ANNOTATE_`Msg.Name` WPP_ANNOTATE_`Msg.Name`_FINAL( \
"TMF:", \
"`SourceFile.Name`", \
"`Msg.Name`", \
L"{"`FORALL Arg IN Msg.Arguments`, \
L"`Arg.Name`, `Arg.MofType` -- `Arg.No`" `ENDFOR Arg`, \
L"}")
#endif // WPP_PUBLIC_ANNOT_`Msg.Name`
`ENDFOR`
# define WPP_ANNOTATE(x) WPP_ANNOTATE_ ## x,
#endif // WPP_NO_ANNOTATIONS
`IF TraceGuids !Empty`
#ifdef WPP_USER_MSG_GUID
#define WPP_LOCAL_MSG_VAR(Guid) WPP_XGLUE3(WPP_, WPP_GUID_NORM Guid, _Traceguids)
#define WPP_LOCAL_MSG_GUID(Guid) \
extern const __declspec(selectany) GUID WPP_LOCAL_MSG_VAR(Guid)[] = { WPP_GUID_STRUCT Guid }
WPP_LOCAL_MSG_GUID(WPP_USER_MSG_GUID);
#define WPP_LOCAL_TraceGuids WPP_LOCAL_MSG_VAR(WPP_USER_MSG_GUID)
#else // WPP_USER_MSG_GUID
#define WPP_LOCAL_TraceGuids WPP_`FORALL Guid IN TraceGuids``Guid.Normalized`_`ENDFOR`Traceguids
extern const __declspec(selectany) GUID WPP_LOCAL_TraceGuids[] = {`FORALL Guid IN TraceGuids` `Guid.Struct`, `ENDFOR`};
#endif // WPP_USER_MSG_GUID
`ENDIF TraceGuids !Empty`
#ifndef WPP_ALREADY_INCLUDED
#ifndef WPP_TRACE_OPTIONS
enum { WPP_TRACE_OPTIONS =
TRACE_MESSAGE_SEQUENCE |
TRACE_MESSAGE_GUID |
TRACE_MESSAGE_SYSTEMINFO |
TRACE_MESSAGE_TIMESTAMP };
#endif // WPP_TRACE_OPTIONS
#ifndef WPP_LOGPAIR_SEPARATOR
# define WPP_LOGPAIR_SEPARATOR ,
#endif
#ifndef WPP_LOGPAIR_SIZET
# define WPP_LOGPAIR_SIZET SIZE_T
#endif
#ifndef WPP_LOGPAIR
# define WPP_LOGPAIR(_Size, _Addr) (_Addr),((WPP_LOGPAIR_SIZET)(_Size))WPP_LOGPAIR_SEPARATOR
#endif
#define WPP_LOGTYPEVAL(_Type, _Value) WPP_LOGPAIR(sizeof(_Type), &(_Value))
#define WPP_LOGTYPEPTR(_Value) WPP_LOGPAIR(sizeof(*(_Value)), (_Value))
// Marshalling macros.
#ifndef WPP_LOGASTR
# ifdef WPP_CHECK_FOR_NULL_STRING
# define WPP_LOGASTR(_value) WPP_LOGPAIR( \
(_value) ? strlen(_value) + 1 : 5, \
(_value) ? (_value) : "NULL" )
# else // WPP_CHECK_FOR_NULL_STRING
# define WPP_LOGASTR(_value) WPP_LOGPAIR( \
strlen(_value) + 1, \
_value )
# endif // WPP_CHECK_FOR_NULL_STRING
#endif // WPP_LOGASTR
#ifndef WPP_LOGWSTR
# ifdef WPP_CHECK_FOR_NULL_STRING
# define WPP_LOGWSTR(_value) WPP_LOGPAIR( \
((_value) ? wcslen(_value) + 1 : 5) * sizeof(WCHAR), \
(_value) ? (_value) : L"NULL" )
# else // WPP_CHECK_FOR_NULL_STRING
# define WPP_LOGWSTR(_value) WPP_LOGPAIR( \
(wcslen(_value) + 1) * sizeof(WCHAR), \
_value )
# endif // WPP_CHECK_FOR_NULL_STRING
#endif // WPP_LOGWSTR
#ifndef WPP_LOGPGUID
# define WPP_LOGPGUID(_value) WPP_LOGPAIR( sizeof(GUID), (_value) )
#endif // WPP_LOGPGUID
#ifndef WPP_LOGPSID
# ifdef WPP_CHECK_FOR_NULL_STRING
# define WPP_LOGPSID(_value) WPP_LOGPAIR( \
(_value) && WPP_IsValidSid(_value) ? WPP_GetLengthSid(_value) : 5, \
(_value) && WPP_IsValidSid(_value) ? (_value) : (void const*)"NULL")
# else // WPP_CHECK_FOR_NULL_STRING
# define WPP_LOGPSID(_value) WPP_LOGPAIR( \
WPP_GetLengthSid(_value), \
(_value) )
#endif // WPP_CHECK_FOR_NULL_STRING
#endif // WPP_LOGPSID
#ifndef WPP_LOGCSTR
# define WPP_LOGCSTR(_x) \
WPP_LOGPAIR( sizeof(USHORT), &(_x).Length ) \
WPP_LOGPAIR( (USHORT)(_x).Length, (USHORT)(_x).Length ? (_x).Buffer : "" )
#endif // WPP_LOGCSTR
#ifndef WPP_LOGUSTR
# define WPP_LOGUSTR(_x) \
WPP_LOGPAIR( sizeof(USHORT), &(_x).Length ) \
WPP_LOGPAIR( (USHORT)(_x).Length, (USHORT)(_x).Length ? (_x).Buffer : L"" )
#endif // WPP_LOGUSTR
#ifndef WPP_LOGPUSTR
#ifdef WPP_CHECK_FOR_NULL_STRING
# define WPP_LOGPUSTR(_x) \
WPP_LOGPAIR( \
sizeof(USHORT), \
(_x) ? &(_x)->Length : (void const*)L"\x08" ) \
WPP_LOGPAIR( \
(_x) ? (USHORT)(_x)->Length : 0x08, \
(_x) && (USHORT)(_x)->Length ? (_x)->Buffer : L"NULL" )
#else // WPP_CHECK_FOR_NULL_STRING
# define WPP_LOGPUSTR(_x) WPP_LOGUSTR(*(_x))
#endif // WPP_CHECK_FOR_NULL_STRING
#endif // WPP_LOGPUSTR
#ifndef WPP_LOGPCSTR
#ifdef WPP_CHECK_FOR_NULL_STRING
# define WPP_LOGPCSTR(_x) \
WPP_LOGPAIR( \
sizeof(USHORT), \
(_x) ? &(_x)->Length : (void const*)L"\x04" ) \
WPP_LOGPAIR( \
(_x) ? (USHORT)(_x)->Length : 0x04, \
(_x) && (USHORT)(_x)->Length ? (_x)->Buffer : "NULL" )
#else // WPP_CHECK_FOR_NULL_STRING
# define WPP_LOGPCSTR(_x) WPP_LOGCSTR(*(_x))
#endif // WPP_CHECK_FOR_NULL_STRING
#endif // WPP_LOGPCSTR
#ifdef __cplusplus
#ifndef WPP_POINTER_TO_USHORT
struct WppPointerToUshort
{
USHORT m_val;
WPP_FORCEINLINE explicit WppPointerToUshort(USHORT val) : m_val(val) {}
WPP_FORCEINLINE USHORT const* get() const { return &m_val; }
};
#define WPP_POINTER_TO_USHORT(val) (WppPointerToUshort((val)).get())
#endif // WPP_POINTER_TO_USHORT
#ifndef WPP_LOGCPPSTR
#define WPP_LOGCPPSTR(_value) \
WPP_LOGPAIR( \
sizeof(USHORT), \
WPP_POINTER_TO_USHORT((USHORT)((_value).size()*sizeof(*(_value).c_str()))) ) \
WPP_LOGPAIR( \
(USHORT)((_value).size()*sizeof(*(_value).c_str())), \
(_value).c_str() )
#endif // WPP_LOGCPPSTR
#ifndef WPP_LOGCPPVEC
#define WPP_LOGCPPVEC(_value) \
WPP_LOGPAIR( \
sizeof(USHORT), \
WPP_POINTER_TO_USHORT((USHORT)((_value).size()*sizeof(*(_value).data()))) ) \
WPP_LOGPAIR( \
(USHORT)((_value).size()*sizeof(*(_value).data())), \
(_value).data() + ((_value).data() == NULL) )
#endif // WPP_LOGCPPVEC
#endif // __cplusplus
#ifndef WPP_BINARY_def
# define WPP_BINARY_def
typedef struct tagWPP_BINARY
{
_Field_size_bytes_(Length) void const* Buffer;
USHORT Length;
} WPP_BINARY;
#endif // WPP_BINARY_def
#ifndef WPP_BINARY_func
# define WPP_BINARY_func
WPP_FORCEINLINE WPP_BINARY
WppBinary(_In_reads_bytes_(Length) void const* Buffer, USHORT Length)
{
WPP_BINARY data;
data.Buffer = Buffer;
data.Length = Length;
return data;
}
#endif // WPP_BINARY_func
#endif // WPP_ALREADY_INCLUDED
#ifndef WPP_ENABLE_FLAG_BIT
#define WPP_ENABLE_FLAG_BIT(flag) (WPP_CB[((flag) >> 16)].Control).Flags[( (0xFFFF & ((flag)-1) ) / 32)] & (1 << ( ((flag)-1) & 31 ))
#endif
`FORALL i IN TypeSigSet WHERE !UnsafeArgs`
#ifndef WPP_SF_`i.Name`_def
# define WPP_SF_`i.Name`_def
WPP_INLINE void WPP_SF_`i.Name`(WPP_LOGGER_ARG unsigned short id, LPCGUID TraceGuid`i.Arguments`)
{ WPP_TRACE(WPP_GET_LOGGER, WPP_TRACE_OPTIONS, (LPGUID)TraceGuid, id, `i.LogArgs` (void*)0); }
#endif // WPP_SF_`i.Name`_def
#if ENABLE_WPP_RECORDER
#if ENABLE_WPP_TRACE_FILTERING_WITH_WPP_RECORDER
//
// Generate the WPP_RECORDER_AND_TRACE_SF_`i.Name` function
//
#ifndef WPP_RECORDER_AND_TRACE_SF_`i.Name`_def
#define WPP_RECORDER_AND_TRACE_SF_`i.Name`_def
WPP_INLINE
VOID
WPP_RECORDER_AND_TRACE_SF_`i.Name`(
WPP_LOGGER_ARG
BOOLEAN wppEnabled,
BOOLEAN recorderEnabled,
PVOID AutoLogContext,
UCHAR level,
ULONG flags,
USHORT id,
LPCGUID traceGuid
`i.Arguments`
)
{
if (wppEnabled)
{
WPP_TRACE( WPP_GET_LOGGER,
WPP_TRACE_OPTIONS,
(LPGUID)traceGuid,
id,
`i.LogArgs` (void*)0);
}
if (recorderEnabled)
{
WPP_RECORDER( AutoLogContext, level, flags, (LPGUID) traceGuid, id, `i.LogArgs` (void*)0 );
}
}
#endif // WPP_RECORDER_AND_TRACE_SF_`i.Name`_def
#else // ENABLE_WPP_TRACE_FILTERING_WITH_WPP_RECORDER
//
// Generate the WPP_RECORDER_SP_`i.Name` function
//
#ifndef WPP_RECORDER_SF_`i.Name`_def
#define WPP_RECORDER_SF_`i.Name`_def
WPP_INLINE
VOID
WPP_RECORDER_SF_`i.Name`(
PVOID AutoLogContext,
UCHAR level,
ULONG flags,
USHORT id,
LPCGUID traceGuid
`i.Arguments`
)
{
if (WPP_ENABLE_FLAG_BIT(flags) &&
(WPP_CONTROL(flags).Level >= level))
{
WPP_TRACE(
WPP_CONTROL(flags).Logger,
WPP_TRACE_OPTIONS,
(LPGUID)traceGuid,
id,
`i.LogArgs` (void*)0);
}
WPP_RECORDER(AutoLogContext, level, flags, (LPGUID) traceGuid, id, `i.LogArgs` (void*)0);
}
#endif // WPP_RECORDER_SF_`i.Name`_def
#endif // ENABLE_WPP_TRACE_FILTERING_WITH_WPP_RECORDER
#endif // ENABLE_WPP_RECORDER
`ENDFOR`
`FORALL i IN TypeSigSet WHERE UnsafeArgs`
#ifndef WPP_SF_`i.Name`_def
#define WPP_SF_`i.Name`_def
WPP_INLINE void WPP_SF_`i.Name`(WPP_LOGGER_ARG unsigned short id, LPCGUID TraceGuid, ...)
{
va_list ap;
va_start(ap, TraceGuid);
UNREFERENCED_PARAMETER(ap);
{
`i.DeclVars`
WPP_TRACE(
WPP_GET_LOGGER,
WPP_TRACE_OPTIONS,
(LPGUID)TraceGuid,
id,
`i.LogArgs` (void*)0);
}
}
#endif // WPP_SF_`i.Name`_def
`ENDFOR`
// WPP_LOG_ALWAYS:
// Called for each event: WPP_LOG_ALWAYS(EX, MSG, arg1, arg2, arg3...) Other()
// If defined, the definition needs to include a trailing comma or semicolon.
// In addition, you will need to define a WPP_EX_[args](args...) macro to
// extract any needed information from the other arguments (e.g. LEVEL).
#ifndef WPP_LOG_ALWAYS
#define WPP_LOG_ALWAYS(...)
#endif
// WPP_DEBUG:
// Called for each enabled event: WPP_DEBUG((MSG, arg1, arg2, arg3...)), Other()
// Potential definition: printf MsgArgs
// Definition should not include any trailing comma or semicolon.
#ifdef WPP_DEBUG
#define WPP_INVOKE_WPP_DEBUG(MsgArgs) WPP_DEBUG(MsgArgs)
#else // WPP_DEBUG
#define WPP_INVOKE_WPP_DEBUG(MsgArgs) (void)0
#endif // WPP_DEBUG
`FORALL i IN Messages WHERE !MsgArgs`
// WPP_CALL_`i.Name`
#ifndef WPP`i.GooId`_PRE
# define WPP`i.GooId`_PRE(`i.GooArgs`)
#endif
#ifndef WPP`i.GooId`_POST
# define WPP`i.GooId`_POST(`i.GooArgs`)
#endif
#if ENABLE_WPP_RECORDER
#if ENABLE_WPP_TRACE_FILTERING_WITH_WPP_RECORDER
#define WPP_CALL_`i.Name`(`i.FixedArgs``i.MacroArgs`) \
WPP_LOG_ALWAYS(WPP_EX`i.GooId`(`i.GooVals`), `i.DbgMacroArgs`) \
WPP`i.GooId`_PRE(`i.GooVals`) \
do {\
WPP_ANNOTATE(`i.Name`) 0; \
BOOLEAN wppEnabled = WPP_CHECK_INIT `i.MsgVal`WPP`i.GooId`_ENABLED(`i.GooVals`); \
BOOLEAN recorderEnabled = WPP_RECORDER_CHECK_INIT `i.MsgVal`WPP_RECORDER`i.GooId`_FILTER(`i.GooVals`); \
if (wppEnabled || recorderEnabled) { \
WPP_INVOKE_WPP_DEBUG((`i.DbgMacroArgs`)); \
WPP_RECORDER_AND_TRACE_SF_`i.TypeSig`( \
WPP`i.GooId`_LOGGER(`i.GooVals`) \
wppEnabled, recorderEnabled, \
WPP_RECORDER`i.GooId`_ARGS(`i.GooVals`), \
`i.MsgNo`, \
WPP_LOCAL_TraceGuids+0`i.MacroExprs`);\
} \
} \
while(0) \
WPP`i.GooId`_POST(`i.GooVals`)
#else // ENABLE_WPP_TRACE_FILTERING_WITH_WPP_RECORDER
#define WPP_CALL_`i.Name`(`i.FixedArgs``i.MacroArgs`) \
WPP_LOG_ALWAYS(WPP_EX`i.GooId`(`i.GooVals`), `i.DbgMacroArgs`) \
WPP`i.GooId`_PRE(`i.GooVals`) \
WPP_ANNOTATE(`i.Name`) \
(( \
WPP_RECORDER_CHECK_INIT `i.MsgVal`WPP_RECORDER`i.GooId`_FILTER(`i.GooVals`) \
? WPP_INVOKE_WPP_DEBUG((`i.DbgMacroArgs`)), \
WPP_RECORDER_SF_`i.TypeSig`( \
WPP_RECORDER`i.GooId`_ARGS(`i.GooVals`), \
`i.MsgNo`, \
WPP_LOCAL_TraceGuids+0`i.MacroExprs`), \
1 \
: 0 \
)) \
WPP`i.GooId`_POST(`i.GooVals`)
#endif // ENABLE_WPP_TRACE_FILTERING_WITH_WPP_RECORDER
#else // ENABLE_WPP_RECORDER
#define WPP_CALL_`i.Name`(`i.FixedArgs``i.MacroArgs`) \
WPP_LOG_ALWAYS(WPP_EX`i.GooId`(`i.GooVals`), `i.DbgMacroArgs`) \
WPP`i.GooId`_PRE(`i.GooVals`) \
WPP_ANNOTATE(`i.Name`) \
(( \
WPP_CHECK_INIT `i.MsgVal`WPP`i.GooId`_ENABLED(`i.GooVals`) \
? WPP_INVOKE_WPP_DEBUG((`i.DbgMacroArgs`)), \
WPP_SF_`i.TypeSig`( \
WPP`i.GooId`_LOGGER(`i.GooVals`) \
`i.MsgNo`, \
WPP_LOCAL_TraceGuids+0`i.MacroExprs`), \
1 \
: 0 \
)) \
WPP`i.GooId`_POST(`i.GooVals`)
#endif // ENABLE_WPP_RECORDER
`ENDFOR`
`FORALL i IN Messages WHERE MsgArgs`
// WPP_CALL_`i.Name`
#ifndef WPP`i.GooId`_PRE
# define WPP`i.GooId`_PRE(`i.GooArgs`)
#endif
#ifndef WPP`i.GooId`_POST
# define WPP`i.GooId`_POST(`i.GooArgs`)
#endif
#if ENABLE_WPP_RECORDER
#define WPP_CALL_`i.Name`(`i.FixedArgs` MSGARGS) \
WPP_LOG_ALWAYS(WPP_EX`i.GooId`(`i.GooVals`), WPP_FLATTEN MSGARGS) \
WPP`i.GooId`_PRE(`i.GooVals`) \
WPP_ANNOTATE(`i.Name`) \
(( \
WPP_RECORDER_CHECK_INIT `i.MsgVal`WPP_RECORDER`i.GooId`_FILTER(`i.GooVals`) \
? WPP_INVOKE_WPP_DEBUG(MSGARGS), \
WPP_RECORDER_SF_`i.TypeSig`( \
WPP_RECORDER`i.GooId`_ARGS(`i.GooVals`), \
`i.MsgNo`, \
WPP_LOCAL_TraceGuids+0 `i.SyntheticExprs`WPP_R`i.ReorderSig` MSGARGS), \
1 \
: 0 \
)) \
WPP`i.GooId`_POST(`i.GooVals`)
#else // ENABLE_WPP_RECORDER
#define WPP_CALL_`i.Name`(`i.FixedArgs` MSGARGS) \
WPP_LOG_ALWAYS(WPP_EX`i.GooId`(`i.GooVals`), WPP_FLATTEN MSGARGS) \
WPP`i.GooId`_PRE(`i.GooVals`) \
WPP_ANNOTATE(`i.Name`) \
(( \
WPP_CHECK_INIT WPP`i.GooId`_ENABLED(`i.GooVals`) \
? WPP_INVOKE_WPP_DEBUG(MSGARGS), \
WPP_SF_`i.TypeSig`( \
WPP`i.GooId`_LOGGER(`i.GooVals`) \
`i.MsgNo`, \
WPP_LOCAL_TraceGuids+0 `i.SyntheticExprs`WPP_R`i.ReorderSig` MSGARGS),\
1 \
: 0 \
)) \
WPP`i.GooId`_POST(`i.GooVals`)
#endif // ENABLE_WPP_RECORDER
`ENDFOR`
// Functions
`FORALL f IN Funcs WHERE !DoubleP && !MsgArgs && !NoMsg`
#undef `f.Name`
#ifdef __INTELLISENSE__
#define `f.Name`(`f.FixedArgs`MSG, ...) ((void)(MSG, ## __VA_ARGS__))
#else
#define `f.Name` WPP_(CALL)
#endif
`ENDFOR`
`FORALL f IN Funcs WHERE !DoubleP && !MsgArgs && NoMsg`
#undef `f.Name`
#ifdef __INTELLISENSE__
#define `f.Name`(`f.FixedArgs`) ((void)0)
#else
#define `f.Name` WPP_(CALL)
#endif
`ENDFOR`
`FORALL f IN Funcs WHERE DoubleP && !MsgArgs`
#undef `f.Name`
#ifdef __INTELLISENSE__
#define `f.Name`(ARGS) ((void)ARGS)
#else
#define `f.Name`(ARGS) WPP_(CALL) ARGS
#endif
`ENDFOR`
`FORALL f IN Funcs WHERE MsgArgs`
#undef `f.Name`
#ifdef __INTELLISENSE__
#define `f.Name`(`f.FixedArgs`MSGARGS) ((void)(MSGARGS))
#else
#define `f.Name`(`f.FixedArgs`MSGARGS) WPP_(CALL)(`f.FixedArgs`MSGARGS)
#endif
`ENDFOR`
`FORALL r IN Reorder`
#undef WPP_R`r.Name`
#define WPP_R`r.Name`(`r.Arguments`) `r.Permutation`
`ENDFOR`
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -0,0 +1,20 @@
`**********************************************************************`
`* This is a template file for the tracewpp preprocessor. *`
`* If you need to use a custom version of this file in your project, *`
`* please clone it from this one and point WPP to it by specifying *`
`* -gen:{yourfile}*.tmh on the RUN_WPP line in your sources file. *`
`* *`
`* Copyright (c) Microsoft Corporation. All rights reserved. *`
`**********************************************************************`
// `Compiler.Checksum` Generated file. Do not edit.
// File created by `Compiler.Name` compiler version `Compiler.Version`
// from template `TemplateFile`
#pragma once
`INCLUDE um-header.tpl`
`INCLUDE control.tpl`
`INCLUDE tracemacro.tpl`
`IF FOUND WPP_INIT_TRACING`
` INCLUDE um-init.tpl`
`ENDIF`

Some files were not shown because too many files have changed in this diff Show more