#include "d3d9_adapter.h" #include "d3d9_interface.h" #include "d3d9_monitor.h" #include "d3d9_caps.h" #include "d3d9_util.h" #include "../util/util_bit.h" #include "../util/util_luid.h" #include "../util/util_ratio.h" #include namespace dxvk { const char* GetDriverDLL(DxvkGpuVendor vendor) { switch (vendor) { default: case DxvkGpuVendor::Nvidia: return "nvd3dum.dll"; #if defined(__x86_64__) || defined(_M_X64) case DxvkGpuVendor::Amd: return "aticfx64.dll"; case DxvkGpuVendor::Intel: return "igdumd64.dll"; #else case DxvkGpuVendor::Amd: return "aticfx32.dll"; case DxvkGpuVendor::Intel: return "igdumd32.dll"; #endif } } D3D9Adapter::D3D9Adapter( D3D9InterfaceEx* pParent, Rc Adapter, UINT Ordinal, UINT DisplayIndex) : m_parent (pParent), m_adapter (Adapter), m_ordinal (Ordinal), m_displayIndex (DisplayIndex), m_modeCacheFormat (D3D9Format::Unknown), m_d3d9Formats (Adapter, m_parent->GetOptions()) { m_adapter->logAdapterInfo(); } template static void copyToStringArray(char (&dst)[N], const char* src) { dxvk::str::strlcpy(dst, src, N); } HRESULT D3D9Adapter::GetAdapterIdentifier( DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier) { if (unlikely(pIdentifier == nullptr)) return D3DERR_INVALIDCALL; auto& options = m_parent->GetOptions(); const auto& props = m_adapter->deviceProperties(); DISPLAY_DEVICEA device = { }; device.cb = sizeof(device); if (!::EnumDisplayDevicesA(nullptr, m_displayIndex, &device, 0)) { Logger::err("D3D9Adapter::GetAdapterIdentifier: Failed to query display info"); return D3DERR_INVALIDCALL; } GUID guid = bit::cast(m_adapter->devicePropertiesExt().vk11.deviceUUID); uint32_t vendorId = options.customVendorId == -1 ? props.vendorID : uint32_t(options.customVendorId); uint32_t deviceId = options.customDeviceId == -1 ? props.deviceID : uint32_t(options.customDeviceId); const char* desc = options.customDeviceDesc.empty() ? props.deviceName : options.customDeviceDesc.c_str(); const char* driver = GetDriverDLL(DxvkGpuVendor(vendorId)); copyToStringArray(pIdentifier->Description, desc); copyToStringArray(pIdentifier->DeviceName, device.DeviceName); // The GDI device name. Not the actual device name. copyToStringArray(pIdentifier->Driver, driver); // This is the driver's dll. pIdentifier->DeviceIdentifier = guid; pIdentifier->DeviceId = deviceId; pIdentifier->VendorId = vendorId; pIdentifier->Revision = 0; pIdentifier->SubSysId = 0; pIdentifier->WHQLLevel = m_parent->IsExtended() ? 1 : 0; // This doesn't check with the driver on Direct3D9Ex and is always 1. pIdentifier->DriverVersion.QuadPart = INT64_MAX; return D3D_OK; } HRESULT D3D9Adapter::CheckDeviceType( D3DDEVTYPE DevType, D3D9Format AdapterFormat, D3D9Format BackBufferFormat, BOOL bWindowed) { if (!IsSupportedAdapterFormat(AdapterFormat)) return D3DERR_NOTAVAILABLE; if (!IsSupportedBackBufferFormat(AdapterFormat, BackBufferFormat, bWindowed)) return D3DERR_NOTAVAILABLE; return D3D_OK; } HRESULT D3D9Adapter::CheckDeviceFormat( D3DDEVTYPE DeviceType, D3D9Format AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3D9Format CheckFormat) { if (!IsSupportedAdapterFormat(AdapterFormat)) return D3DERR_NOTAVAILABLE; const bool dmap = Usage & D3DUSAGE_DMAP; const bool rt = Usage & D3DUSAGE_RENDERTARGET; const bool ds = Usage & D3DUSAGE_DEPTHSTENCIL; const bool surface = RType == D3DRTYPE_SURFACE; const bool texture = RType == D3DRTYPE_TEXTURE; const bool twoDimensional = surface || texture; const bool srgb = (Usage & (D3DUSAGE_QUERY_SRGBREAD | D3DUSAGE_QUERY_SRGBWRITE)) != 0; if (CheckFormat == D3D9Format::INST) return D3D_OK; if (rt && CheckFormat == D3D9Format::A8 && m_parent->GetOptions().disableA8RT) return D3DERR_NOTAVAILABLE; if (ds && !IsDepthFormat(CheckFormat)) return D3DERR_NOTAVAILABLE; if (rt && CheckFormat == D3D9Format::NULL_FORMAT && twoDimensional) return D3D_OK; if (rt && CheckFormat == D3D9Format::RESZ && surface) return D3D_OK; if (CheckFormat == D3D9Format::ATOC && surface) return D3D_OK; if (CheckFormat == D3D9Format::NVDB && surface) return m_adapter->features().core.features.depthBounds ? D3D_OK : D3DERR_NOTAVAILABLE; // I really don't want to support this... if (dmap) return D3DERR_NOTAVAILABLE; auto mapping = m_d3d9Formats.GetFormatMapping(CheckFormat); if (mapping.FormatColor == VK_FORMAT_UNDEFINED) return D3DERR_NOTAVAILABLE; if (mapping.FormatSrgb == VK_FORMAT_UNDEFINED && srgb) return D3DERR_NOTAVAILABLE; if (RType == D3DRTYPE_VERTEXBUFFER || RType == D3DRTYPE_INDEXBUFFER) return D3D_OK; // Let's actually ask Vulkan now that we got some quirks out the way! return CheckDeviceVkFormat(mapping.FormatColor, Usage, RType); } HRESULT D3D9Adapter::CheckDeviceMultiSampleType( D3DDEVTYPE DeviceType, D3D9Format SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels) { if (pQualityLevels != nullptr) *pQualityLevels = 1; auto dst = ConvertFormatUnfixed(SurfaceFormat); if (dst.FormatColor == VK_FORMAT_UNDEFINED) return D3DERR_NOTAVAILABLE; if (MultiSampleType != D3DMULTISAMPLE_NONE && (SurfaceFormat == D3D9Format::D32_LOCKABLE || SurfaceFormat == D3D9Format::D32F_LOCKABLE || SurfaceFormat == D3D9Format::D16_LOCKABLE)) return D3DERR_NOTAVAILABLE; uint32_t sampleCount = std::max(MultiSampleType, 1u); // Check if this is a power of two... if (sampleCount & (sampleCount - 1)) return D3DERR_NOTAVAILABLE; // Therefore... VkSampleCountFlags sampleFlags = VkSampleCountFlags(sampleCount); auto availableFlags = m_adapter->deviceProperties().limits.framebufferColorSampleCounts & m_adapter->deviceProperties().limits.framebufferDepthSampleCounts; if (!(availableFlags & sampleFlags)) return D3DERR_NOTAVAILABLE; if (pQualityLevels != nullptr) { if (MultiSampleType == D3DMULTISAMPLE_NONMASKABLE) *pQualityLevels = 32 - bit::lzcnt(availableFlags); else *pQualityLevels = 1; } return D3D_OK; } HRESULT D3D9Adapter::CheckDepthStencilMatch( D3DDEVTYPE DeviceType, D3D9Format AdapterFormat, D3D9Format RenderTargetFormat, D3D9Format DepthStencilFormat) { if (!IsDepthFormat(DepthStencilFormat)) return D3DERR_NOTAVAILABLE; if (RenderTargetFormat == dxvk::D3D9Format::NULL_FORMAT) return D3D_OK; auto mapping = ConvertFormatUnfixed(RenderTargetFormat); if (mapping.FormatColor == VK_FORMAT_UNDEFINED) return D3DERR_NOTAVAILABLE; return D3D_OK; } HRESULT D3D9Adapter::CheckDeviceFormatConversion( D3DDEVTYPE DeviceType, D3D9Format SourceFormat, D3D9Format TargetFormat) { bool sourceSupported = SourceFormat != D3D9Format::Unknown && IsSupportedBackBufferFormat(SourceFormat); bool targetSupported = TargetFormat == D3D9Format::X1R5G5B5 || TargetFormat == D3D9Format::A1R5G5B5 || TargetFormat == D3D9Format::R5G6B5 // || TargetFormat == D3D9Format::R8G8B8 <-- We don't support R8G8B8 || TargetFormat == D3D9Format::X8R8G8B8 || TargetFormat == D3D9Format::A8R8G8B8 || TargetFormat == D3D9Format::A2R10G10B10 || TargetFormat == D3D9Format::A16B16G16R16 || TargetFormat == D3D9Format::A2B10G10R10 || TargetFormat == D3D9Format::A8B8G8R8 || TargetFormat == D3D9Format::X8B8G8R8 || TargetFormat == D3D9Format::A16B16G16R16F || TargetFormat == D3D9Format::A32B32G32R32F; return (sourceSupported && targetSupported) ? D3D_OK : D3DERR_NOTAVAILABLE; } HRESULT D3D9Adapter::GetDeviceCaps( D3DDEVTYPE DeviceType, D3DCAPS9* pCaps) { using namespace dxvk::caps; if (pCaps == nullptr) return D3DERR_INVALIDCALL; auto& options = m_parent->GetOptions(); // TODO: Actually care about what the adapter supports here. // ^ For Intel and older cards most likely here. // Device Type pCaps->DeviceType = DeviceType; // Adapter Id pCaps->AdapterOrdinal = m_ordinal; // Caps 1 pCaps->Caps = D3DCAPS_READ_SCANLINE; // Caps 2 pCaps->Caps2 = D3DCAPS2_FULLSCREENGAMMA /* | D3DCAPS2_CANCALIBRATEGAMMA */ /* | D3DCAPS2_RESERVED */ /* | D3DCAPS2_CANMANAGERESOURCE */ | D3DCAPS2_DYNAMICTEXTURES | D3DCAPS2_CANAUTOGENMIPMAP /* | D3DCAPS2_CANSHARERESOURCE */; // Caps 3 pCaps->Caps3 = D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD | D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION | D3DCAPS3_COPY_TO_VIDMEM | D3DCAPS3_COPY_TO_SYSTEMMEM /* | D3DCAPS3_DXVAHD */ /* | D3DCAPS3_DXVAHD_LIMITED */; // Presentation Intervals pCaps->PresentationIntervals = D3DPRESENT_INTERVAL_DEFAULT | D3DPRESENT_INTERVAL_ONE | D3DPRESENT_INTERVAL_TWO | D3DPRESENT_INTERVAL_THREE | D3DPRESENT_INTERVAL_FOUR | D3DPRESENT_INTERVAL_IMMEDIATE; // Cursor pCaps->CursorCaps = D3DCURSORCAPS_COLOR; // I do not support Cursor yet, but I don't want to say I don't support it for compatibility reasons. // Dev Caps pCaps->DevCaps = D3DDEVCAPS_EXECUTESYSTEMMEMORY | D3DDEVCAPS_EXECUTEVIDEOMEMORY | D3DDEVCAPS_TLVERTEXSYSTEMMEMORY | D3DDEVCAPS_TLVERTEXVIDEOMEMORY /* | D3DDEVCAPS_TEXTURESYSTEMMEMORY */ | D3DDEVCAPS_TEXTUREVIDEOMEMORY | D3DDEVCAPS_DRAWPRIMTLVERTEX | D3DDEVCAPS_CANRENDERAFTERFLIP | D3DDEVCAPS_TEXTURENONLOCALVIDMEM | D3DDEVCAPS_DRAWPRIMITIVES2 /* | D3DDEVCAPS_SEPARATETEXTUREMEMORIES */ | D3DDEVCAPS_DRAWPRIMITIVES2EX | D3DDEVCAPS_HWTRANSFORMANDLIGHT | D3DDEVCAPS_CANBLTSYSTONONLOCAL | D3DDEVCAPS_HWRASTERIZATION | D3DDEVCAPS_PUREDEVICE /* | D3DDEVCAPS_QUINTICRTPATCHES */ /* | D3DDEVCAPS_RTPATCHES */ /* | D3DDEVCAPS_RTPATCHHANDLEZERO */ /* | D3DDEVCAPS_NPATCHES */; // Primitive Misc. Caps pCaps->PrimitiveMiscCaps = D3DPMISCCAPS_MASKZ | D3DPMISCCAPS_CULLNONE | D3DPMISCCAPS_CULLCW | D3DPMISCCAPS_CULLCCW | D3DPMISCCAPS_COLORWRITEENABLE | D3DPMISCCAPS_CLIPPLANESCALEDPOINTS | D3DPMISCCAPS_CLIPTLVERTS | D3DPMISCCAPS_TSSARGTEMP | D3DPMISCCAPS_BLENDOP /* | D3DPMISCCAPS_NULLREFERENCE */ | D3DPMISCCAPS_INDEPENDENTWRITEMASKS | D3DPMISCCAPS_PERSTAGECONSTANT | D3DPMISCCAPS_FOGANDSPECULARALPHA | D3DPMISCCAPS_SEPARATEALPHABLEND | D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS | D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING | D3DPMISCCAPS_FOGVERTEXCLAMPED | D3DPMISCCAPS_POSTBLENDSRGBCONVERT; // Raster Caps pCaps->RasterCaps = D3DPRASTERCAPS_DITHER | D3DPRASTERCAPS_ZTEST | D3DPRASTERCAPS_FOGVERTEX | D3DPRASTERCAPS_FOGTABLE | D3DPRASTERCAPS_MIPMAPLODBIAS /* | D3DPRASTERCAPS_ZBUFFERLESSHSR */ | D3DPRASTERCAPS_FOGRANGE | D3DPRASTERCAPS_ANISOTROPY /* | D3DPRASTERCAPS_WBUFFER */ | D3DPRASTERCAPS_WFOG | D3DPRASTERCAPS_ZFOG | D3DPRASTERCAPS_COLORPERSPECTIVE | D3DPRASTERCAPS_SCISSORTEST | D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS | D3DPRASTERCAPS_DEPTHBIAS | D3DPRASTERCAPS_MULTISAMPLE_TOGGLE; // <-- TODO! (but difficult in Vk) // Z Comparison Caps pCaps->ZCmpCaps = D3DPCMPCAPS_NEVER | D3DPCMPCAPS_LESS | D3DPCMPCAPS_EQUAL | D3DPCMPCAPS_LESSEQUAL | D3DPCMPCAPS_GREATER | D3DPCMPCAPS_NOTEQUAL | D3DPCMPCAPS_GREATEREQUAL | D3DPCMPCAPS_ALWAYS; // Source Blend Caps pCaps->SrcBlendCaps = D3DPBLENDCAPS_ZERO | D3DPBLENDCAPS_ONE | D3DPBLENDCAPS_SRCCOLOR | D3DPBLENDCAPS_INVSRCCOLOR | D3DPBLENDCAPS_SRCALPHA | D3DPBLENDCAPS_INVSRCALPHA | D3DPBLENDCAPS_DESTALPHA | D3DPBLENDCAPS_INVDESTALPHA | D3DPBLENDCAPS_DESTCOLOR | D3DPBLENDCAPS_INVDESTCOLOR | D3DPBLENDCAPS_SRCALPHASAT | D3DPBLENDCAPS_BOTHSRCALPHA | D3DPBLENDCAPS_BOTHINVSRCALPHA | D3DPBLENDCAPS_BLENDFACTOR | D3DPBLENDCAPS_INVSRCCOLOR2 | D3DPBLENDCAPS_SRCCOLOR2; // Destination Blend Caps pCaps->DestBlendCaps = pCaps->SrcBlendCaps; // Alpha Comparison Caps pCaps->AlphaCmpCaps = pCaps->ZCmpCaps; // Shade Caps pCaps->ShadeCaps = D3DPSHADECAPS_COLORGOURAUDRGB | D3DPSHADECAPS_SPECULARGOURAUDRGB | D3DPSHADECAPS_ALPHAGOURAUDBLEND | D3DPSHADECAPS_FOGGOURAUD; // Texture Caps pCaps->TextureCaps = D3DPTEXTURECAPS_PERSPECTIVE /* | D3DPTEXTURECAPS_POW2 */ | D3DPTEXTURECAPS_ALPHA /* | D3DPTEXTURECAPS_SQUAREONLY */ | D3DPTEXTURECAPS_TEXREPEATNOTSCALEDBYSIZE | D3DPTEXTURECAPS_ALPHAPALETTE /* | D3DPTEXTURECAPS_NONPOW2CONDITIONAL */ | D3DPTEXTURECAPS_PROJECTED | D3DPTEXTURECAPS_CUBEMAP | D3DPTEXTURECAPS_VOLUMEMAP | D3DPTEXTURECAPS_MIPMAP | D3DPTEXTURECAPS_MIPVOLUMEMAP | D3DPTEXTURECAPS_MIPCUBEMAP /* | D3DPTEXTURECAPS_CUBEMAP_POW2 */ /* | D3DPTEXTURECAPS_VOLUMEMAP_POW2 */ /* | D3DPTEXTURECAPS_NOPROJECTEDBUMPENV */; // Texture Filter Caps pCaps->TextureFilterCaps = D3DPTFILTERCAPS_MINFPOINT | D3DPTFILTERCAPS_MINFLINEAR | D3DPTFILTERCAPS_MINFANISOTROPIC /* | D3DPTFILTERCAPS_MINFPYRAMIDALQUAD */ /* | D3DPTFILTERCAPS_MINFGAUSSIANQUAD */ | D3DPTFILTERCAPS_MIPFPOINT | D3DPTFILTERCAPS_MIPFLINEAR /* | D3DPTFILTERCAPS_CONVOLUTIONMONO */ | D3DPTFILTERCAPS_MAGFPOINT | D3DPTFILTERCAPS_MAGFLINEAR | D3DPTFILTERCAPS_MAGFANISOTROPIC /* | D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD */ /* | D3DPTFILTERCAPS_MAGFGAUSSIANQUAD */; // Cube Texture Filter Caps pCaps->CubeTextureFilterCaps = pCaps->TextureFilterCaps; // Volume Texture Filter Caps pCaps->VolumeTextureFilterCaps = pCaps->TextureFilterCaps; // Texture Address Caps pCaps->TextureAddressCaps = D3DPTADDRESSCAPS_WRAP | D3DPTADDRESSCAPS_MIRROR | D3DPTADDRESSCAPS_CLAMP | D3DPTADDRESSCAPS_BORDER | D3DPTADDRESSCAPS_INDEPENDENTUV | D3DPTADDRESSCAPS_MIRRORONCE; // Volume Texture Address Caps pCaps->VolumeTextureAddressCaps = pCaps->TextureAddressCaps; // Line Caps pCaps->LineCaps = D3DLINECAPS_TEXTURE | D3DLINECAPS_ZTEST | D3DLINECAPS_BLEND | D3DLINECAPS_ALPHACMP | D3DLINECAPS_FOG | D3DLINECAPS_ANTIALIAS; //<-- Lying about doing AA lines here, we don't *fully* support that. // Max Texture Width pCaps->MaxTextureWidth = MaxTextureDimension; // Max Texture Height pCaps->MaxTextureHeight = MaxTextureDimension; // Max Volume Extent pCaps->MaxVolumeExtent = 8192; // Max Texture Repeat pCaps->MaxTextureRepeat = 8192; // Max Texture Aspect Ratio pCaps->MaxTextureAspectRatio = 8192; // Max Anisotropy pCaps->MaxAnisotropy = 16; // Max Vertex W pCaps->MaxVertexW = 1e10f; // Guard Bands pCaps->GuardBandLeft = -32768.0f; pCaps->GuardBandTop = -32768.0f; pCaps->GuardBandRight = 32768.0f; pCaps->GuardBandBottom = 32768.0f; // Extents Adjust pCaps->ExtentsAdjust = 0.0f; // Stencil Caps pCaps->StencilCaps = D3DSTENCILCAPS_KEEP | D3DSTENCILCAPS_ZERO | D3DSTENCILCAPS_REPLACE | D3DSTENCILCAPS_INCRSAT | D3DSTENCILCAPS_DECRSAT | D3DSTENCILCAPS_INVERT | D3DSTENCILCAPS_INCR | D3DSTENCILCAPS_DECR | D3DSTENCILCAPS_TWOSIDED; // FVF Caps pCaps->FVFCaps = (MaxSimultaneousTextures & D3DFVFCAPS_TEXCOORDCOUNTMASK) /* | D3DFVFCAPS_DONOTSTRIPELEMENTS */ | D3DFVFCAPS_PSIZE; // Texture Op Caps pCaps->TextureOpCaps = D3DTEXOPCAPS_DISABLE | D3DTEXOPCAPS_SELECTARG1 | D3DTEXOPCAPS_SELECTARG2 | D3DTEXOPCAPS_MODULATE | D3DTEXOPCAPS_MODULATE2X | D3DTEXOPCAPS_MODULATE4X | D3DTEXOPCAPS_ADD | D3DTEXOPCAPS_ADDSIGNED | D3DTEXOPCAPS_ADDSIGNED2X | D3DTEXOPCAPS_SUBTRACT | D3DTEXOPCAPS_ADDSMOOTH | D3DTEXOPCAPS_BLENDDIFFUSEALPHA | D3DTEXOPCAPS_BLENDTEXTUREALPHA | D3DTEXOPCAPS_BLENDFACTORALPHA | D3DTEXOPCAPS_BLENDTEXTUREALPHAPM | D3DTEXOPCAPS_BLENDCURRENTALPHA | D3DTEXOPCAPS_PREMODULATE | D3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR | D3DTEXOPCAPS_MODULATECOLOR_ADDALPHA | D3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR | D3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA | D3DTEXOPCAPS_BUMPENVMAP | D3DTEXOPCAPS_BUMPENVMAPLUMINANCE | D3DTEXOPCAPS_DOTPRODUCT3 | D3DTEXOPCAPS_MULTIPLYADD | D3DTEXOPCAPS_LERP; // Max Texture Blend Stages pCaps->MaxTextureBlendStages = MaxTextureBlendStages; // Max Simultaneous Textures pCaps->MaxSimultaneousTextures = MaxSimultaneousTextures; // Vertex Processing Caps pCaps->VertexProcessingCaps = D3DVTXPCAPS_TEXGEN | D3DVTXPCAPS_MATERIALSOURCE7 | D3DVTXPCAPS_DIRECTIONALLIGHTS | D3DVTXPCAPS_POSITIONALLIGHTS | D3DVTXPCAPS_LOCALVIEWER | D3DVTXPCAPS_TWEENING | D3DVTXPCAPS_TEXGEN_SPHEREMAP /* | D3DVTXPCAPS_NO_TEXGEN_NONLOCALVIEWER*/; // Max Active Lights pCaps->MaxActiveLights = caps::MaxEnabledLights; // Max User Clip Planes pCaps->MaxUserClipPlanes = MaxClipPlanes; // Max Vertex Blend Matrices pCaps->MaxVertexBlendMatrices = 4; // Max Vertex Blend Matrix Index pCaps->MaxVertexBlendMatrixIndex = 8; // Max Point Size pCaps->MaxPointSize = 256.0f; // Max Primitive Count pCaps->MaxPrimitiveCount = 0x00555555; // Max Vertex Index pCaps->MaxVertexIndex = 0x00ffffff; // Max Streams pCaps->MaxStreams = MaxStreams; // Max Stream Stride pCaps->MaxStreamStride = 508; // bytes const uint32_t majorVersion = options.shaderModel; const uint32_t minorVersion = options.shaderModel != 1 ? 0 : 4; // Shader Versions pCaps->VertexShaderVersion = D3DVS_VERSION(majorVersion, minorVersion); pCaps->PixelShaderVersion = D3DPS_VERSION(majorVersion, minorVersion); // Max Vertex Shader Const pCaps->MaxVertexShaderConst = MaxFloatConstantsVS; // Max PS1 Value pCaps->PixelShader1xMaxValue = FLT_MAX; // Dev Caps 2 pCaps->DevCaps2 = D3DDEVCAPS2_STREAMOFFSET /* | D3DDEVCAPS2_DMAPNPATCH */ /* | D3DDEVCAPS2_ADAPTIVETESSRTPATCH */ /* | D3DDEVCAPS2_ADAPTIVETESSNPATCH */ | D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES /* | D3DDEVCAPS2_PRESAMPLEDDMAPNPATCH */ | D3DDEVCAPS2_VERTEXELEMENTSCANSHARESTREAMOFFSET; // Max N Patch Tesselation Level pCaps->MaxNpatchTessellationLevel = 0.0f; // Reserved for... something pCaps->Reserved5 = 0; // Master adapter for us is adapter 0, atm... pCaps->MasterAdapterOrdinal = 0; // The group of adapters this one is in pCaps->AdapterOrdinalInGroup = 0; // Number of adapters in current group pCaps->NumberOfAdaptersInGroup = 1; // Decl Type Caps pCaps->DeclTypes = D3DDTCAPS_UBYTE4 | D3DDTCAPS_UBYTE4N | D3DDTCAPS_SHORT2N | D3DDTCAPS_SHORT4N | D3DDTCAPS_USHORT2N | D3DDTCAPS_USHORT4N | D3DDTCAPS_UDEC3 | D3DDTCAPS_DEC3N | D3DDTCAPS_FLOAT16_2 | D3DDTCAPS_FLOAT16_4; // Number of simultaneous RTs pCaps->NumSimultaneousRTs = MaxSimultaneousRenderTargets; // Possible StretchRect filters pCaps->StretchRectFilterCaps = D3DPTFILTERCAPS_MINFPOINT | D3DPTFILTERCAPS_MINFLINEAR /* | D3DPTFILTERCAPS_MINFANISOTROPIC */ /* | D3DPTFILTERCAPS_MINFPYRAMIDALQUAD */ /* | D3DPTFILTERCAPS_MINFGAUSSIANQUAD */ /* | D3DPTFILTERCAPS_MIPFPOINT */ /* | D3DPTFILTERCAPS_MIPFLINEAR */ /* | D3DPTFILTERCAPS_CONVOLUTIONMONO */ | D3DPTFILTERCAPS_MAGFPOINT | D3DPTFILTERCAPS_MAGFLINEAR /* | D3DPTFILTERCAPS_MAGFANISOTROPIC */ /* | D3DPTFILTERCAPS_MAGFPYRAMIDALQUAD */ /* | D3DPTFILTERCAPS_MAGFGAUSSIANQUAD */; // Not too bothered about doing these longhand // We should match whatever my AMD hardware reports here // methinks for the best chance of stuff working. pCaps->VS20Caps.Caps = 1; pCaps->VS20Caps.DynamicFlowControlDepth = 24; pCaps->VS20Caps.NumTemps = 32; pCaps->VS20Caps.StaticFlowControlDepth = 4; pCaps->PS20Caps.Caps = 31; pCaps->PS20Caps.DynamicFlowControlDepth = 24; pCaps->PS20Caps.NumTemps = 32; pCaps->PS20Caps.StaticFlowControlDepth = 4; pCaps->PS20Caps.NumInstructionSlots = options.shaderModel >= 2 ? 512 : 256; pCaps->VertexTextureFilterCaps = 50332416; pCaps->MaxVShaderInstructionsExecuted = 4294967295; pCaps->MaxPShaderInstructionsExecuted = 4294967295; pCaps->MaxVertexShader30InstructionSlots = options.shaderModel == 3 ? 32768 : 0; pCaps->MaxPixelShader30InstructionSlots = options.shaderModel == 3 ? 32768 : 0; return D3D_OK; } HMONITOR D3D9Adapter::GetMonitor() { return GetDefaultMonitor(); } UINT D3D9Adapter::GetAdapterModeCountEx(const D3DDISPLAYMODEFILTER* pFilter) { if (pFilter == nullptr) return 0; // We don't offer any interlaced formats here so early out and avoid destroying mode cache. if (pFilter->ScanLineOrdering == D3DSCANLINEORDERING_INTERLACED) return 0; CacheModes(EnumerateFormat(pFilter->Format)); return m_modes.size(); } HRESULT D3D9Adapter::EnumAdapterModesEx( const D3DDISPLAYMODEFILTER* pFilter, UINT Mode, D3DDISPLAYMODEEX* pMode) { if (pMode == nullptr || pFilter == nullptr) return D3DERR_INVALIDCALL; const D3D9Format format = EnumerateFormat(pFilter->Format); if (FAILED(CheckDeviceFormat( D3DDEVTYPE_HAL, EnumerateFormat(pFilter->Format), D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, EnumerateFormat(pFilter->Format)))) return D3DERR_INVALIDCALL; CacheModes(format); // We don't return any scanline orderings that aren't progressive, // The format filtering is already handled for us by cache modes // So we can early out here and then just index. if (pFilter->ScanLineOrdering == D3DSCANLINEORDERING_INTERLACED) return D3DERR_INVALIDCALL; if (Mode >= m_modes.size()) return D3DERR_INVALIDCALL; *pMode = m_modes[Mode]; return D3D_OK; } HRESULT D3D9Adapter::GetAdapterDisplayModeEx( D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation) { if (pMode == nullptr) return D3DERR_INVALIDCALL; if (pRotation != nullptr) *pRotation = D3DDISPLAYROTATION_IDENTITY; DEVMODEW devMode = DEVMODEW(); devMode.dmSize = sizeof(devMode); if (!GetMonitorDisplayMode(GetDefaultMonitor(), ENUM_CURRENT_SETTINGS, &devMode)) { Logger::err("D3D9Adapter::GetAdapterDisplayModeEx: Failed to enum display settings"); return D3DERR_INVALIDCALL; } pMode->Size = sizeof(D3DDISPLAYMODEEX); pMode->Width = devMode.dmPelsWidth; pMode->Height = devMode.dmPelsHeight; pMode->RefreshRate = devMode.dmDisplayFrequency; pMode->Format = D3DFMT_X8R8G8B8; pMode->ScanLineOrdering = D3DSCANLINEORDERING_PROGRESSIVE; return D3D_OK; } HRESULT D3D9Adapter::GetAdapterLUID(LUID* pLUID) { if (pLUID == nullptr) return D3DERR_INVALIDCALL; auto& deviceId = m_adapter->devicePropertiesExt().coreDeviceId; if (deviceId.deviceLUIDValid) *pLUID = bit::cast(deviceId.deviceLUID); else *pLUID = dxvk::GetAdapterLUID(m_ordinal); return D3D_OK; } HRESULT D3D9Adapter::CheckDeviceVkFormat( VkFormat Format, DWORD Usage, D3DRESOURCETYPE RType) { VkFormatFeatureFlags checkFlags = 0; if (RType != D3DRTYPE_SURFACE) checkFlags |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; if (Usage & D3DUSAGE_RENDERTARGET) { checkFlags |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT; if (Usage & D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING) checkFlags |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT; } if (Usage & D3DUSAGE_DEPTHSTENCIL) checkFlags |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; else checkFlags |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; VkFormatFeatureFlags checkFlagsMipGen = checkFlags; if (Usage & D3DUSAGE_AUTOGENMIPMAP) { checkFlagsMipGen |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; checkFlagsMipGen |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT; } VkFormatProperties fmtSupport = m_adapter->formatProperties(Format); VkFormatFeatureFlags imgFeatures = fmtSupport.optimalTilingFeatures | fmtSupport.linearTilingFeatures; if ((imgFeatures & checkFlags) != checkFlags) return D3DERR_NOTAVAILABLE; return ((imgFeatures & checkFlagsMipGen) != checkFlagsMipGen) ? D3DOK_NOAUTOGEN : D3D_OK; } void D3D9Adapter::CacheModes(D3D9Format Format) { if (!m_modes.empty() && m_modeCacheFormat == Format) return; // We already cached the modes for this format. No need to do it again. m_modes.clear(); m_modeCacheFormat = Format; // Skip unsupported formats if (!IsSupportedAdapterFormat(Format)) return; auto& options = m_parent->GetOptions(); // Walk over all modes that the display supports and // return those that match the requested format etc. DEVMODEW devMode = { }; devMode.dmSize = sizeof(DEVMODEW); uint32_t modeIndex = 0; const auto forcedRatio = Ratio(options.forceAspectRatio); while (GetMonitorDisplayMode(GetDefaultMonitor(), modeIndex++, &devMode)) { // Skip interlaced modes altogether if (devMode.dmDisplayFlags & DM_INTERLACED) continue; // Skip modes with incompatible formats if (devMode.dmBitsPerPel != GetMonitorFormatBpp(Format)) continue; if (!forcedRatio.undefined() && Ratio(devMode.dmPelsWidth, devMode.dmPelsHeight) != forcedRatio) continue; D3DDISPLAYMODEEX mode; mode.Size = sizeof(D3DDISPLAYMODEEX); mode.Width = devMode.dmPelsWidth; mode.Height = devMode.dmPelsHeight; mode.RefreshRate = devMode.dmDisplayFrequency; mode.Format = static_cast(Format); mode.ScanLineOrdering = D3DSCANLINEORDERING_PROGRESSIVE; m_modes.push_back(mode); } // Sort display modes by width, height and refresh rate, // in that order. Some games rely on correct ordering. std::sort(m_modes.begin(), m_modes.end(), [](const D3DDISPLAYMODEEX& a, const D3DDISPLAYMODEEX& b) { if (a.Width < b.Width) return true; if (a.Width > b.Width) return false; if (a.Height < b.Height) return true; if (a.Height > b.Height) return false; return a.RefreshRate < b.RefreshRate; }); } }