Unverified Commit 3a6622f7 authored by Yuting Jiang's avatar Yuting Jiang Committed by GitHub
Browse files

Benchmarks: Add benchmark - Add source code of DirectXGPUCoreFLOPs microbenchmark (#488)



**Description**
Add source code of DirectXGPUCoreFLOPs microbenchmark.

---------
Co-authored-by: default avatarv-junlinlv <v-junlinlv@microsoft.com>
parent 44ef5314
......@@ -141,3 +141,76 @@ dmypy.json
# Cython debug symbols
cython_debug/
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio code coverage results
*.coverage
*.coveragexml
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include "../directx_utils/Options.h"
namespace Option {
enum Precision {
F16,
F32,
};
using PrecisionType = Option::Precision;
} // namespace Option
class BenchmarkOptions : public Options {
public:
// Number of warm up rounds to run.
int num_warm_up = 0;
// The number of benchmark runs.
int num_loops = 0;
// Dimension m of GEMM.
int m = 0;
// Dimension n of GEMM.
int n = 0;
// Dimension k of GEMM.
int k = 0;
// The precision of calculate.
Option::PrecisionType mode_precision = Option::F32;
/**
* @brief Construct a new GPUCoreOptions object.
*/
BenchmarkOptions(int argc, char *argv[]) : Options(argc, argv) {}
/**
* @brief Parse the arguments.
*/
virtual void parse_arguments() {
num_loops = get_cmd_line_argument_int("--num_loops", 10);
num_warm_up = get_cmd_line_argument_int("--num_loops", 0);
m = get_cmd_line_argument_int("--m", 16 * 256);
n = get_cmd_line_argument_int("--n", 16 * 256);
k = get_cmd_line_argument_int("--k", 16 * 256);
if (get_cmd_line_argument_bool("--f16")) {
mode_precision = Option::F16;
}
if (get_cmd_line_argument_bool("--f32")) {
mode_precision = Option::F32;
}
}
/**
* @brief Get the option usage.
*/
void get_option_usage() override {
std::cout << "Usage: " << std::endl;
std::cout << " --help: Print help message." << std::endl;
std::cout << " --num_loops: The number of benchmark runs." << std::endl;
std::cout << " --num_warm_up: The number of warmup runs." << std::endl;
std::cout << " --m: m dimension of GEMM." << std::endl;
std::cout << " --n: n dimension of GEMM." << std::endl;
std::cout << " --k: l dimension of GEMM." << std::endl;
std::cout << " --fp16: half precision to compute." << std::endl;
std::cout << " --fp32: float precision to compute." << std::endl;
}
};
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers.
#endif
#include <DirectXPackedVector.h>
#include <chrono>
#include <d3d12.h>
#include <d3d12shader.h>
#include <d3dcompiler.h>
#include <directml.h>
#include <dxgi1_6.h>
#include <string>
#include <unordered_map>
#include <windowsx.h>
#include <wrl.h>
// linker
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "D3D12.lib")
#pragma comment(lib, "d3dcompiler.lib")
#if defined(_DEBUG)
#include <dxgidebug.h>
#endif
#include "../directx_third_party/DXSampleHelper.h"
#include "../directx_third_party/d3dx12.h"
#include "../directx_utils/D3D12Timer.h"
#include "BenchmarkOptions.h"
using namespace std;
using namespace DirectX;
// Note that while ComPtr is used to manage the lifetime of resources on the CPU,
// it has no understanding of the lifetime of resources on the GPU. Apps must account
// for the GPU lifetime of resources to avoid destroying objects that may still be
// referenced by the GPU.
// An example of this can be found in the class method: OnDestroy().
using Microsoft::WRL::ComPtr;
template <typename T> T *get_rvalue_ptr(T &&v) { return &v; }
class GPUCore {
public:
GPUCore(BenchmarkOptions *opts) : opts(opts) {}
~GPUCore() {}
/**
* @brief Setup GPU and start benchmark.
*/
void Run();
/**
* @brief Create pipeline including
* create device object, command list, command queue
* and synchronization objects.
*/
void CreatePipeline();
/**
* @brief Prepare input and output data and buffers of the tensor elements..
*/
template <typename T> void PrepareData(const int m, const int n, const int k);
/**
* @brief Create and initialize DML_TENSOR_DESC.
*/
std::unique_ptr<DML_TENSOR_DESC> CreateTensorDesc(DML_TENSOR_DATA_TYPE dataType, UINT *tensorSizes,
int dimensionCount);
/**
* @brief Setup and compile DirectML operator.
*/
void SetupAndCompileOp(int m, int n, int k, DML_TENSOR_DATA_TYPE dataType);
/**
* @brief Initialize DirectML operator.
*/
template <typename T> void InitializeOp(int m, int n, int k);
/**
* @brief Execute the computation GEMM op.
* @return the elapsed time in ms.
*/
double ExecuteComputeOp();
/**
* @brief Close and execute command list, wait until command completed.
*/
void CloseExecuteResetWait(DWORD dwMilliseconds = 300000);
#if defined _PRINT_RESULT
/**
* @brief Print the result of the benchmark for debug.
*/
template <typename T> void PrintResultForDebug(int m, int n);
#endif
/**
* @brief Create a default buffer and upload data with the upload buffer.
* @param device the GPU device object.
* @param cmdList the GPU command list object.
* @param initData the data that need to upload.
* @param byteSize the size of data that need to upload.
* @param UploadBuffer the upload that use for upload data.
* @return a default buffer object.
*/
Microsoft::WRL::ComPtr<ID3D12Resource> CreateDefaultBuffer(ID3D12Device *device, ID3D12GraphicsCommandList *cmdList,
const void *initData, UINT64 byteSize,
Microsoft::WRL::ComPtr<ID3D12Resource> &UploadBuffer);
private:
// Pipeline objects.
ComPtr<ID3D12Device> m_device = nullptr;
ComPtr<ID3D12CommandAllocator> m_commandAllocator = nullptr;
ComPtr<ID3D12CommandQueue> m_commandQueue = nullptr;
ComPtr<ID3D12GraphicsCommandList> m_commandList = nullptr;
ComPtr<IDMLDevice> m_dmlDevice = nullptr;
ComPtr<IDMLCommandRecorder> m_dmlCommandRecorder = nullptr;
ComPtr<IDMLCompiledOperator> m_dmlCompiledOperator = nullptr;
ComPtr<IDMLBindingTable> m_bindingTable = nullptr;
ComPtr<ID3D12DescriptorHeap> m_descriptorHeap = nullptr;
// Input buffer to pass data into GPU.
ComPtr<ID3D12Resource> m_inputBufferA = nullptr;
ComPtr<ID3D12Resource> m_inputUploadBufferA = nullptr;
ComPtr<ID3D12Resource> m_inputBufferB = nullptr;
ComPtr<ID3D12Resource> m_inputUploadBufferB = nullptr;
// Output buffer that result output on GPU.
ComPtr<ID3D12Resource> m_outputBuffer = nullptr;
// Readback buffer to copy data from GPU side to CPU side.
ComPtr<ID3D12Resource> m_readBackBuffer = nullptr;
// Synchronization objects.
ComPtr<ID3D12Fence> m_fence = nullptr;
UINT64 m_currentFence = 0;
HANDLE m_eventHandle = nullptr;
// GPU timer.
D3D12::D3D12Timer gpuTimer;
// Options.
BenchmarkOptions *opts;
};
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="packages\Microsoft.AI.DirectML.1.11.0\build\Microsoft.AI.DirectML.props" Condition="Exists('packages\Microsoft.AI.DirectML.1.11.0\build\Microsoft.AI.DirectML.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{8407ef34-a93c-473a-8fac-2598b2695b61}</ProjectGuid>
<RootNamespace>GPUCore</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<RunCodeAnalysis>false</RunCodeAnalysis>
<EnableClangTidyCodeAnalysis>true</EnableClangTidyCodeAnalysis>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_PRINT_RESULT</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions);</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\directx_utils\D3D12Timer.cpp" />
<ClCompile Include="GPUCore.cpp" />
<ClCompile Include="Main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\directx_third_party\d3dx12.h" />
<ClInclude Include="..\directx_third_party\DXSampleHelper.h" />
<ClInclude Include="..\directx_utils\D3D12Timer.h" />
<ClInclude Include=".\directx_utils\Options.h" />
<ClInclude Include="GPUCore.h" />
<ClInclude Include="BenchmarkOptions.h" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<PropertyGroup>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="packages\Microsoft.AI.DirectML.1.11.0\build\Microsoft.AI.DirectML.targets" Condition="Exists('packages\Microsoft.AI.DirectML.1.11.0\build\Microsoft.AI.DirectML.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\Microsoft.AI.DirectML.1.11.0\build\Microsoft.AI.DirectML.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.AI.DirectML.1.11.0\build\Microsoft.AI.DirectML.props'))" />
<Error Condition="!Exists('packages\Microsoft.AI.DirectML.1.11.0\build\Microsoft.AI.DirectML.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.AI.DirectML.1.11.0\build\Microsoft.AI.DirectML.targets'))" />
</Target>
</Project>
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "GPUCore.h"
int main(int argc, char *argv[]) {
std::unique_ptr<BenchmarkOptions> opts = std::make_unique<BenchmarkOptions>(argc, argv);
opts->init();
std::unique_ptr<GPUCore> gpucopy = std::make_unique<GPUCore>(opts.get());
gpucopy->Run();
}
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.AI.DirectML" version="1.11.0" targetFramework="native" />
</packages>
\ No newline at end of file
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
#include <d3d12.h>
#include <dxgi1_6.h>
#include <stdexcept>
#include <wrl.h>
// Note that while ComPtr is used to manage the lifetime of resources on the CPU,
// it has no understanding of the lifetime of resources on the GPU. Apps must account
// for the GPU lifetime of resources to avoid destroying objects that may still be
// referenced by the GPU.
using Microsoft::WRL::ComPtr;
inline std::string HrToString(HRESULT hr) {
char s_str[64] = {};
sprintf_s(s_str, "HRESULT of 0x%08X", static_cast<UINT>(hr));
return std::string(s_str);
}
class HrException : public std::runtime_error {
public:
HrException(HRESULT hr) : std::runtime_error(HrToString(hr)), m_hr(hr) {}
HRESULT Error() const { return m_hr; }
private:
const HRESULT m_hr;
};
#define SAFE_RELEASE(p) \
if (p) \
(p)->Release()
inline void ThrowIfFailed(HRESULT hr) {
if (FAILED(hr)) {
throw HrException(hr);
}
}
inline void GetAssetsPath(_Out_writes_(pathSize) WCHAR *path, UINT pathSize) {
if (path == nullptr) {
throw std::exception();
}
DWORD size = GetModuleFileName(nullptr, path, pathSize);
if (size == 0 || size == pathSize) {
// Method failed or path was truncated.
throw std::exception();
}
WCHAR *lastSlash = wcsrchr(path, L'\\');
if (lastSlash) {
*(lastSlash + 1) = L'\0';
}
}
inline HRESULT ReadDataFromFile(LPCWSTR filename, byte **data, UINT *size) {
using namespace Microsoft::WRL;
CREATEFILE2_EXTENDED_PARAMETERS extendedParams = {};
extendedParams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
extendedParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
extendedParams.dwFileFlags = FILE_FLAG_SEQUENTIAL_SCAN;
extendedParams.dwSecurityQosFlags = SECURITY_ANONYMOUS;
extendedParams.lpSecurityAttributes = nullptr;
extendedParams.hTemplateFile = nullptr;
Wrappers::FileHandle file(CreateFile2(filename, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, &extendedParams));
if (file.Get() == INVALID_HANDLE_VALUE) {
throw std::exception();
}
FILE_STANDARD_INFO fileInfo = {};
if (!GetFileInformationByHandleEx(file.Get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) {
throw std::exception();
}
if (fileInfo.EndOfFile.HighPart != 0) {
throw std::exception();
}
*data = reinterpret_cast<byte *>(malloc(fileInfo.EndOfFile.LowPart));
*size = fileInfo.EndOfFile.LowPart;
if (!ReadFile(file.Get(), *data, fileInfo.EndOfFile.LowPart, nullptr, nullptr)) {
throw std::exception();
}
return S_OK;
}
inline HRESULT ReadDataFromDDSFile(LPCWSTR filename, byte **data, UINT *offset, UINT *size) {
if (FAILED(ReadDataFromFile(filename, data, size))) {
return E_FAIL;
}
// DDS files always start with the same magic number.
static const UINT DDS_MAGIC = 0x20534444;
UINT magicNumber = *reinterpret_cast<const UINT *>(*data);
if (magicNumber != DDS_MAGIC) {
return E_FAIL;
}
struct DDS_PIXELFORMAT {
UINT size;
UINT flags;
UINT fourCC;
UINT rgbBitCount;
UINT rBitMask;
UINT gBitMask;
UINT bBitMask;
UINT aBitMask;
};
struct DDS_HEADER {
UINT size;
UINT flags;
UINT height;
UINT width;
UINT pitchOrLinearSize;
UINT depth;
UINT mipMapCount;
UINT reserved1[11];
DDS_PIXELFORMAT ddsPixelFormat;
UINT caps;
UINT caps2;
UINT caps3;
UINT caps4;
UINT reserved2;
};
auto ddsHeader = reinterpret_cast<const DDS_HEADER *>(*data + sizeof(UINT));
if (ddsHeader->size != sizeof(DDS_HEADER) || ddsHeader->ddsPixelFormat.size != sizeof(DDS_PIXELFORMAT)) {
return E_FAIL;
}
const ptrdiff_t ddsDataOffset = sizeof(UINT) + sizeof(DDS_HEADER);
*offset = ddsDataOffset;
*size = *size - ddsDataOffset;
return S_OK;
}
// Assign a name to the object to aid with debugging.
#if defined(_DEBUG) || defined(DBG)
inline void SetName(ID3D12Object *pObject, LPCWSTR name) { pObject->SetName(name); }
inline void SetNameIndexed(ID3D12Object *pObject, LPCWSTR name, UINT index) {
WCHAR fullName[50];
if (swprintf_s(fullName, L"%s[%u]", name, index) > 0) {
pObject->SetName(fullName);
}
}
#else
inline void SetName(ID3D12Object *, LPCWSTR) {}
inline void SetNameIndexed(ID3D12Object *, LPCWSTR, UINT) {}
#endif
// Naming helper for ComPtr<T>.
// Assigns the name of the variable as the name of the object.
// The indexed variant will include the index in the name of the object.
#define NAME_D3D12_OBJECT(x) SetName((x).Get(), L#x)
#define NAME_D3D12_OBJECT_INDEXED(x, n) SetNameIndexed((x)[n].Get(), L#x, n)
inline UINT CalculateConstantBufferByteSize(UINT byteSize) {
// Constant buffer size is required to be aligned.
return (byteSize + (D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1)) &
~(D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 1);
}
#ifdef D3D_COMPILE_STANDARD_FILE_INCLUDE
inline Microsoft::WRL::ComPtr<ID3DBlob> CompileShader(const std::wstring &filename, const D3D_SHADER_MACRO *defines,
const std::string &entrypoint, const std::string &target) {
UINT compileFlags = 0;
#if defined(_DEBUG) || defined(DBG)
compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#endif
HRESULT hr;
Microsoft::WRL::ComPtr<ID3DBlob> byteCode = nullptr;
Microsoft::WRL::ComPtr<ID3DBlob> errors;
hr = D3DCompileFromFile(filename.c_str(), defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, entrypoint.c_str(),
target.c_str(), compileFlags, 0, &byteCode, &errors);
if (errors != nullptr) {
OutputDebugStringA((char *)errors->GetBufferPointer());
}
ThrowIfFailed(hr);
return byteCode;
}
#endif
// Resets all elements in a ComPtr array.
template <class T> void ResetComPtrArray(T *comPtrArray) {
for (auto &i : *comPtrArray) {
i.Reset();
}
}
// Resets all elements in a unique_ptr array.
template <class T> void ResetUniquePtrArray(T *uniquePtrArray) {
for (auto &i : *uniquePtrArray) {
i.reset();
}
}
/**
* @brief Helper function for acquiring the first available hardware adapter that supports Direct3D 12.
* If no such adapter can be found, *ppAdapter will be set to nullptr.
* @param pFactory a pointer to factory object.
* @param[out] ppAdapter a pointer of pointer to a adapter.
* @param requestHighPerformanceAdapter the option of adapter.
*/
inline void GetHardwareAdapter(IDXGIFactory1 *pFactory, IDXGIAdapter1 **ppAdapter,
bool requestHighPerformanceAdapter = FALSE) {
*ppAdapter = nullptr;
ComPtr<IDXGIAdapter1> adapter;
ComPtr<IDXGIFactory6> factory6;
if (SUCCEEDED(pFactory->QueryInterface(IID_PPV_ARGS(&factory6)))) {
for (UINT adapterIndex = 0; SUCCEEDED(factory6->EnumAdapterByGpuPreference(
adapterIndex,
requestHighPerformanceAdapter == true ? DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE
: DXGI_GPU_PREFERENCE_UNSPECIFIED,
IID_PPV_ARGS(&adapter)));
++adapterIndex) {
DXGI_ADAPTER_DESC1 desc;
adapter->GetDesc1(&desc);
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
// Don't select the Basic Render Driver adapter.
// If you want a software adapter, pass in "/warp" on the command line.
continue;
}
// Check to see whether the adapter supports Direct3D 12, but don't create the
// actual device yet.
if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) {
break;
}
}
}
if (adapter.Get() == nullptr) {
for (UINT adapterIndex = 0; SUCCEEDED(pFactory->EnumAdapters1(adapterIndex, &adapter)); ++adapterIndex) {
DXGI_ADAPTER_DESC1 desc;
adapter->GetDesc1(&desc);
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
// Don't select the Basic Render Driver adapter.
// If you want a software adapter, pass in "/warp" on the command line.
continue;
}
// Check to see whether the adapter supports Direct3D 12, but don't create the
// actual device yet.
if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) {
break;
}
}
}
*ppAdapter = adapter.Detach();
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "D3D12Timer.h"
#include "../directx_third_party/DXSampleHelper.h"
#include "../directx_third_party/d3dx12.h"
#include <cassert>
namespace D3D12 {
D3D12Timer::D3D12Timer() {}
// Destructor.
D3D12Timer::~D3D12Timer() {
if (m_queryHeap)
m_queryHeap->Release();
if (m_queryResourceCPU)
m_queryResourceCPU->Release();
}
void D3D12Timer::init(ID3D12Device *pDevice, ID3D12CommandQueue *pCommandQueue, UINT numTimers, QueueType type) {
assert(pDevice != nullptr);
m_device = pDevice;
m_timerCount = numTimers;
UINT64 gpuFreq;
ThrowIfFailed(pCommandQueue->GetTimestampFrequency(&gpuFreq));
m_gpuFreqInv = 1000.0 / double(gpuFreq);
D3D12_QUERY_HEAP_DESC queryHeapDesc;
queryHeapDesc.Count = m_timerCount * 2;
queryHeapDesc.NodeMask = 0;
if (type == QueueType::compute) {
queryHeapDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP;
} else if (type == QueueType::copy) {
queryHeapDesc.Type = D3D12_QUERY_HEAP_TYPE_COPY_QUEUE_TIMESTAMP;
}
ThrowIfFailed(m_device->CreateQueryHeap(&queryHeapDesc, IID_PPV_ARGS(&m_queryHeap)));
D3D12_HEAP_PROPERTIES heapProp = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_READBACK);
D3D12_RESOURCE_DESC resouceDesc = CD3DX12_RESOURCE_DESC::Buffer(m_timerCount * sizeof(GPUTimestampPair));
ThrowIfFailed(m_device->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &resouceDesc,
D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
IID_PPV_ARGS(&m_queryResourceCPU)));
}
// Start timestamp.
bool D3D12Timer::start(ID3D12GraphicsCommandList *pCommandList, UINT timestampPairIndex) {
if (timestampPairIndex >= m_timerCount)
return false;
pCommandList->EndQuery(m_queryHeap, D3D12_QUERY_TYPE_TIMESTAMP, getStartIndex(timestampPairIndex));
return true;
}
// Stop timestamp.
bool D3D12Timer::stop(ID3D12GraphicsCommandList *pCommandList, UINT timestampPairIndex) {
if (timestampPairIndex >= m_timerCount)
return false;
pCommandList->EndQuery(m_queryHeap, D3D12_QUERY_TYPE_TIMESTAMP, getEndIndex(timestampPairIndex));
return true;
}
// Resolve query data. Write query to device memory. Make sure to wait for query to finish before resolving data.
void D3D12Timer::resolveQueryToCPU(ID3D12GraphicsCommandList *pCommandList, UINT timestampPairIndex) {
pCommandList->ResolveQueryData(m_queryHeap, D3D12_QUERY_TYPE_TIMESTAMP, getStartIndex(timestampPairIndex), 2,
m_queryResourceCPU, sizeof(GPUTimestampPair) * timestampPairIndex);
}
// Get start and end timestamp pair.
double D3D12Timer::getElapsedMsByTimestampPair(UINT timestampPairIndex) {
GPUTimestampPair *timingData = nullptr;
D3D12_RANGE readRange{sizeof(GPUTimestampPair) * timestampPairIndex,
sizeof(GPUTimestampPair) * (timestampPairIndex + 1)};
D3D12_RANGE writeRange{0, 0};
if (SUCCEEDED(m_queryResourceCPU->Map(0, &readRange, (void **)&timingData))) {
m_queryResourceCPU->Unmap(0, &writeRange);
return (timingData->Stop - timingData->Start) * m_gpuFreqInv;
}
return -1;
}
} // namespace D3D12
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include <d3d12.h>
namespace D3D12 {
struct GPUTimestampPair {
UINT64 Start;
UINT64 Stop;
};
enum QueueType { compute = 0, copy = 1 };
// D3D12 timer.
class D3D12Timer {
public:
// Constructor.
D3D12Timer();
// Destructor.
~D3D12Timer();
void init(ID3D12Device *pDevice, ID3D12CommandQueue *pCommandQueue, UINT numTimers, QueueType type);
// Start timestamp.
bool start(ID3D12GraphicsCommandList *pCommandList, UINT timestampPairIndex);
// Stop timestamp.
bool stop(ID3D12GraphicsCommandList *pCommandList, UINT timestampPairIndex);
// Resolve query data. Write query to device memory. Make sure to wait for query to finsih before resolving data.
void resolveQueryToCPU(ID3D12GraphicsCommandList *pCommandList, UINT timestampPairIndex);
// Get start and end timestamp pair.
double getElapsedMsByTimestampPair(UINT timestampPairIndex);
// Get the GPU frequency.
double getGPUFrequecy() { return m_gpuFreqInv; }
// Get start index of the selected timestamp pair
UINT getStartIndex(UINT timestampPairIndex) { return timestampPairIndex * 2; }
// Get end index of the selected timestamp pair
UINT getEndIndex(UINT timestampPairIndex) { return timestampPairIndex * 2 + 1; }
private:
ID3D12Device *m_device = nullptr;
ID3D12QueryHeap *m_queryHeap = nullptr;
ID3D12Resource *m_queryResourceCPU = nullptr;
UINT m_timerCount = 0;
double m_gpuFreqInv;
};
} // namespace D3D12
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#pragma once
#include <iostream>
#include <sstream>
#include <string>
class Options {
protected:
char **begin;
char **end;
/**
* @brief Get the char* value of the cmd line argument.
* @param option the argument in cmd.
* @return char*
*/
char *get_cmd_option(const std::string &option) {
char **itr = std::find(begin, end, option);
if (itr != end && ++itr != end) {
return *itr;
}
return 0;
}
/**
* @brief Get the int type value of cmd line argument.
* @param option the cmd line argument.
* @param defaults the default value.
* @return int the int type value of cmd line argument 'option'.
*/
int get_cmd_line_argument_int(const std::string &option, int defaults) {
if (char *value = get_cmd_option(option)) {
try {
return std::stoi(value);
} catch (const std::exception &e) {
std::cerr << "Error: Invalid argument - " << option << " should be INT " << e.what() << '\n';
exit(1);
}
}
return defaults;
}
/**
* @brief Get the string type value of cmd line argument.
* @param option the cmd line argument.
* @return std::string the int type value of cmd line argument 'option'.
*/
std::string get_cmd_line_argument_string(const std::string &option) {
if (char *value = get_cmd_option(option)) {
return std::string(value);
}
return "";
}
/**
* @brief Get the boolean type value of cmd line argument.
* @param option the cmd line argument.
* @return bool the boolean value.
*/
bool get_cmd_line_argument_bool(const std::string &option) {
if (cmd_option_exists(option)) {
return true;
}
return false;
}
/**
* @brief Check if a argument exists.
* @param option the cmd line argument.
* @return bool if a argument exists.
*/
bool cmd_option_exists(const std::string &option) { return std::find(begin, end, option) != end; }
/**
* @brief Get the option usage.
*/
virtual void get_option_usage(){};
/**
* @brief Parse the arguments.
*/
virtual void parse_arguments(){};
public:
/**
* @brief Construct a new Command Line object.
* @param argc the number of command line arguments.
* @param argv the string array of comamnd line arguments.
*/
Options(int argc, char *argv[]) {
begin = argv;
end = argv + argc;
}
/**
* @brief Init and parse the arguments.
*/
virtual void init() {
if (cmd_option_exists("--help")) {
get_option_usage();
exit(0);
}
try {
parse_arguments();
} catch (const std::exception &e) {
std::cerr << "Error: Invalid argument - " << e.what() << '\n';
exit(1);
}
};
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment