summaryrefslogtreecommitdiff
path: root/Runtime/GfxDevice/d3d/D3D9Enumeration.cpp
blob: b78433ea3f114ced39f5527e3853825522f910b9 (plain)
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
#include "UnityPrefix.h"
#include "D3D9Enumeration.h"
#include "D3D9Utils.h"
#include "Runtime/GfxDevice/VramLimits.h"

// ---------------------------------------------------------------------------


const int kMinDisplayWidth = 512;
const int kMinDisplayHeight = 384;
const int kMinColorBits = 4;
const int kMinAlphaBits = 0;

extern D3DDEVTYPE g_D3DDevType;
extern DWORD g_D3DAdapter;

// ---------------------------------------------------------------------------

static int GetFormatColorBits( D3DFORMAT fmt ) {
	switch( fmt ) {
	case D3DFMT_A2B10G10R10:
	case D3DFMT_A2R10G10B10:	return 10;
	case D3DFMT_R8G8B8:
	case D3DFMT_A8R8G8B8:
	case D3DFMT_X8R8G8B8:		return 8;
	case D3DFMT_R5G6B5:
	case D3DFMT_X1R5G5B5:
	case D3DFMT_A1R5G5B5:		return 5;
	case D3DFMT_A4R4G4B4:
	case D3DFMT_X4R4G4B4:		return 4;
	case D3DFMT_R3G3B2:
	case D3DFMT_A8R3G3B2:		return 2;
	default:					return 0;
	}
}

static int GetFormatAlphaBits( D3DFORMAT fmt ) {
	switch( fmt ) {
	case D3DFMT_R8G8B8:
	case D3DFMT_X8R8G8B8:
	case D3DFMT_R5G6B5:
	case D3DFMT_X1R5G5B5:
	case D3DFMT_R3G3B2:
	case D3DFMT_X4R4G4B4:		return 0;
	case D3DFMT_A8R8G8B8:
	case D3DFMT_A8R3G3B2:		return 8;
	case D3DFMT_A1R5G5B5:		return 1;
	case D3DFMT_A4R4G4B4:		return 4;
	case D3DFMT_A2B10G10R10:
	case D3DFMT_A2R10G10B10:	return 2;
	default:					return 0;
	}
}

int GetFormatDepthBits( D3DFORMAT fmt ) {
	switch( fmt ) {
	case D3DFMT_D16:		return 16;
	case D3DFMT_D15S1:		return 15;
	case D3DFMT_D24X8:
	case D3DFMT_D24S8:
	case D3DFMT_D24X4S4:	return 24;
	case D3DFMT_D32:		return 32;
	default:				return 0;
	}
}

static D3DFORMAT ConvertToAlphaFormat( D3DFORMAT fmt )
{
	if( fmt == D3DFMT_X8R8G8B8 )
		fmt = D3DFMT_A8R8G8B8;
	else if( fmt == D3DFMT_X4R4G4B4 )
		fmt = D3DFMT_A4R4G4B4;
	else if( fmt == D3DFMT_X1R5G5B5 )
		fmt = D3DFMT_A1R5G5B5;
	return fmt;
}

// -----------------------------------------------------------------------------


static UInt32 buildVertexProcessings( const D3DCAPS9& caps )
{
	UInt32 result =  0;

	// TODO: check vertex shader version

	DWORD devCaps = caps.DevCaps;
	if( devCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) {
		if( devCaps & D3DDEVCAPS_PUREDEVICE ) {
			result |= (1<<kVPPureHardware);
		}
		result |= (1<<kVPHardware);
		result |= (1<<kVPMixed);
	}

	result |= (1<<kVPSoftware);

	return result;
}


static void buildDepthStencilFormats( IDirect3D9& d3d, D3DDeviceCombo& devCombo )
{
	const D3DFORMAT dsFormats[] = {
		D3DFMT_D24S8, D3DFMT_D24X8, D3DFMT_D24X4S4, D3DFMT_D16, D3DFMT_D15S1, D3DFMT_D32,
	};
	const int dsFormatCount = sizeof(dsFormats) / sizeof(dsFormats[0]);
	
	for( int idsf = 0; idsf < dsFormatCount; ++idsf ) {
		D3DFORMAT format = dsFormats[idsf];
		if( SUCCEEDED( d3d.CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, devCombo.adapterFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, format ) ) )
		{
			if( SUCCEEDED( d3d.CheckDepthStencilMatch( g_D3DAdapter, g_D3DDevType, devCombo.adapterFormat, devCombo.backBufferFormat, format ) ) )
			{
				devCombo.depthStencilFormats.push_back( format );
			}
		}
	}
}


static void buildMultiSampleTypes( IDirect3D9& d3d, D3DDeviceCombo& devCombo )
{
	const size_t kMaxSamples = 16;
	devCombo.multiSampleTypes.reserve( kMaxSamples );
	devCombo.multiSampleTypes.push_back( D3DMULTISAMPLE_NONE );

	for( int samples = 2; samples <= kMaxSamples; ++samples ) {
		D3DMULTISAMPLE_TYPE	msType = GetD3DMultiSampleType( samples );
		DWORD msQuality;
		if( SUCCEEDED( d3d.CheckDeviceMultiSampleType( g_D3DAdapter, g_D3DDevType, devCombo.backBufferFormat, devCombo.isWindowed, msType, NULL ) ) )
			devCombo.multiSampleTypes.push_back( samples );
	}
}


static void buildConflicts( IDirect3D9& d3d, D3DDeviceCombo& devCombo )
{
	for( size_t ids = 0; ids < devCombo.depthStencilFormats.size(); ++ids ) {
		D3DFORMAT format = (D3DFORMAT)devCombo.depthStencilFormats[ids];
		for( size_t ims = 0; ims < devCombo.multiSampleTypes.size(); ++ims ) {
			D3DMULTISAMPLE_TYPE msType = (D3DMULTISAMPLE_TYPE)devCombo.multiSampleTypes[ims];
			if( FAILED( d3d.CheckDeviceMultiSampleType(
				g_D3DAdapter, g_D3DDevType,
				format, devCombo.isWindowed, msType, NULL ) ) )
			{
				D3DDeviceCombo::MultiSampleConflict conflict;
				conflict.format = format;
				conflict.type = msType;
				devCombo.conflicts.push_back( conflict );
			}
		}
	}
}


static bool enumerateDeviceCombos( IDirect3D9& d3d, const D3DCAPS9& caps, const DwordVector& adapterFormats, D3DDeviceComboVector& outCombos )
{
	const D3DFORMAT bbufferFormats[] = {
		D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_A2R10G10B10,
		D3DFMT_R5G6B5, D3DFMT_A1R5G5B5, D3DFMT_X1R5G5B5
	};
	const int bbufferFormatCount = sizeof(bbufferFormats) / sizeof(bbufferFormats[0]);

	bool isWindowedArray[] = { false, true };

	// see which adapter formats are supported by this device
	for( size_t iaf = 0; iaf < adapterFormats.size(); ++iaf )
	{
		D3DFORMAT format = (D3DFORMAT)adapterFormats[iaf];
		for( int ibbf = 0; ibbf < bbufferFormatCount; ibbf++ )
		{
			D3DFORMAT bbufferFormat = bbufferFormats[ibbf];
			if( GetFormatAlphaBits(bbufferFormat) < kMinAlphaBits )
				continue;
			for( int iiw = 0; iiw < 2; ++iiw ) {
				bool isWindowed = isWindowedArray[iiw];
				if( FAILED( d3d.CheckDeviceType( g_D3DAdapter, g_D3DDevType, format, bbufferFormat, isWindowed ) ) )
					continue;

				// Here, we have an adapter format / backbuffer format/ windowed
				// combo that is supported by the system. We still need to find one or
				// more suitable depth/stencil buffer format, multisample type,
				// vertex processing type, and vsync.
				D3DDeviceCombo devCombo;

				devCombo.adapterFormat = format;
				devCombo.backBufferFormat = bbufferFormat;
				devCombo.isWindowed = isWindowed;
				devCombo.presentationIntervals = caps.PresentationIntervals;

				buildDepthStencilFormats( d3d, devCombo );
				if( devCombo.depthStencilFormats.empty() )
					continue;

				buildMultiSampleTypes( d3d, devCombo );
				if( devCombo.multiSampleTypes.empty() )
					continue;

				buildConflicts( d3d, devCombo );

				outCombos.push_back( devCombo );
			}
		}
	}

	return !outCombos.empty();
}


bool D3D9FormatCaps::Enumerate( IDirect3D9& d3d )
{
	AssertIf( !m_Combos.empty() );
	HRESULT hr;

	const D3DFORMAT allowedFormats[] = {
		D3DFMT_X8R8G8B8, D3DFMT_X1R5G5B5, D3DFMT_R5G6B5, D3DFMT_A2R10G10B10
	};
	const int allowedFormatCount = sizeof(allowedFormats) / sizeof(allowedFormats[0]);

	m_AdapterFormatForChecks = D3DFMT_UNKNOWN;

	// build a list of all display adapter formats
	DwordVector adapterFormatList; // D3DFORMAT

	for( size_t ifmt = 0; ifmt < allowedFormatCount; ++ifmt )
	{
		D3DFORMAT format = allowedFormats[ifmt];
		int modeCount = d3d.GetAdapterModeCount( g_D3DAdapter, format );
		for( int mode = 0; mode < modeCount; ++mode ) {
			D3DDISPLAYMODE dm;
			d3d.EnumAdapterModes( g_D3DAdapter, format, mode, &dm );
			if( dm.Width < (UINT)kMinDisplayWidth || dm.Height < (UINT)kMinDisplayHeight || GetFormatColorBits(dm.Format) < kMinColorBits )
				continue;
			// adapterInfo->displayModes.push_back( dm );
			if( std::find(adapterFormatList.begin(),adapterFormatList.end(),dm.Format) == adapterFormatList.end() ) {
				adapterFormatList.push_back( dm.Format );
				if( m_AdapterFormatForChecks == D3DFMT_UNKNOWN )
					m_AdapterFormatForChecks = format;
			}
		}
	}

	if( m_AdapterFormatForChecks == D3DFMT_UNKNOWN ) // for some reason no format was selected for checks, use default
		m_AdapterFormatForChecks = allowedFormats[0];

	// get info for device on this adapter
	D3DCAPS9 caps;
	if( FAILED( d3d.GetDeviceCaps( g_D3DAdapter, g_D3DDevType, &caps ) ) )
		return false;

	// find suitable vertex processing modes (if any)
	m_VertexProcessings = buildVertexProcessings( caps );
	AssertIf( !m_VertexProcessings );

	// get info for each device combo on this device
	if( !enumerateDeviceCombos( d3d, caps, adapterFormatList, m_Combos ) )
		return false;

	return true;
}


void D3D9FormatCaps::FindBestPresentationParams( int width, int height, D3DFORMAT desktopMode, bool windowed, int vBlankCount, int multiSample, D3DPRESENT_PARAMETERS& outParams ) const
{
	const D3DDeviceCombo* bestCombo = NULL;
	int bestScore = -1;

	for( size_t idc = 0; idc < m_Combos.size(); ++idc )
	{
		const D3DDeviceCombo& devCombo = m_Combos[idc];
		if( windowed && !devCombo.isWindowed )
			continue;
		if( !windowed && devCombo.isWindowed )
			continue;
		if( windowed )
		{
			if( devCombo.adapterFormat != desktopMode )
				continue;
		}

		int score = 0;

		bool matchesBB = (devCombo.backBufferFormat == ConvertToAlphaFormat(devCombo.adapterFormat));
		bool matchesDesktop = (devCombo.adapterFormat == desktopMode);

		if( matchesBB )
			score += 1;
		if( matchesDesktop )
			score += 1;
		if( GetFormatAlphaBits(devCombo.backBufferFormat) > 0 )
			score += 1;

		if( score > bestScore )
		{
			bestScore = score;
			bestCombo = &devCombo;
		}
	}

	if( !bestCombo )
	{
		// This can happen if we're debugging force-16BPP modes on a 32BPP desktop, and so on
		outParams.BackBufferFormat = desktopMode;
		outParams.AutoDepthStencilFormat = D3DFMT_D16;
		outParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
		outParams.MultiSampleType = D3DMULTISAMPLE_NONE;
		outParams.MultiSampleQuality = 0;
		return;
	}

	outParams.BackBufferFormat = bestCombo->backBufferFormat;
	outParams.AutoDepthStencilFormat = (D3DFORMAT)bestCombo->depthStencilFormats[0];

	// No support for intervals above 1 in windowed mode (case 497116)
	if (windowed && vBlankCount > 1)
		vBlankCount = 1;

	// best possible vsync parameter (if device doesn't support 2 fall back to 1)
	DWORD intervals = bestCombo->presentationIntervals;
	outParams.PresentationInterval = ( vBlankCount >= 2 ) && ( intervals & D3DPRESENT_INTERVAL_TWO ) ? D3DPRESENT_INTERVAL_TWO :
									 ( vBlankCount >= 1 ) && ( intervals & D3DPRESENT_INTERVAL_ONE ) ? D3DPRESENT_INTERVAL_ONE :
									 ( vBlankCount == 0 ) && ( intervals & D3DPRESENT_INTERVAL_IMMEDIATE ) ? D3DPRESENT_INTERVAL_IMMEDIATE :
									 D3DPRESENT_INTERVAL_DEFAULT;

	// Here we already know backbuffer, depth buffer formats and so on, so we can also clamp used FSAA to sane VRAM limits.
	int backbufferBPP = GetBPPFromD3DFormat(outParams.BackBufferFormat)/8;
	int frontbufferBPP = GetBPPFromD3DFormat(desktopMode)/8;
	int depthBPP = GetBPPFromD3DFormat(outParams.AutoDepthStencilFormat)/8;
	multiSample = ChooseSuitableFSAALevel( width, height, backbufferBPP, frontbufferBPP, depthBPP, multiSample );

	// Find out best matched multi sample type.
	int msIdx = 0;
	if( multiSample > 1 )
	{
		while( msIdx < bestCombo->multiSampleTypes.size() && bestCombo->multiSampleTypes[msIdx] <= multiSample )
			++msIdx;
		--msIdx;
		AssertIf( msIdx < 0 );
	}
	outParams.MultiSampleType = GetD3DMultiSampleType(bestCombo->multiSampleTypes[msIdx]);
	outParams.MultiSampleQuality = 0;
}