Cogs.Core
GraphicsDeviceD3D11.cpp
1#include "GraphicsDeviceD3D11.h"
2
3#include "BuffersD3D11.h"
4#include "TexturesD3D11.h"
5#include "ContextD3D11.h"
6#include "EffectsD3D11.h"
7#include "FormatsD3D11.h"
8
9#include "Foundation/Logging/Logger.h"
10#include "Foundation/Platform/Unicode.h"
11#include "Foundation/Platform/WindowData.h"
12
13#include <atomic>
14
15namespace {
16 enum {
17 ENV_COGS_USE_DEBUG_LAYER = 0,
18 ENV_COGS_USE_WARP,
19 ENV_COGS_USE_REF,
20 ENV_COGS_USE_DX11_0,
21 ENV_N
22 };
23
24 const char* env_keys[ENV_N] {
25 "COGS_USE_DEBUG_LAYER",
26 "COGS_USE_WARP",
27 "COGS_USE_REF",
28 "COGS_USE_DX11_0"
29 };
30
31 Cogs::Logging::Log logger = Cogs::Logging::getLogger("GraphicsDeviceD3D11");
32}
33
34#ifdef COGS_RENDERING_D3D9SUPPORT
35#include "d3d9.h"
36
37namespace Cogs {
38 struct SharedResources {
39 ResourcePointer<IDirect3D9Ex> d3D9Interface;
40 ResourcePointer<IDirect3DDevice9Ex> d3d9Device;
41 // The shared handle of the D3D resource.
42 HANDLE sharedHandle = 0;
43 // Shared surface, pulled through the shared texture.
44 ResourcePointer<IDirect3DSurface9> sharedSurface;
45 };
46}
47#endif
48
49namespace Cogs {
51 ResourceAllocator(Allocator* baseAllocator) : baseAllocator(baseAllocator ? baseAllocator : defaultAllocator()) {
52 }
53
55 assert(!memorySize && "Rendering resource memory leaked.");
56 }
57
58 void* allocate(size_t size, size_t alignment, MemBlockType type = MemBlockType::Block) override {
59 memorySize += size;
60
61 return baseAllocator->allocate(size, alignment, type);
62 }
63
64 void deallocate(void* ptr, size_t size, MemBlockType type = MemBlockType::Block) override {
65 memorySize -= size;
66 baseAllocator->deallocate(ptr, size, type);
67 }
68
69 Memory::Allocator* baseAllocator;
70 std::atomic<size_t> memorySize = 0;
71 };
72}
73
74Cogs::GraphicsDeviceD3D11::GraphicsDeviceD3D11(RenderingAllocatorInfo * allocateInfo) :
75 resourceAllocator(new ResourceAllocator(allocateInfo ? allocateInfo->resourceAllocator : nullptr)),
76#ifdef COGS_RENDERING_D3D9SUPPORT
77 sharedResources(std::make_unique<SharedResources>()),
78#endif
79 defaultSwapChain(this),
80 buffers(this),
81 textures(this) {
82}
83
85 if (settings.ioHandler) {
86 effects.setIOHandler(settings.ioHandler);
87 }
88
89 renderTargets.initialize(this, &textures);
90
92 if (!initializeDeviceShared()) {
93 return false;
94 }
95 }
96 else {
97 if (!initializeDeviceAndSwapChain()) {
98 return false;
99 }
100 }
101
102 device->QueryInterface(__uuidof(ID3D11InfoQueue), infoQueue.internalVoidPointer());
103
104 if (device5) {
105 capabilities.initialize(device5);
106 syncObjects.setDevice(device5);
107 }
108 else if (device1) {
109 capabilities.initialize(device1);
110 }
111 else {
112 capabilities.initialize(device);
113 }
114 buffers.setDevice(device);
115 effects.setDevice(this, device);
116 context.setDevice(device, device1);
117
118 buffers.initialize(&context, &effects);
119 textures.initialize(&context);
120 context.initialize(this, &buffers, &textures, &effects, &renderTargets, &syncObjects);
121 return true;
122}
123
125 inFrame = true;
126
127 context.frameStatisticsBeginFrame();
128 context.setDefaults();
129
130 defaultSwapChain.beginFrame();
131
132 for (ISwapChain* swapChain : swapChains) {
133 swapChain->beginFrame();
134 }
135}
136
137void Cogs::GraphicsDeviceD3D11::endFrame(uint32_t syncInterval, uint32_t presentFlags) {
138 static std::vector<uint8_t> buffer;
139 static constexpr Logging::Category categories[] = {
140 Logging::Category::Fatal,
141 Logging::Category::Error,
142 Logging::Category::Warning,
143 Logging::Category::Debug,
144 Logging::Category::Trace
145 };
146
147 for (ISwapChain* swapChain : swapChains) {
148 swapChain->endFrame(0, presentFlags);
149 }
150 defaultSwapChain.endFrame(syncInterval, presentFlags);
151 context.endFrame();
152
153 if (infoQueue) {
154 for(uint64_t idx = 0, messageCount = infoQueue->GetNumStoredMessages(); idx < messageCount; ++idx) {
155 size_t messageSize = 0;
156
157 infoQueue->GetMessage(idx, nullptr, &messageSize);
158 buffer.resize(messageSize);
159
160 D3D11_MESSAGE* message = reinterpret_cast<D3D11_MESSAGE*>(buffer.data());
161
162 infoQueue->GetMessage(idx, message, &messageSize);
163
164 logger.logFileLine(__FILE__, __LINE__, categories[message->Severity], Cogs::Logging::ErrorGroup::Unspecified, message->pDescription);
165 }
166 infoQueue->ClearStoredMessages();
167 }
168 inFrame = false;
169}
170
172 effects.releaseResources();
173 renderTargets.releaseResources();
174 textures.releaseResources();
175 buffers.releaseResources();
176}
177
179 SwapChainD3D11* swapChain = new SwapChainD3D11(this);
180
181 if (swapChain->initialize(windowData)) {
182 swapChains.push_back(swapChain);
183 return swapChain;
184 }
185 delete swapChain;
186 return nullptr;
187}
188
190 auto e = swapChains.end();
191 auto i = std::find(swapChains.begin(), e, swapChain);
192
193 if (i != e) {
194 swapChains.erase(i);
195 delete swapChain;
196 }
197}
198
199void Cogs::GraphicsDeviceD3D11::setSize(int newWidth, int newHeight) {
200 defaultSwapChain.setSize(newWidth, newHeight);
201}
202
203bool Cogs::GraphicsDeviceD3D11::getSize(int& w, int& h) const {
204 w = defaultSwapChain.getWidth();
205 h = defaultSwapChain.getHeight();
206 return true;
207}
208
209bool Cogs::GraphicsDeviceD3D11::resizeSharedSurfaces()
210{
211#ifdef COGS_RENDERING_D3D9SUPPORT
213 HRESULT hr = defaultSwapChain.getSharedBackBuffer()->QueryInterface(__uuidof(IDXGIResource), dxgiSurface.internalVoidPointer());
214
215 if (FAILED(hr)) {
216 LOG_ERROR(logger, "QueryInterface failed in resizeSharedSurfaces: %s", direct3D11ReturnCodeAsString(hr));
217 return false;
218 }
219 hr = dxgiSurface->GetSharedHandle(&sharedResources->sharedHandle);
220
221 if (FAILED(hr)) {
222 LOG_ERROR(logger, "GetSharedHandle failed in resizeSharedSurfaces: %s", direct3D11ReturnCodeAsString(hr));
223 return false;
224 }
225
226 sharedResources->sharedSurface = {};
227
228 ResourcePointer<IDirect3DTexture9> sharedTexture;
229 D3DFORMAT d3D9Format = D3DFMT_A8R8G8B8;
230
231 hr = sharedResources->d3d9Device->CreateTexture(defaultSwapChain.getWidth(),
232 defaultSwapChain.getHeight(),
233 1,
234 D3DUSAGE_RENDERTARGET,
235 d3D9Format,
236 D3DPOOL_DEFAULT,
237 sharedTexture.internalPointer(),
238 &sharedResources->sharedHandle);
239
240 if (FAILED(hr)) {
241 LOG_ERROR(logger, "Failed to create D3D9 texture in resizeSharedSurfaces: %s", direct3D11ReturnCodeAsString(hr));
242 return false;
243 }
244
245 hr = sharedTexture->GetSurfaceLevel(0, sharedResources->sharedSurface.internalPointer());
246
247 if (FAILED(hr)) {
248 LOG_ERROR(logger, "Get surface level failed in resizeSharedSurfaces: %s", direct3D11ReturnCodeAsString(hr));
249 return false;
250 }
251 if (settings.sharedSurface) {
252 *settings.sharedSurface = sharedResources->sharedSurface;
253 }
254#endif
255 return true;
256}
257
258bool Cogs::GraphicsDeviceD3D11::initializeDeviceAndSwapChain() {
259 HRESULT hr = S_OK;
260
261 char tmp[256];
262 auto * tmp_p = &tmp[0];
263 auto * tmp_e = tmp_p + sizeof(tmp);
264
265 bool envvars[ENV_N];
266 for (unsigned i = 0; i < ENV_N; i++) {
267 envvars[i] = getenv(env_keys[i]);
268 auto n = strlen(env_keys[i]);
269 if (i != 0 && tmp_p + 1 < tmp_e) *tmp_p++ = ' ';
270 for (size_t k = 0; k < n && tmp_p + 1 < tmp_e; k++) {
271 *tmp_p++ = env_keys[i][k];
272 }
273 if (tmp_p + 1 < tmp_e) *tmp_p++ = '=';
274 if (tmp_p + 1 < tmp_e) *tmp_p++ = envvars[i] ? '1' : '0';
275 }
276 assert(tmp_p < tmp_e);
277 *tmp_p++ = '\0';
278 LOG_DEBUG(logger, "envvars: %s", tmp);
279
280 std::vector<ResourcePointer<IDXGIAdapter>> adapters;
281
282 std::vector<D3D_DRIVER_TYPE> driverTypes;
283 if (((settings.flags & GraphicsDeviceFlags::ForceSoftwareRendering) == GraphicsDeviceFlags::ForceSoftwareRendering) || envvars[ENV_COGS_USE_WARP]) {
284 driverTypes.push_back(D3D_DRIVER_TYPE_WARP);
285 }
286 else if (envvars[ENV_COGS_USE_REF]) {
287 driverTypes.push_back(D3D_DRIVER_TYPE_REFERENCE);
288 }
289 else {
290 driverTypes = { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_REFERENCE };
291
292 ResourcePointer<IDXGIFactory5> dxgiFactory;
293
294 if (SUCCEEDED(CreateDXGIFactory(__uuidof(IDXGIFactory5), dxgiFactory.internalVoidPointer()))) {
295 ResourcePointer<IDXGIAdapter> adapter;
296
297 for (uint32_t idx = 0; dxgiFactory->EnumAdapters(idx, adapter.internalPointer()) != DXGI_ERROR_NOT_FOUND; ++idx) {
298 DXGI_ADAPTER_DESC desc;
299
300 adapter->GetDesc(&desc);
301
302 if (desc.VendorId == 0x10DE) { // 0x10DE == NVIDIA
303 adapters.push_back(adapter);
304 }
305 }
306 }
307 }
308
309 adapters.push_back({}); // Add a default adapter if no nvidia gpus are present.
310
311 std::vector<D3D_FEATURE_LEVEL> FeatureLevelsRequested;
312 if (envvars[ENV_COGS_USE_DX11_0]) {
313 FeatureLevelsRequested.push_back(D3D_FEATURE_LEVEL_11_0);
314 }
315 else {
316 FeatureLevelsRequested = {
317 D3D_FEATURE_LEVEL_11_1,
318 D3D_FEATURE_LEVEL_11_0
319 };
320 }
321
322 bool debug = isDebug() || ((settings.flags & GraphicsDeviceFlags::Debug) == GraphicsDeviceFlags::Debug) || envvars[ENV_COGS_USE_DEBUG_LAYER];
323 D3D_FEATURE_LEVEL apiFeatureLevel = D3D_FEATURE_LEVEL_11_0;
324
325 assert(!driverTypes.empty());
326 assert(!FeatureLevelsRequested.empty());
327
328 for (auto i = adapters.begin(), e = adapters.end(); (i != e) && !device; ++i) {
329 for (auto driverType : driverTypes) {
330
331 // If we're specifying an adapter, we must ignore the driver type.
332 hr = E_INVALIDARG;
333 if (debug) {
334 hr = createDevice(*i, driverType, D3D11_CREATE_DEVICE_DEBUG, FeatureLevelsRequested, apiFeatureLevel);
335 }
336 if (FAILED(hr)) {
337 hr = createDevice(*i, driverType, 0, FeatureLevelsRequested, apiFeatureLevel);
338 }
339
340 if (SUCCEEDED(hr)) {
341 if (driverType == D3D_DRIVER_TYPE_WARP) {
342 LOG_WARNING(logger, "Software rendering via WARP device.");
343 }
344 else if (driverType == D3D_DRIVER_TYPE_REFERENCE) {
345 LOG_WARNING(logger, "Software rendering via reference driver.");
346 }
347 if (apiFeatureLevel == D3D_FEATURE_LEVEL_11_0) {
348 LOG_WARNING(logger, "Got feature level 11.0");
349 }
350 break;
351 }
352 }
353 }
354 if (FAILED(hr)) {
355 LOG_FATAL(logger, "Could not create Direct3D 11 device: %s", direct3D11ReturnCodeAsString(hr));
356 return false;
357 }
358
359 device1 = nullptr;
360 if (apiFeatureLevel != D3D_FEATURE_LEVEL_11_0) {
361 hr = device->QueryInterface(__uuidof(ID3D11Device1), (void **)&device1);
362 if (SUCCEEDED(hr)) {
363 LOG_DEBUG(logger, "Got ID3D11Device1 interface");
364 }
365 }
366
367 if (hr = device->QueryInterface(__uuidof(ID3D11Device5), (void**)&device5); SUCCEEDED(hr)) {
368 LOG_DEBUG(logger, "Got ID3D11Device5 interface");
369 }
370
371 if (debug) {
372 setupDebugging();
373 }
374
375 return defaultSwapChain.initialize(settings.windowData);
376}
377
378bool Cogs::GraphicsDeviceD3D11::initializeDeviceShared() {
379#ifdef COGS_RENDERING_D3D9SUPPORT
380 LOG_DEBUG(logger, "Initializing D3D11 with shared surface support...");
381 // Fetch desktop window to create dummy Direct3D9Ex device in.
382 auto desktopHwnd = GetDesktopWindow();
383 HRESULT hr;
384 if (FAILED(hr = Direct3DCreate9Ex(D3D_SDK_VERSION, sharedResources->d3D9Interface.internalPointer()))) {
385 LOG_ERROR(logger, "Could not create Direct3D 9 Ex interface: %s", direct3D11ReturnCodeAsString(hr));
386 return false;
387 }
388 D3DPRESENT_PARAMETERS presentParameters = {};
389 presentParameters.Windowed = TRUE;
390 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
391 presentParameters.hDeviceWindow = desktopHwnd;
392 presentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
393 if (FAILED(hr = sharedResources->d3D9Interface->CreateDeviceEx(D3DADAPTER_DEFAULT,
394 D3DDEVTYPE_HAL,
395 desktopHwnd,
396 D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
397 &presentParameters,
398 nullptr,
399 sharedResources->d3d9Device.internalPointer()))) {
400 LOG_ERROR(logger, "Could not create Direct3D 9 Ex device: %s", direct3D11ReturnCodeAsString(hr));
401 return false;
402 }
403 const UINT deviceFlags = isDebug() ? D3D11_CREATE_DEVICE_DEBUG : 0;
404 const D3D_FEATURE_LEVEL level = D3D_FEATURE_LEVEL_11_0;
405 if (FAILED(hr = D3D11CreateDevice(nullptr,
406 D3D_DRIVER_TYPE_HARDWARE,
407 nullptr,
408 deviceFlags,
409 &level,
410 1,
411 D3D11_SDK_VERSION,
412 device.internalPointer(),
413 nullptr,
414 nullptr))) {
415 LOG_ERROR(logger, "Could not create Direct3D 11 device: %s", direct3D11ReturnCodeAsString(hr));
416 return false;
417 }
418 if (defaultSwapChain.recreateOffscreenBuffers()) {
419 return resizeSharedSurfaces();
420 }
421#endif
422 return false;
423}
424
425void Cogs::GraphicsDeviceD3D11::setupDebugging()
426{
427 HRESULT hr;
428 ResourcePointer<ID3D11Debug> d3dDebug;
429 if (FAILED(hr = device->QueryInterface(__uuidof(ID3D11Debug), (void**)&d3dDebug))) {
430 LOG_WARNING(logger, "Could not get ID3D11Debug interface: %s", direct3D11ReturnCodeAsString(hr));
431 return;
432 }
433
434 ResourcePointer<ID3D11InfoQueue> d3dInfoQueue;
435 if (FAILED(hr = d3dDebug->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)&d3dInfoQueue))) {
436 LOG_WARNING(logger, "Could not get ID3D11InfoQueue interface: %s", direct3D11ReturnCodeAsString(hr));
437 return;
438 }
439
440#ifdef COGSRENDERING_GFX_BREAK
441 d3dInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
442 d3dInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
443 LOG_DEBUG(logger, "Break to debugger on D3D errors.");
444#endif
445
446 // Messages to ignore
447 D3D11_MESSAGE_ID hide[] = {
448 D3D11_MESSAGE_ID_SETPRIVATEDATA_CHANGINGPARAMS,
449 };
450
451 D3D11_INFO_QUEUE_FILTER filter = {};
452 filter.DenyList.NumIDs = _countof(hide);
453 filter.DenyList.pIDList = hide;
454
455 if (FAILED(hr = d3dInfoQueue->AddStorageFilterEntries(&filter))) {
456 LOG_WARNING(logger, "Could not add storage filter entries: %s", direct3D11ReturnCodeAsString(hr));
457 }
458 LOG_DEBUG(logger, "Set up debug layer.");
459}
460
461Cogs::ResourceStatistics Cogs::GraphicsDeviceD3D11::getResourceStatistics() {
462 ResourceStatistics stats{};
463
464 stats.bufferMemoryConsumption = buffers.bufferMemoryConsumption;
465 stats.textureMemoryConsumption = textures.textureMemoryConsumption;
466 stats.bufferCount = static_cast<uint32_t>(buffers.buffers.size());
467 stats.inputLayoutCount = static_cast<uint32_t>(buffers.inputLayouts.size());
468 stats.textureCount = static_cast<uint32_t>(textures.textures.size());
469 stats.samplerStateCount = static_cast<uint32_t>(textures.samplerStates.size());
470 stats.effectCount = static_cast<uint32_t>(effects.effects.size());
471 stats.blendStateCount = static_cast<uint32_t>(renderTargets.blendStates.size());
472 stats.rasterizerStateCount = static_cast<uint32_t>(renderTargets.rasterizerStates.size());
473 stats.depthStencilStateCount = static_cast<uint32_t>(renderTargets.depthStencilStates.size());
474 stats.rendertargetsCount = static_cast<uint32_t>(renderTargets.renderTargets.size());
475 stats.framebufferCount = ~0u;
476
477 return stats;
478}
479
480HRESULT Cogs::GraphicsDeviceD3D11::createDevice(IDXGIAdapter* adapter,
481 D3D_DRIVER_TYPE driverType,
482 UINT flags,
483 const std::vector<D3D_FEATURE_LEVEL>& featureLevelsRequested,
484 D3D_FEATURE_LEVEL& apiFeatureLevel) {
485 if (adapter) {
486 driverType = D3D_DRIVER_TYPE_UNKNOWN;
487 }
488 HRESULT hr = D3D11CreateDevice(adapter,
489 driverType,
490 nullptr,
491 flags,
492 featureLevelsRequested.data(),
493 (UINT)featureLevelsRequested.size(),
494 D3D11_SDK_VERSION,
495 device.internalPointer(),
496 &apiFeatureLevel,
497 nullptr);
498 if (FAILED(hr)) {
499 constexpr D3D_FEATURE_LEVEL featureLevel_11_0 = D3D_FEATURE_LEVEL_11_0;
500
501 // If we failed, retry immediately with just a 11.0 feature level.
502 //
503 // "If you provide a D3D_FEATURE_LEVEL array that contains D3D_FEATURE_LEVEL_11_1
504 // on a computer that doesn't have the Direct3D 11.1 runtime installed, this
505 // function immediately fails with E_INVALIDARG."
506 hr = D3D11CreateDevice(adapter,
507 driverType,
508 nullptr,
509 flags,
510 &featureLevel_11_0,
511 1,
512 D3D11_SDK_VERSION,
513 device.internalPointer(),
514 &apiFeatureLevel,
515 nullptr);
516 if (SUCCEEDED(hr)) {
517 LOG_WARNING(logger, "D3D11CreateDevice didn't recognize feature level 11.1, KB2670838 is probably missing.");
518 }
519 }
520 return hr;
521}
Log implementation class.
Definition: LogManager.h:139
void logFileLine(const char *file, const int line, const Category category, uint32_t errorNumber, _Printf_format_string_ const char *fmt,...) const VALIDATE_ARGS(6)
Log a formatted message with file/line information.
Definition: LogManager.h:147
Base allocator implementation.
Definition: Allocator.h:30
static Allocator * defaultAllocator()
Gets the system default allocator.
Definition: Allocator.cpp:17
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
@ Unspecified
The default error number for legacy logger usage.
Definition: LogManager.h:49
Category
Logging categories used to filter log messages.
Definition: LogManager.h:31
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
@ ForceSoftwareRendering
Force software rendering.
@ Debug
If available, the device will operate in debug mode, performing additional validation of input data,...
@ UseSharedSurface
Use shared surface for D3D9 interop.
STL namespace.
bool initialize()
Initializes the graphics device with the settings previous set through calling setSettings.
void setSize(int newWidth, int newHeight)
Set the size of the main drawing buffer used by the graphics device in pixels.
void beginFrame()
Signal the beginning of a new frame to the graphics device.
void endFrame(uint32_t syncInterval=0, uint32_t presentFlags=PresentFlags::None)
Signal the end of a frame to the graphics device.
virtual ISwapChain * createSwapChain(struct WindowData *windowData) override
Create a new swap chain for the specified window.
void releaseResources()
Release all resources allocated.
virtual void deleteSwapChain(ISwapChain *swapChain) override
Deletes the specified swap chain.
bool getSize(int &w, int &h) const override
Retrieve the size previously set by setSize.
void * allocate(size_t size, size_t alignment, MemBlockType type=MemBlockType::Block) override
Allocate raw memory.
void deallocate(void *ptr, size_t size, MemBlockType type=MemBlockType::Block) override
Deallocate the memory block at the given pointer, with the given size.