17 using DeviceResult = std::expected<ComPtr<ID3D12Device5>, std::string>;
34 D3D12_FEATURE_DATA_SHADER_MODEL shaderModel{D3D_SHADER_MODEL_6_8};
35 if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shaderModel,
sizeof(shaderModel))) ||
36 shaderModel.HighestShaderModel < D3D_SHADER_MODEL_6_8) {
41 D3D12_FEATURE_DATA_D3D12_OPTIONS21 options21{};
42 if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS21, &options21,
sizeof(options21))) ||
43 options21.WorkGraphsTier == D3D12_WORK_GRAPHS_TIER_NOT_SUPPORTED) {
44 missing +=
" WorkGraphs";
47 D3D12_FEATURE_DATA_D3D12_OPTIONS1 options1{};
48 if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS1, &options1,
sizeof(options1))) ||
49 !options1.Int64ShaderOps) {
50 missing +=
" Int64ShaderOps";
58 DXGI_ADAPTER_DESC1 desc{};
59 if (FAILED(adapter->GetDesc1(&desc)) || (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0)
62 ComPtr<ID3D12Device5> device;
67 if (!missing.empty()) {
74 static_cast<unsigned long>(desc.AdapterLuid.HighPart), desc.AdapterLuid.LowPart);
81 for (UINT outputIndex = 0;; ++outputIndex) {
82 ComPtr<IDXGIOutput> output;
83 if (adapter->EnumOutputs(outputIndex, &output) == DXGI_ERROR_NOT_FOUND)
86 DXGI_OUTPUT_DESC outputDesc{};
87 if (SUCCEEDED(output->GetDesc(&outputDesc)) && outputDesc.Monitor == monitor)
94 for (UINT index = 0;; ++index) {
95 ComPtr<IDXGIAdapter1> adapter;
96 if (factory->EnumAdapterByGpuPreference(index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
97 IID_PPV_ARGS(&adapter)) == DXGI_ERROR_NOT_FOUND) {
111 if (presentMonitor !=
nullptr) {
121 return std::unexpected(
"最低機の条件(FL 12_2・SM 6.8・Work Graphs・Int64ShaderOps)を満たすアダプタが無い");
131 HMODULE warpModule = GetModuleHandleW(L
"d3d10warp.dll");
132 if (warpModule ==
nullptr)
133 return {.description =
"d3d10warp.dll が読み込まれていない"};
135 std::wstring warpPath(MAX_PATH * 4, L
'\0');
136 warpPath.resize(GetModuleFileNameW(warpModule, warpPath.data(),
static_cast<DWORD
>(warpPath.size())));
137 std::wstring executablePath(MAX_PATH * 4, L
'\0');
138 executablePath.resize(
139 GetModuleFileNameW(
nullptr, executablePath.data(),
static_cast<DWORD
>(executablePath.size())));
142 .description =
ToUtf8(warpPath),
143 .besideExecutable = fs::path(warpPath).parent_path() == fs::path(executablePath).parent_path(),
147 const DWORD infoSize = GetFileVersionInfoSizeW(warpPath.c_str(),
nullptr);
148 std::vector<std::byte> info(infoSize);
149 void* fixedInfoPointer =
nullptr;
150 UINT fixedInfoSize = 0;
151 const bool hasVersion = infoSize != 0 && GetFileVersionInfoW(warpPath.c_str(), 0, infoSize, info.data()) &&
152 VerQueryValueW(info.data(), L
"\\", &fixedInfoPointer, &fixedInfoSize) &&
153 fixedInfoSize >=
sizeof(VS_FIXEDFILEINFO);
159 const auto* fixedInfo =
static_cast<const VS_FIXEDFILEINFO*
>(fixedInfoPointer);
160 result.
description += std::format(
"(版 {}.{}.{}.{})", HIWORD(fixedInfo->dwFileVersionMS),
161 LOWORD(fixedInfo->dwFileVersionMS), HIWORD(fixedInfo->dwFileVersionLS),
162 LOWORD(fixedInfo->dwFileVersionLS));
168 ComPtr<IDXGIAdapter1> adapter;
169 HRESULT result = factory->EnumWarpAdapter(IID_PPV_ARGS(&adapter));
173 ComPtr<ID3D12Device5> device;
179 if (!missing.empty())
180 return std::unexpected(
"WARP に足りない機能:" + missing);
190 "WARP が exe の横の版ではない(Work Graph が落ちることがある。ADR-0013): {}",
201 ComPtr<ID3D12Debug1> debug;
202 if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug)))) {
203 debug->EnableDebugLayer();
210 ComPtr<ID3D12DeviceRemovedExtendedDataSettings1> dred;
211 if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&dred)))) {
212 dred->SetAutoBreadcrumbsEnablement(D3D12_DRED_ENABLEMENT_FORCED_ON);
213 dred->SetPageFaultEnablement(D3D12_DRED_ENABLEMENT_FORCED_ON);
214 dred->SetBreadcrumbContextEnablement(D3D12_DRED_ENABLEMENT_FORCED_ON);
226 case D3D12_MESSAGE_SEVERITY_CORRUPTION:
238 case D3D12_AUTO_BREADCRUMB_OP_SETMARKER:
return "SetMarker";
239 case D3D12_AUTO_BREADCRUMB_OP_BEGINEVENT:
return "BeginEvent";
240 case D3D12_AUTO_BREADCRUMB_OP_ENDEVENT:
return "EndEvent";
241 case D3D12_AUTO_BREADCRUMB_OP_DRAWINSTANCED:
return "DrawInstanced";
242 case D3D12_AUTO_BREADCRUMB_OP_DRAWINDEXEDINSTANCED:
return "DrawIndexedInstanced";
243 case D3D12_AUTO_BREADCRUMB_OP_EXECUTEINDIRECT:
return "ExecuteIndirect";
244 case D3D12_AUTO_BREADCRUMB_OP_DISPATCH:
return "Dispatch";
245 case D3D12_AUTO_BREADCRUMB_OP_COPYBUFFERREGION:
return "CopyBufferRegion";
246 case D3D12_AUTO_BREADCRUMB_OP_COPYTEXTUREREGION:
return "CopyTextureRegion";
247 case D3D12_AUTO_BREADCRUMB_OP_COPYRESOURCE:
return "CopyResource";
248 case D3D12_AUTO_BREADCRUMB_OP_CLEARUNORDEREDACCESSVIEW:
return "ClearUnorderedAccessView";
249 case D3D12_AUTO_BREADCRUMB_OP_RESOURCEBARRIER:
return "ResourceBarrier";
250 case D3D12_AUTO_BREADCRUMB_OP_PRESENT:
return "Present";
251 case D3D12_AUTO_BREADCRUMB_OP_RESOLVEQUERYDATA:
return "ResolveQueryData";
252 case D3D12_AUTO_BREADCRUMB_OP_WRITEBUFFERIMMEDIATE:
return "WriteBufferImmediate";
253 case D3D12_AUTO_BREADCRUMB_OP_DISPATCHRAYS:
return "DispatchRays";
254 case D3D12_AUTO_BREADCRUMB_OP_DISPATCHMESH:
return "DispatchMesh";
255 case D3D12_AUTO_BREADCRUMB_OP_BARRIER:
return "Barrier";
256 case D3D12_AUTO_BREADCRUMB_OP_BEGIN_COMMAND_LIST:
return "BeginCommandList";
257 case D3D12_AUTO_BREADCRUMB_OP_DISPATCHGRAPH:
return "DispatchGraph";
258 case D3D12_AUTO_BREADCRUMB_OP_SETPROGRAM:
return "SetProgram";
259 default:
return std::format(
"op {}",
static_cast<int>(op));
264 return name !=
nullptr ?
ToUtf8(name) : std::string(
"(名前なし)");
269 const uint32_t completed = node.pLastBreadcrumbValue !=
nullptr ? *node.pLastBreadcrumbValue : 0;
270 const std::string where = std::format(
"リスト {}(キュー {})",
DebugNameOf(node.pCommandListDebugNameW),
273 if (completed >= node.BreadcrumbCount) {
275 node.BreadcrumbCount);
280 node.BreadcrumbCount, completed);
284 for (uint32_t index = first; index < last; ++index) {
290 void LogAllocations(std::string_view title,
const D3D12_DRED_ALLOCATION_NODE1* node) {
293 static_cast<int>(node->AllocationType));
304#ifdef BICAMERAL_GPU_VALIDATION
305 return {.debugLayer =
true, .gpuBasedValidation =
true, .dred =
true};
314 EnableDebugFeatures(options);
315 ComPtr<IDXGIFactory6> factory;
316 const HRESULT result = CreateDXGIFactory2(0, IID_PPV_ARGS(&factory));
318 return std::unexpected(
"DXGI のファクトリを作れない: " +
DescribeHresult(result));
320 DeviceResult created = kind ==
AdapterKind::Warp ? CreateWarpDevice(factory.Get())
323 return std::unexpected(created.error());
327 device.
m_device = std::move(*created);
336 auto sink = std::make_unique<MessageSink>();
337 if (FAILED(device->QueryInterface(IID_PPV_ARGS(&sink->infoQueue)))) {
344 &sink->callbackCookie))) {
345 sink->infoQueue.Reset();
357 D3D12_MESSAGE_ID
id, LPCSTR description,
void* context) {
359 const Level level = LevelOfSeverity(severity);
361 sink->errorCount.fetch_add(1, std::memory_order_relaxed);
364 sink->warningCount.fetch_add(1, std::memory_order_relaxed);
366 Log(
Channel::Gpu, level,
"D3D12 [{}] {}",
static_cast<int>(
id), description !=
nullptr ? description :
"");
383 ComPtr<ID3D12DeviceRemovedExtendedData1> dred;
384 if (FAILED(device->QueryInterface(IID_PPV_ARGS(&dred))))
387 D3D12_DRED_AUTO_BREADCRUMBS_OUTPUT1 breadcrumbs{};
388 const HRESULT breadcrumbResult = dred->GetAutoBreadcrumbsOutput1(&breadcrumbs);
389 if (FAILED(breadcrumbResult)) {
396 for (
const D3D12_AUTO_BREADCRUMB_NODE1* node = breadcrumbs.pHeadAutoBreadcrumbNode; node !=
nullptr;
397 node = node->pNext) {
399 LogBreadcrumbNode(*node);
402 D3D12_DRED_PAGE_FAULT_OUTPUT1 pageFault{};
403 if (SUCCEEDED(dred->GetPageFaultAllocationOutput1(&pageFault)) && pageFault.PageFaultVA != 0) {
406 LogAllocations(
"そこにあるもの", pageFault.pHeadExistingAllocationNode);
407 LogAllocations(
"最近解放したもの", pageFault.pHeadRecentFreedAllocationNode);
uint32_t ValidationErrorCount() const
static std::unique_ptr< MessageSink > AttachMessageSink(ID3D12Device5 *device)
static std::expected< Device, std::string > Create(AdapterKind kind, const DeviceOptions &options=DefaultDeviceOptions())
ComPtr< IDXGIFactory6 > m_factory
uint32_t ValidationWarningCount() const
ID3D12Device5 * Get() const
static void __stdcall OnDebugMessage(D3D12_MESSAGE_CATEGORY category, D3D12_MESSAGE_SEVERITY severity, D3D12_MESSAGE_ID id, LPCSTR description, void *context)
ComPtr< ID3D12Device5 > m_device
std::unique_ptr< MessageSink > m_messageSink
#define BICAMERAL_CHECK_HR(channel, expression)
std::expected< ComPtr< ID3D12Device5 >, std::string > DeviceResult
std::string DebugNameOf(const wchar_t *name)
std::string BreadcrumbOpName(D3D12_AUTO_BREADCRUMB_OP op)
ComPtr< ID3D12Device5 > TryCreateHardwareDevice(IDXGIAdapter1 *adapter)
void EnableDebugFeatures(const DeviceOptions &options)
constexpr D3D_FEATURE_LEVEL WARP_FEATURE_LEVEL
constexpr uint32_t BREADCRUMB_CONTEXT_AFTER
ComPtr< ID3D12Device5 > CreateFirstHardwareDevice(IDXGIFactory6 *factory, HMONITOR monitor)
std::string FindMissingFeatures(ID3D12Device *device)
WarpModule DescribeWarpModule()
constexpr D3D_FEATURE_LEVEL HARDWARE_FEATURE_LEVEL
Level LevelOfSeverity(D3D12_MESSAGE_SEVERITY severity)
constexpr int MAX_LOGGED_ALLOCATIONS
DeviceResult CreateWarpDevice(IDXGIFactory6 *factory)
void LogBreadcrumbNode(const D3D12_AUTO_BREADCRUMB_NODE1 &node)
constexpr uint32_t BREADCRUMB_CONTEXT_BEFORE
void LogAllocations(std::string_view title, const D3D12_DRED_ALLOCATION_NODE1 *node)
bool AdapterOwnsMonitor(IDXGIAdapter1 *adapter, HMONITOR monitor)
DeviceResult CreateHardwareDevice(IDXGIFactory6 *factory, HMONITOR presentMonitor)
DeviceRemovedReport LogDeviceRemoved(ID3D12Device *device)
const char * AdapterKindName(AdapterKind kind)
DeviceOptions DefaultDeviceOptions()
std::string DescribeHresult(HResult result)
void Log(Channel channel, Level level, FormatWithLocation< std::type_identity_t< Args >... > pattern, Args &&... args)
std::string ToUtf8(std::wstring_view text)
uint32_t breadcrumbListCount
uint64_t pageFaultAddress
ComPtr< ID3D12InfoQueue1 > infoQueue