summaryrefslogtreecommitdiff
path: root/Runtime/GfxDevice/d3d/GraphicsCapsD3D9.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/GfxDevice/d3d/GraphicsCapsD3D9.cpp')
-rw-r--r--Runtime/GfxDevice/d3d/GraphicsCapsD3D9.cpp384
1 files changed, 384 insertions, 0 deletions
diff --git a/Runtime/GfxDevice/d3d/GraphicsCapsD3D9.cpp b/Runtime/GfxDevice/d3d/GraphicsCapsD3D9.cpp
new file mode 100644
index 0000000..f1d95c8
--- /dev/null
+++ b/Runtime/GfxDevice/d3d/GraphicsCapsD3D9.cpp
@@ -0,0 +1,384 @@
+#include "UnityPrefix.h"
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "D3D9Context.h"
+#include "Runtime/Utilities/Utility.h"
+#include "PlatformDependent/Win/WinDriverUtils.h"
+#include "D3D9Utils.h"
+#include <Shlwapi.h>
+
+#define CAPS_DEBUG_DISABLE_RT 0
+
+
+extern D3DFORMAT kD3D9RenderTextureFormats[kRTFormatCount];
+
+
+extern D3DDEVTYPE g_D3DDevType;
+extern DWORD g_D3DAdapter;
+
+static bool IsTextureFormatSupported( D3DFORMAT format )
+{
+ if( format == D3DFMT_UNKNOWN )
+ return false;
+ HRESULT hr = GetD3DObject()->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), 0, D3DRTYPE_TEXTURE, format );
+ return SUCCEEDED( hr );
+}
+static bool IsSRGBTextureReadSupported( D3DFORMAT format )
+{
+ if( format == D3DFMT_UNKNOWN )
+ return false;
+ HRESULT hr = GetD3DObject()->CheckDeviceFormat (g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_QUERY_SRGBREAD, D3DRTYPE_TEXTURE, format);
+ return SUCCEEDED( hr );
+}
+static bool IsSRGBTextureWriteSupported( D3DFORMAT format )
+{
+ if( format == D3DFMT_UNKNOWN )
+ return false;
+ HRESULT hr = GetD3DObject()->CheckDeviceFormat (g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_QUERY_SRGBWRITE, D3DRTYPE_TEXTURE, format);
+ return SUCCEEDED( hr );
+}
+static bool IsRenderTextureFormatSupported( D3DFORMAT format )
+{
+ if( format == D3DFMT_UNKNOWN )
+ return false;
+ HRESULT hr = GetD3DObject()->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, format );
+ return SUCCEEDED( hr );
+}
+
+D3DFORMAT GetD3D9TextureFormat( TextureFormat inFormat ); // TexturesD3D9.cpp
+
+
+enum {
+ kVendorDummyRef = 0x0000,
+ kVendor3DLabs = 0x3d3d,
+ kVendorMatrox = 0x102b,
+ kVendorS3 = 0x5333,
+ kVendorSIS = 0x1039,
+ kVendorXGI = 0x18ca,
+ kVendorIntel = 0x8086,
+ kVendorATI = 0x1002,
+ kVendorNVIDIA = 0x10de,
+ kVendorTrident = 0x1023,
+ kVendorImgTech = 0x104a,
+ kVendorVIAS3G = 0x1106,
+ kVendor3dfx = 0x121a,
+ kVendorParallels= 0x1ab8,
+ kVendorMicrosoft= 0x1414,
+ kVendorVMWare = 0x15ad,
+};
+struct KnownVendors {
+ DWORD vendorId;
+ const char* name;
+};
+static KnownVendors s_KnownVendors[] = {
+ { kVendorDummyRef, "REFERENCE" },
+ { kVendor3DLabs, "3dLabs" },
+ { kVendorMatrox, "Matrox" },
+ { kVendorS3, "S3" },
+ { kVendorSIS, "SIS" },
+ { kVendorXGI, "XGI" },
+ { kVendorIntel, "Intel" },
+ { kVendorATI, "ATI" },
+ { kVendorNVIDIA, "NVIDIA" },
+ { kVendorTrident, "Trident" },
+ { kVendorImgTech, "Imagination Technologies" },
+ { kVendorVIAS3G, "VIA/S3" },
+ { kVendor3dfx, "3dfx" },
+ { kVendorParallels, "Parallels" },
+ { kVendorMicrosoft, "Microsoft" },
+ { kVendorVMWare, "VMWare" },
+};
+static int kKnownVendorsSize = sizeof(s_KnownVendors)/sizeof(s_KnownVendors[0]);
+
+
+void GraphicsCaps::InitD3D9()
+{
+ IDirect3D9* d3dobject = GetD3DObject();
+ d3dobject->GetDeviceCaps( g_D3DAdapter, g_D3DDevType, &d3d.d3dcaps );
+
+ // get renderer, vendor & driver information
+ D3DADAPTER_IDENTIFIER9 adapterInfo;
+ d3dobject->GetAdapterIdentifier( g_D3DAdapter, 0, &adapterInfo );
+ adapterInfo.Driver[MAX_DEVICE_IDENTIFIER_STRING-1] = 0;
+ adapterInfo.Description[MAX_DEVICE_IDENTIFIER_STRING-1] = 0;
+ adapterInfo.DeviceName[31] = 0;
+ rendererString = adapterInfo.Description;
+
+ if (g_D3DDevType == D3DDEVTYPE_REF)
+ {
+ adapterInfo.VendorId = kVendorDummyRef;
+ rendererString = "REF on " + rendererString;
+ }
+
+ int i;
+ for( i = 0; i < kKnownVendorsSize; ++i )
+ {
+ if( s_KnownVendors[i].vendorId == adapterInfo.VendorId )
+ {
+ vendorString = s_KnownVendors[i].name;
+ break;
+ }
+ }
+ if( i == kKnownVendorsSize )
+ {
+ vendorString = Format( "Unknown (ID=%x)", adapterInfo.VendorId );
+ }
+ windriverutils::VersionInfo driverVersion( HIWORD(adapterInfo.DriverVersion.HighPart), LOWORD(adapterInfo.DriverVersion.HighPart),
+ HIWORD(adapterInfo.DriverVersion.LowPart), LOWORD(adapterInfo.DriverVersion.LowPart) );
+ driverVersionString = Format( "%s %i.%i.%i.%i", adapterInfo.Driver,
+ HIWORD(adapterInfo.DriverVersion.HighPart), LOWORD(adapterInfo.DriverVersion.HighPart),
+ HIWORD(adapterInfo.DriverVersion.LowPart), LOWORD(adapterInfo.DriverVersion.LowPart) );
+ driverLibraryString = driverVersionString;
+ fixedVersionString = "Direct3D 9.0c [" + driverVersionString + ']';
+
+ rendererID = adapterInfo.DeviceId;
+ vendorID = adapterInfo.VendorId;
+
+ // We can't use GetAvailableTextureMem here because the device is not created yet!
+ // And besides that, it would return much more than VRAM on Vista (virtualization and so on).
+ // Use WMI instead.
+ int vramMB;
+ const char* vramMethod = "";
+ if (g_D3DDevType != D3DDEVTYPE_REF)
+ vramMB = windriverutils::GetVideoMemorySizeMB (d3dobject->GetAdapterMonitor(g_D3DAdapter), &vramMethod);
+ else
+ vramMB = 128;
+ videoMemoryMB = vramMB;
+
+ // On windows, we always output D3D info. There is so much variety that it always helps!
+ printf_console( "Direct3D:\n" );
+ printf_console( " Version: %s\n", fixedVersionString.c_str() );
+ printf_console( " Renderer: %s\n", rendererString.c_str() );
+ printf_console( " Vendor: %s\n", vendorString.c_str() );
+ printf_console( " VRAM: %i MB (via %s)\n", (int)videoMemoryMB, vramMethod );
+
+ maxVSyncInterval = 0;
+ if( d3d.d3dcaps.PresentationIntervals & D3DPRESENT_INTERVAL_ONE )
+ {
+ maxVSyncInterval = 1;
+ if( d3d.d3dcaps.PresentationIntervals & D3DPRESENT_INTERVAL_TWO )
+ maxVSyncInterval = 2;
+ }
+
+ DWORD declTypesFloat16 = D3DDTCAPS_FLOAT16_2 | D3DDTCAPS_FLOAT16_4;
+ has16BitFloatVertex = (d3d.d3dcaps.DeclTypes & declTypesFloat16) == declTypesFloat16;
+ needsToSwizzleVertexColors = true;
+
+ bool usesSoftwareVP = !(d3d.d3dcaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT);
+ if( usesSoftwareVP )
+ maxLights = 8; // software T&L always has 8 lights
+ else
+ maxLights = clamp<unsigned int>( d3d.d3dcaps.MaxActiveLights, 0, 8 );
+
+ // Texture sizes
+ maxTextureSize = std::min( d3d.d3dcaps.MaxTextureWidth, d3d.d3dcaps.MaxTextureHeight );
+ maxRenderTextureSize = maxTextureSize;
+ maxCubeMapSize = maxTextureSize;
+
+ has3DTexture = d3d.d3dcaps.TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP;
+ maxTexUnits = d3d.d3dcaps.MaxSimultaneousTextures;
+ maxTexImageUnits = 16;
+ maxTexCoords = d3d.d3dcaps.MaxSimultaneousTextures;
+ if (maxTexCoords > 8)
+ maxTexCoords = 8;
+
+ // In theory, vertex texturing is texture format dependent. However, in practice the caps lie,
+ // especially on NVIDIA hardware.
+ //
+ // ATI cards: all DX10+ GPUs report all texture formats as vertex texture capable (good!)
+ // Intel cards: all SM3.0+ GPUs report all texture formats as vertex texture capable (good!)
+ // NV cards: all DX10+ GPUs report only floating point formats as capable, but all others actually work as well.
+ // GeForce 6&7 only report R32F and A32R32G32B32F, and only those work.
+ //
+ // So we check for R16F support; this will return true on all GPUs that can handle ALL
+ // texture formats.
+ hasVertexTextures = ((LOWORD(d3d.d3dcaps.VertexShaderVersion) >= (3<<8)+0)) &&
+ SUCCEEDED(d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_QUERY_VERTEXTEXTURE, D3DRTYPE_TEXTURE, D3DFMT_R16F));
+
+ hasAnisoFilter = d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY;
+ maxAnisoLevel = hasAnisoFilter ? d3d.d3dcaps.MaxAnisotropy : 1;
+ hasMipLevelBias = d3d.d3dcaps.RasterCaps & D3DPRASTERCAPS_MIPMAPLODBIAS;
+
+ for( i = 0; i < kTexFormatPCCount; ++i )
+ {
+ d3d.hasBaseTextureFormat[i] = IsTextureFormatSupported( GetD3D9TextureFormat( static_cast<TextureFormat>(i) ) );
+ supportsTextureFormat[i] = d3d.hasBaseTextureFormat[i];
+ }
+
+ hasS3TCCompression = IsTextureFormatSupported(D3DFMT_DXT1) && IsTextureFormatSupported(D3DFMT_DXT3) && IsTextureFormatSupported(D3DFMT_DXT5);
+ d3d.hasTextureFormatA8 = IsTextureFormatSupported(D3DFMT_A8);
+ d3d.hasTextureFormatL8 = IsTextureFormatSupported(D3DFMT_L8);
+ d3d.hasTextureFormatA8L8 = IsTextureFormatSupported(D3DFMT_A8L8);
+ d3d.hasTextureFormatL16 = IsTextureFormatSupported(D3DFMT_L16);
+
+ if (!(d3d.d3dcaps.TextureCaps & D3DPTEXTURECAPS_POW2))
+ npot = kNPOTFull;
+ else if (d3d.d3dcaps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL)
+ npot = kNPOTRestricted;
+ else
+ npot = kNPOTNone;
+
+ npotRT = npot;
+
+ hasSRGBReadWrite =
+ IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatRGB24)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatRGBA32)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatARGB32)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatBGR24)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatDXT1)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatDXT3)))
+ && IsSRGBTextureReadSupported(GetD3D9TextureFormat(static_cast<TextureFormat>(kTexFormatDXT5)));
+
+ // we only do sRGB writes to an 8 bit buffer ...
+ hasSRGBReadWrite = hasSRGBReadWrite && IsSRGBTextureWriteSupported(D3DFMT_A8R8G8B8);
+
+ hasInstancing = false; //@TODO: instancing!
+
+ hasBlendSquare = (d3d.d3dcaps.SrcBlendCaps & D3DPBLENDCAPS_SRCCOLOR) && (d3d.d3dcaps.DestBlendCaps & D3DPBLENDCAPS_DESTCOLOR);
+ hasSeparateAlphaBlend = d3d.d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_SEPARATEALPHABLEND;
+ hasBlendSub = hasBlendMinMax = d3d.d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_BLENDOP;
+
+ hasAutoMipMapGeneration = d3d.d3dcaps.Caps2 & D3DCAPS2_CANAUTOGENMIPMAP;
+
+ for (int i = 0; i < kRTFormatCount; ++i)
+ {
+ if (i == kRTFormatDefault || i == kRTFormatDefaultHDR || i == kRTFormatShadowMap)
+ continue;
+ supportsRenderTextureFormat[i] = IsRenderTextureFormatSupported(kD3D9RenderTextureFormats[i]);
+ }
+ hasRenderToTexture = supportsRenderTextureFormat[kRTFormatARGB32];
+ supportsRenderTextureFormat[kRTFormatDefault] = hasRenderToTexture;
+
+ hasRenderToCubemap = hasRenderToTexture;
+ hasStencil = true;
+ hasRenderTargetStencil = true;
+ hasTwoSidedStencil = d3d.d3dcaps.StencilCaps & D3DSTENCILCAPS_TWOSIDED;
+ maxMRTs = clamp<int> (d3d.d3dcaps.NumSimultaneousRTs, 1, kMaxSupportedRenderTargets);
+ if (!(d3d.d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING))
+ maxMRTs = 1;
+
+ d3d.hasATIDepthFormat16 = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, kD3D9FormatDF16 ) );
+ supportsRenderTextureFormat[kRTFormatDepth] |= d3d.hasATIDepthFormat16;
+ d3d.hasNVDepthFormatINTZ = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, kD3D9FormatINTZ ) );
+ supportsRenderTextureFormat[kRTFormatDepth] |= d3d.hasNVDepthFormatINTZ;
+ d3d.hasNVDepthFormatRAWZ = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, kD3D9FormatRAWZ ) );
+ d3d.hasNULLFormat = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, kD3D9FormatNULL ) );
+ d3d.hasDepthResolveRESZ = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, kD3D9FormatRESZ ) );
+
+ hasNativeDepthTexture = d3d.hasATIDepthFormat16 || d3d.hasNVDepthFormatINTZ;
+ hasStencilInDepthTexture = d3d.hasNVDepthFormatINTZ;
+ hasNativeShadowMap = SUCCEEDED( d3dobject->CheckDeviceFormat( g_D3DAdapter, g_D3DDevType, GetD3DFormatForChecks(), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE, D3DFMT_D16 ) );
+ supportsRenderTextureFormat[kRTFormatShadowMap] = hasRenderToTexture && hasNativeShadowMap;
+
+ #if CAPS_DEBUG_DISABLE_RT
+ hasRenderToTexture = hasRenderToCubemap = false;
+ for (int i = 0; i < kRTFormatCount; ++i)
+ supportsRenderTextureFormat[i] = false;
+ maxMRTs = 1;
+ #endif
+
+ // This is somewhat dummy; actual resolving of FSAA levels and types supported happens later when choosing presentation parameters.
+ hasMultiSample = true;
+
+ // Driver bugs/workarounds following
+ DetectDriverBugsD3D9( adapterInfo.VendorId, driverVersion );
+
+ // safeguards
+ maxRenderTextureSize = std::min( maxRenderTextureSize, maxTextureSize );
+ maxCubeMapSize = std::min( maxCubeMapSize, maxTextureSize );
+
+ // in the very end, figure out shader capabilities level (after all workarounds are applied)
+ if( LOWORD(d3d.d3dcaps.PixelShaderVersion) < (3<<8)+0 )
+ {
+ // no ps3.0: 2.x shaders
+ shaderCaps = kShaderLevel2;
+ }
+ else
+ {
+ // has everything we care about!
+ shaderCaps = kShaderLevel3;
+ }
+
+ // Print overall caps & D3D9 hacks used
+ printf_console( " Caps: Shader=%i DepthRT=%i NativeDepth=%i NativeShadow=%i DF16=%i INTZ=%i RAWZ=%i NULL=%i RESZ=%i SlowINTZ=%i\n",
+ shaderCaps,
+ supportsRenderTextureFormat[kRTFormatDepth], hasNativeDepthTexture, hasNativeShadowMap,
+ d3d.hasATIDepthFormat16,
+ d3d.hasNVDepthFormatINTZ, d3d.hasNVDepthFormatRAWZ,
+ d3d.hasNULLFormat, d3d.hasDepthResolveRESZ,
+ d3d.slowINTZSampling
+ );
+}
+
+
+enum WindowsVersion {
+ kWindows2000 = 50, // 5.0
+ kWindowsXP = 51, // 5.1
+ kWindows2003 = 52, // 5.2
+ kWindowsVista = 60, // 6.0
+};
+
+static int GetWindowsVersion()
+{
+ OSVERSIONINFO osinfo;
+ osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if( !GetVersionEx(&osinfo) )
+ return 0;
+
+ if( osinfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
+ return osinfo.dwMajorVersion * 10 + osinfo.dwMinorVersion % 10;
+ else
+ return 0;
+}
+
+
+void GraphicsCaps::DetectDriverBugsD3D9( UInt32 vendorCode, const windriverutils::VersionInfo& driverVersion )
+{
+ d3d.slowINTZSampling = false;
+
+
+ if( vendorCode == kVendorNVIDIA )
+ {
+ // GeForceFX and earlier have sort-of-buggy render to cubemap. E.g. skybox draws correctly,
+ // but objects do not appear. Huh.
+ const int kShaderVersion30 = (3 << 8) + 0;
+ bool isFXOrEarlier = LOWORD(gGraphicsCaps.d3d.d3dcaps.PixelShaderVersion) < kShaderVersion30;
+ if( isFXOrEarlier )
+ {
+ printf_console( "D3D: disabling render to cubemap on pre-GeForce6\n" );
+ buggyCameraRenderToCubemap = true;
+ }
+
+ // Also, native shadow maps seem to have problems on GeForce FX; perhaps it needs to use tex2Dproj instead of tex2D,
+ // or something (FX 5200). Since FX cards are really dying, and the only left ones are FX 5200/5500,
+ // let's just turn shadows off. You don't want them on those cards anyway!
+ if (isFXOrEarlier)
+ {
+ printf_console ("D3D: disabling shadows on pre-GeForce6\n");
+ hasNativeShadowMap = false;
+ hasNativeDepthTexture = false;
+ supportsRenderTextureFormat[kRTFormatDepth] = false;
+ }
+
+ // GeForceFX on 6.14.10.9147 drivers has buggy fullscreen FSAA.
+ // It displays everything stretched, as if AA samples map to pixels directly.
+ if( isFXOrEarlier && driverVersion <= windriverutils::VersionInfo(6,14,10,9147) )
+ {
+ printf_console( "D3D: disabling fullscreen AA (buggy pre-GeForce6 driver)\n" );
+ buggyFullscreenFSAA = true;
+ }
+ }
+ if( vendorCode == kVendorATI )
+ {
+ // On D3D9 Radeon HD cards have big performance hit when using INTZ texture for both sampling & depth testing
+ // (Radeon HD 3xxx-5xxx, Catalyst 9.10 to 10.5). Talking with AMD, we found that using RESZ to copy it into a separate
+ // texture is a decent workaround that results in ok performance.
+ if (d3d.hasDepthResolveRESZ)
+ d3d.slowINTZSampling = true;
+ }
+
+ // Sanitize VRAM amount
+ if( videoMemoryMB < 32 ) {
+ printf_console("D3D: VRAM amount suspiciously low (less than 32MB)\n");
+ videoMemoryMB = 32;
+ }
+}