blob: e2d54005eefb0161d725a12f35a2c5146b2ae3d9 [file] [log] [blame]
daniel@transgaming.com621ce052012-10-31 17:52:29 +00001//
2// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Renderer.cpp: Implements a back-end specific class that hides the details of the
8// implementation-specific renderer.
9
10#include "libGLESv2/renderer/Renderer.h"
11#include "common/debug.h"
12#include "libGLESv2/utilities.h"
13
14// Can also be enabled by defining FORCE_REF_RAST in the project's predefined macros
15#define REF_RAST 0
16
17// The "Debug This Pixel..." feature in PIX often fails when using the
18// D3D9Ex interfaces. In order to get debug pixel to work on a Vista/Win 7
19// machine, define "ANGLE_ENABLE_D3D9EX=0" in your project file.
20#if !defined(ANGLE_ENABLE_D3D9EX)
21// Enables use of the IDirect3D9Ex interface, when available
22#define ANGLE_ENABLE_D3D9EX 1
23#endif // !defined(ANGLE_ENABLE_D3D9EX)
24
25namespace renderer
26{
27
28Renderer::Renderer(HMODULE hModule, HDC hDc): mDc(hDc)
29{
30 mD3d9Module = hModule;
31
32 mD3d9 = NULL;
33 mD3d9Ex = NULL;
34 mDevice = NULL;
35 mDeviceEx = NULL;
36 mDeviceWindow = NULL;
37
38 mAdapter = D3DADAPTER_DEFAULT;
39
40 #if REF_RAST == 1 || defined(FORCE_REF_RAST)
41 mDeviceType = D3DDEVTYPE_REF;
42 #else
43 mDeviceType = D3DDEVTYPE_HAL;
44 #endif
45
46 mDeviceLost = false;
47}
48
49Renderer::~Renderer()
50{
51 if (mDevice)
52 {
53 // If the device is lost, reset it first to prevent leaving the driver in an unstable state
54 if (testDeviceLost())
55 {
56 resetDevice();
57 }
58
59 mDevice->Release();
60 mDevice = NULL;
61 }
62
63 if (mDeviceEx)
64 {
65 mDeviceEx->Release();
66 mDeviceEx = NULL;
67 }
68
69 if (mD3d9)
70 {
71 mD3d9->Release();
72 mD3d9 = NULL;
73 }
74
75 if (mDeviceWindow)
76 {
77 DestroyWindow(mDeviceWindow);
78 mDeviceWindow = NULL;
79 }
80
81 if (mD3d9Ex)
82 {
83 mD3d9Ex->Release();
84 mD3d9Ex = NULL;
85 }
86
87 if (mD3d9Module)
88 {
89 mD3d9Module = NULL;
90 }
91
92}
93
94EGLint Renderer::initialize()
95{
96 typedef HRESULT (WINAPI *Direct3DCreate9ExFunc)(UINT, IDirect3D9Ex**);
97 Direct3DCreate9ExFunc Direct3DCreate9ExPtr = reinterpret_cast<Direct3DCreate9ExFunc>(GetProcAddress(mD3d9Module, "Direct3DCreate9Ex"));
98
99 // Use Direct3D9Ex if available. Among other things, this version is less
100 // inclined to report a lost context, for example when the user switches
101 // desktop. Direct3D9Ex is available in Windows Vista and later if suitable drivers are available.
102 if (ANGLE_ENABLE_D3D9EX && Direct3DCreate9ExPtr && SUCCEEDED(Direct3DCreate9ExPtr(D3D_SDK_VERSION, &mD3d9Ex)))
103 {
104 ASSERT(mD3d9Ex);
105 mD3d9Ex->QueryInterface(IID_IDirect3D9, reinterpret_cast<void**>(&mD3d9));
106 ASSERT(mD3d9);
107 }
108 else
109 {
110 mD3d9 = Direct3DCreate9(D3D_SDK_VERSION);
111 }
112
113 if (!mD3d9)
114 {
115 ERR("Could not create D3D9 device - aborting!\n");
116 return EGL_NOT_INITIALIZED;
117 }
118 if (mDc != NULL)
119 {
120 // UNIMPLEMENTED(); // FIXME: Determine which adapter index the device context corresponds to
121 }
122
123 HRESULT result;
124
125 // Give up on getting device caps after about one second.
126 for (int i = 0; i < 10; ++i)
127 {
128 result = mD3d9->GetDeviceCaps(mAdapter, mDeviceType, &mDeviceCaps);
129 if (SUCCEEDED(result))
130 {
131 break;
132 }
133 else if (result == D3DERR_NOTAVAILABLE)
134 {
135 Sleep(100); // Give the driver some time to initialize/recover
136 }
137 else if (FAILED(result)) // D3DERR_OUTOFVIDEOMEMORY, E_OUTOFMEMORY, D3DERR_INVALIDDEVICE, or another error we can't recover from
138 {
139 ERR("failed to get device caps (0x%x)\n", result);
140 return EGL_NOT_INITIALIZED;
141 }
142 }
143
144 if (mDeviceCaps.PixelShaderVersion < D3DPS_VERSION(2, 0))
145 {
146 ERR("Renderer does not support PS 2.0. aborting!\n");
147 return EGL_NOT_INITIALIZED;
148 }
149
150 // When DirectX9 is running with an older DirectX8 driver, a StretchRect from a regular texture to a render target texture is not supported.
151 // This is required by Texture2D::convertToRenderTarget.
152 if ((mDeviceCaps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES) == 0)
153 {
154 ERR("Renderer does not support stretctrect from textures!\n");
155 return EGL_NOT_INITIALIZED;
156 }
157
158 mD3d9->GetAdapterIdentifier(mAdapter, 0, &mAdapterIdentifier);
159
160 // ATI cards on XP have problems with non-power-of-two textures.
161 mSupportsNonPower2Textures = !(mDeviceCaps.TextureCaps & D3DPTEXTURECAPS_POW2) &&
162 !(mDeviceCaps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP_POW2) &&
163 !(mDeviceCaps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL) &&
164 !(getComparableOSVersion() < versionWindowsVista && mAdapterIdentifier.VendorId == VENDOR_ID_AMD);
165
166 static const TCHAR windowName[] = TEXT("AngleHiddenWindow");
167 static const TCHAR className[] = TEXT("STATIC");
168
169 mDeviceWindow = CreateWindowEx(WS_EX_NOACTIVATE, className, windowName, WS_DISABLED | WS_POPUP, 0, 0, 1, 1, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
170
171 D3DPRESENT_PARAMETERS presentParameters = getDefaultPresentParameters();
172 DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE | D3DCREATE_NOWINDOWCHANGES;
173
174 result = mD3d9->CreateDevice(mAdapter, mDeviceType, mDeviceWindow, behaviorFlags | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE, &presentParameters, &mDevice);
175 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DEVICELOST)
176 {
177 return EGL_BAD_ALLOC;
178 }
179
180 if (FAILED(result))
181 {
182 result = mD3d9->CreateDevice(mAdapter, mDeviceType, mDeviceWindow, behaviorFlags | D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentParameters, &mDevice);
183
184 if (FAILED(result))
185 {
186 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_NOTAVAILABLE || result == D3DERR_DEVICELOST);
187 return EGL_BAD_ALLOC;
188 }
189 }
190
191 if (mD3d9Ex)
192 {
193 result = mDevice->QueryInterface(IID_IDirect3DDevice9Ex, (void**) &mDeviceEx);
194 ASSERT(SUCCEEDED(result));
195 }
196
197 initializeDevice();
198
199 return EGL_SUCCESS;
200}
201
202// do any one-time device initialization
203// NOTE: this is also needed after a device lost/reset
204// to reset the scene status and ensure the default states are reset.
205void Renderer::initializeDevice()
206{
207 // Permanent non-default states
208 mDevice->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE);
209 mDevice->SetRenderState(D3DRS_LASTPIXEL, FALSE);
210
211 if (mDeviceCaps.PixelShaderVersion >= D3DPS_VERSION(3, 0))
212 {
213 mDevice->SetRenderState(D3DRS_POINTSIZE_MAX, (DWORD&)mDeviceCaps.MaxPointSize);
214 }
215 else
216 {
217 mDevice->SetRenderState(D3DRS_POINTSIZE_MAX, 0x3F800000); // 1.0f
218 }
219
220 mSceneStarted = false;
221}
222
223D3DPRESENT_PARAMETERS Renderer::getDefaultPresentParameters()
224{
225 D3DPRESENT_PARAMETERS presentParameters = {0};
226
227 // The default swap chain is never actually used. Surface will create a new swap chain with the proper parameters.
228 presentParameters.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
229 presentParameters.BackBufferCount = 1;
230 presentParameters.BackBufferFormat = D3DFMT_UNKNOWN;
231 presentParameters.BackBufferWidth = 1;
232 presentParameters.BackBufferHeight = 1;
233 presentParameters.EnableAutoDepthStencil = FALSE;
234 presentParameters.Flags = 0;
235 presentParameters.hDeviceWindow = mDeviceWindow;
236 presentParameters.MultiSampleQuality = 0;
237 presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE;
238 presentParameters.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
239 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
240 presentParameters.Windowed = TRUE;
241
242 return presentParameters;
243}
244
245void Renderer::startScene()
246{
247 if (!mSceneStarted)
248 {
249 long result = mDevice->BeginScene();
250 if (SUCCEEDED(result)) {
251 // This is defensive checking against the device being
252 // lost at unexpected times.
253 mSceneStarted = true;
254 }
255 }
256}
257
258void Renderer::endScene()
259{
260 if (mSceneStarted)
261 {
262 // EndScene can fail if the device was lost, for example due
263 // to a TDR during a draw call.
264 mDevice->EndScene();
265 mSceneStarted = false;
266 }
267}
268
269void Renderer::markDeviceLost()
270{
271 mDeviceLost = true;
272}
273
274bool Renderer::isDeviceLost()
275{
276 return mDeviceLost;
277}
278
279bool Renderer::testDeviceLost()
280{
281 bool isLost = false;
282
283 if (mDeviceEx)
284 {
285 isLost = FAILED(mDeviceEx->CheckDeviceState(NULL));
286 }
287 else if (mDevice)
288 {
289 isLost = FAILED(mDevice->TestCooperativeLevel());
290 }
291 else
292 {
293 // No device yet, so no reset required
294 }
295
296 if (isLost)
297 {
298 // ensure we note the device loss --
299 // we'll probably get this done again by markDeviceLost
300 // but best to remember it!
301 // Note that we don't want to clear the device loss status here
302 // -- this needs to be done by resetDevice
303 mDeviceLost = true;
304 }
305
306 return isLost;
307}
308
309bool Renderer::testDeviceResettable()
310{
311 HRESULT status = D3D_OK;
312
313 if (mDeviceEx)
314 {
315 status = mDeviceEx->CheckDeviceState(NULL);
316 }
317 else if (mDevice)
318 {
319 status = mDevice->TestCooperativeLevel();
320 }
321
322 switch (status)
323 {
324 case D3DERR_DEVICENOTRESET:
325 case D3DERR_DEVICEHUNG:
326 return true;
327 default:
328 return false;
329 }
330}
331
332bool Renderer::resetDevice()
333{
334 D3DPRESENT_PARAMETERS presentParameters = getDefaultPresentParameters();
335
336 HRESULT result = D3D_OK;
337 bool lost = testDeviceLost();
338 int attempts = 3;
339
340 while (lost && attempts > 0)
341 {
342 if (mDeviceEx)
343 {
344 Sleep(500); // Give the graphics driver some CPU time
345 result = mDeviceEx->ResetEx(&presentParameters, NULL);
346 }
347 else
348 {
349 result = mDevice->TestCooperativeLevel();
350 while (result == D3DERR_DEVICELOST)
351 {
352 Sleep(100); // Give the graphics driver some CPU time
353 result = mDevice->TestCooperativeLevel();
354 }
355
356 if (result == D3DERR_DEVICENOTRESET)
357 {
358 result = mDevice->Reset(&presentParameters);
359 }
360 }
361
362 lost = testDeviceLost();
363 attempts --;
364 }
365
366 if (FAILED(result))
367 {
368 ERR("Reset/ResetEx failed multiple times: 0x%08X", result);
369 return false;
370 }
371
372 // reset device defaults
373 initializeDevice();
374 mDeviceLost = false;
375
376 return true;
377}
378
379void Renderer::getMultiSampleSupport(D3DFORMAT format, bool *multiSampleArray)
380{
381 for (int multiSampleIndex = 0; multiSampleIndex <= D3DMULTISAMPLE_16_SAMPLES; multiSampleIndex++)
382 {
383 HRESULT result = mD3d9->CheckDeviceMultiSampleType(mAdapter, mDeviceType, format,
384 TRUE, (D3DMULTISAMPLE_TYPE)multiSampleIndex, NULL);
385
386 multiSampleArray[multiSampleIndex] = SUCCEEDED(result);
387 }
388}
389
390bool Renderer::getDXT1TextureSupport()
391{
392 D3DDISPLAYMODE currentDisplayMode;
393 mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
394
395 return SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0, D3DRTYPE_TEXTURE, D3DFMT_DXT1));
396}
397
398bool Renderer::getDXT3TextureSupport()
399{
400 D3DDISPLAYMODE currentDisplayMode;
401 mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
402
403 return SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0, D3DRTYPE_TEXTURE, D3DFMT_DXT3));
404}
405
406bool Renderer::getDXT5TextureSupport()
407{
408 D3DDISPLAYMODE currentDisplayMode;
409 mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
410
411 return SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0, D3DRTYPE_TEXTURE, D3DFMT_DXT5));
412}
413
414// we use INTZ for depth textures in Direct3D9
415// we also want NULL texture support to ensure the we can make depth-only FBOs
416// see http://aras-p.info/texts/D3D9GPUHacks.html
417bool Renderer::getDepthTextureSupport() const
418{
419 D3DDISPLAYMODE currentDisplayMode;
420 mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
421
422 bool intz = SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format,
423 D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, D3DFMT_INTZ));
424 bool null = SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format,
425 D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, D3DFMT_NULL));
426
427 return intz && null;
428}
429
430bool Renderer::getFloat32TextureSupport(bool *filtering, bool *renderable)
431{
432 D3DDISPLAYMODE currentDisplayMode;
433 mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
434
435 *filtering = SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, D3DUSAGE_QUERY_FILTER,
436 D3DRTYPE_TEXTURE, D3DFMT_A32B32G32R32F)) &&
437 SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, D3DUSAGE_QUERY_FILTER,
438 D3DRTYPE_CUBETEXTURE, D3DFMT_A32B32G32R32F));
439
440 *renderable = SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, D3DUSAGE_RENDERTARGET,
441 D3DRTYPE_TEXTURE, D3DFMT_A32B32G32R32F))&&
442 SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, D3DUSAGE_RENDERTARGET,
443 D3DRTYPE_CUBETEXTURE, D3DFMT_A32B32G32R32F));
444
445 if (!*filtering && !*renderable)
446 {
447 return SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0,
448 D3DRTYPE_TEXTURE, D3DFMT_A32B32G32R32F)) &&
449 SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0,
450 D3DRTYPE_CUBETEXTURE, D3DFMT_A32B32G32R32F));
451 }
452 else
453 {
454 return true;
455 }
456}
457
458bool Renderer::getFloat16TextureSupport(bool *filtering, bool *renderable)
459{
460 D3DDISPLAYMODE currentDisplayMode;
461 mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
462
463 *filtering = SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, D3DUSAGE_QUERY_FILTER,
464 D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F)) &&
465 SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, D3DUSAGE_QUERY_FILTER,
466 D3DRTYPE_CUBETEXTURE, D3DFMT_A16B16G16R16F));
467
468 *renderable = SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, D3DUSAGE_RENDERTARGET,
469 D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F)) &&
470 SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, D3DUSAGE_RENDERTARGET,
471 D3DRTYPE_CUBETEXTURE, D3DFMT_A16B16G16R16F));
472
473 if (!*filtering && !*renderable)
474 {
475 return SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0,
476 D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F)) &&
477 SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0,
478 D3DRTYPE_CUBETEXTURE, D3DFMT_A16B16G16R16F));
479 }
480 else
481 {
482 return true;
483 }
484}
485
486bool Renderer::getLuminanceTextureSupport()
487{
488 D3DDISPLAYMODE currentDisplayMode;
489 mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
490
491 return SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0, D3DRTYPE_TEXTURE, D3DFMT_L8));
492}
493
494bool Renderer::getLuminanceAlphaTextureSupport()
495{
496 D3DDISPLAYMODE currentDisplayMode;
497 mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
498
499 return SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0, D3DRTYPE_TEXTURE, D3DFMT_A8L8));
500}
501
502float Renderer::getTextureFilterAnisotropySupport() const
503{
504 // Must support a minimum of 2:1 anisotropy for max anisotropy to be considered supported, per the spec
505 if ((mDeviceCaps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) && (mDeviceCaps.MaxAnisotropy >= 2))
506 {
507 return mDeviceCaps.MaxAnisotropy;
508 }
509 return 1.0f;
510}
511
512bool Renderer::getEventQuerySupport()
513{
514#if 0 // D3D9_REPLACE
515 IDirect3DQuery9 *query = allocateEventQuery();
516 if (query)
517 {
518 freeEventQuery(query);
519 return true;
520 }
521 else
522 {
523 return false;
524 }
525#endif
526 return true;
527}
528
529// Only Direct3D 10 ready devices support all the necessary vertex texture formats.
530// We test this using D3D9 by checking support for the R16F format.
531bool Renderer::getVertexTextureSupport() const
532{
533 if (!mDevice || mDeviceCaps.PixelShaderVersion < D3DPS_VERSION(3, 0))
534 {
535 return false;
536 }
537
538 D3DDISPLAYMODE currentDisplayMode;
539 mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
540
541 HRESULT result = mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, D3DUSAGE_QUERY_VERTEXTEXTURE, D3DRTYPE_TEXTURE, D3DFMT_R16F);
542
543 return SUCCEEDED(result);
544}
545
546bool Renderer::getNonPower2TextureSupport() const
547{
548 return mSupportsNonPower2Textures;
549}
550
551bool Renderer::getOcclusionQuerySupport() const
552{
553 if (!mDevice)
554 {
555 return false;
556 }
557
558 IDirect3DQuery9 *query = NULL;
559 HRESULT result = mDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &query);
560 if (SUCCEEDED(result) && query)
561 {
562 query->Release();
563 return true;
564 }
565 else
566 {
567 return false;
568 }
569}
570
571bool Renderer::getInstancingSupport() const
572{
573 return mDeviceCaps.PixelShaderVersion >= D3DPS_VERSION(3, 0);
574}
575
daniel@transgaming.com313e3922012-10-31 17:52:39 +0000576bool Renderer::getShareHandleSupport() const
577{
578 // PIX doesn't seem to support using share handles, so disable them.
579 // D3D9_REPLACE
580 return isD3d9ExDevice() && !gl::perfActive();
581}
daniel@transgaming.com621ce052012-10-31 17:52:29 +0000582
583D3DPOOL Renderer::getBufferPool(DWORD usage) const
584{
585 if (mD3d9Ex != NULL)
586 {
587 return D3DPOOL_DEFAULT;
588 }
589 else
590 {
591 if (!(usage & D3DUSAGE_DYNAMIC))
592 {
593 return D3DPOOL_MANAGED;
594 }
595 }
596
597 return D3DPOOL_DEFAULT;
598}
599
600D3DPOOL Renderer::getTexturePool(DWORD usage) const
601{
602 if (mD3d9Ex != NULL)
603 {
604 return D3DPOOL_DEFAULT;
605 }
606 else
607 {
608 if (!(usage & (D3DUSAGE_DEPTHSTENCIL | D3DUSAGE_RENDERTARGET)))
609 {
610 return D3DPOOL_MANAGED;
611 }
612 }
613
614 return D3DPOOL_DEFAULT;
615}
616
617}