[d3d9] Implement Direct3D9 Frontend (#1275)

Co-authored-by: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Co-authored-by: Robin Kertels <robin.kertels@gmail.com>
Co-authored-by: pchome <pchome@users.noreply.github.com>
Co-authored-by: Christopher Egert <cme3000@gmail.com>
Co-authored-by: Derek Lesho <dereklesho52@Gmail.com>
Co-authored-by: Luis Cáceres <lacaceres97@gmail.com>
Co-authored-by: Nelson Chen <crazysim@gmail.com>
Co-authored-by: Edmondo Tommasina <edmondo.tommasina@gmail.com>
Co-authored-by: Riesi <riesi@opentrash.com>
Co-authored-by: gbMichelle <gbmichelle.dev@gmail.com>
This commit is contained in:
Joshie 2019-12-16 03:28:01 +00:00 committed by Philip Rebohle
parent 566fb84abd
commit 54ed8f0bb0
118 changed files with 28889 additions and 3 deletions

View file

@ -9,7 +9,10 @@ assignees: ''
Please describe your issue as accurately as possible. If you run into a problem with a binary release, make sure to test with latest `master` as well.
**Important:** When reporting an issue with a specific game or application, such as crashes or rendering issues, please include log files and a D3D11 Apitrace (see https://github.com/apitrace/apitrace) so that the issue can be reproduced. In order to create a trace, run `wine apitrace.exe trace -a dxgi YOURGAME.exe`. DO NOT use DXVK together with apitrace!
**Important:** When reporting an issue with a specific game or application, such as crashes or rendering issues, please include log files and a D3D11/D3D9 Apitrace (see https://github.com/apitrace/apitrace) so that the issue can be reproduced.
In order to create a trace for **D3D11/D3D10**: Run `wine apitrace.exe trace -a dxgi YOURGAME.exe`.
In order to create a trace for **D3D9**: Follow https://github.com/Joshua-Ashton/d9vk/wiki/Making-a-Trace.
DO NOT use DXVK together with apitrace!
### Software information
Name of the game, settings used etc.
@ -24,5 +27,6 @@ Name of the game, settings used etc.
- Put a link here
### Log files
- d3d9.log:
- d3d11.log:
- dxgi.log:

View file

@ -1,4 +1,5 @@
Copyright (c) 2017-2019 Philip Rebohle
Copyright (c) 2019 Joshua Ashton
zlib/libpng license

112
dxvk.conf
View file

@ -6,6 +6,7 @@
# Supported values: True, False
# dxgi.deferSurfaceCreation = False
# d3d9.deferSurfaceCreation = False
# Enforce a stricter maximum frame latency. Overrides the application
@ -15,6 +16,7 @@
# Supported values : 0 - 16
# dxgi.maxFrameLatency = 0
# d3d9.maxFrameLatency = 0
# Override PCI vendor and device IDs reported to the application. Can
@ -25,6 +27,9 @@
# dxgi.customDeviceId = 0000
# dxgi.customVendorId = 0000
# d3d9.customDeviceId = 0000
# d3d9.customVendorId = 0000
# Report Nvidia GPUs as AMD GPUs by default. This is enabled by default
# to work around issues with NVAPI, but may cause issues in some games.
@ -60,7 +65,8 @@
#
# Supported values: Any non-negative number
# dxgi.syncInterval = -1
# dxgi.syncInterval = -1
# d3d9.presentInterval = -1
# Performs range check on dynamically indexed constant buffers in shaders.
@ -115,6 +121,7 @@
# Supported values: Any number between 0 and 16
# d3d11.samplerAnisotropy = -1
# d3d9.samplerAnisotropy = -1
# Replaces NaN outputs from fragment shaders with zeroes for floating
@ -179,3 +186,106 @@
# ignored. The syntax is identical.
# dxvk.hud =
# Reported shader model
#
# The shader model to state that we support in the device
# capabilities that the applicatation queries.
#
# Supported values:
# - 1: Shader Model 1
# - 2: Shader Model 2
# - 3: Shader Model 3
# d3d9.shaderModel = 3
# Evict Managed on Unlock
#
# Decides whether we should evict managed resources from
# system memory when they are unlocked entirely.
#
# Supported values:
# - True, False: Always enable / disable
# d3d9.evictManagedOnUnlock = False
# DPI Awareness
#
# Decides whether we should call SetProcessDPIAware on device
# creation. Helps avoid upscaling blur in modern Windows on
# Hi-DPI screens/devices.
#
# Supported values:
# - True, False: Always enable / disable
# d3d9.dpiAware = True
# Strict Constant Copies
#
# Decides whether we should always copy defined constants to
# the UBO when relative addresssing is used, or only when the
# relative addressing starts a defined constant.
#
# Supported values:
# - True, False: Always enable / disable
# d3d9.strictConstantCopies = False
# Strict Pow
#
# Decides whether we have an opSelect for handling pow(0,0) = 0
# otherwise it becomes undefined.
#
# Supported values:
# - True, False: Always enable / disable
# d3d9.strictPow = True
# Lenient Clear
#
# Decides whether or not we fastpath clear anyway if we are close enough to
# clearing a full render target.
#
# Supported values:
# - True, False: Always enable / disable
# d3d9.lenientClear = False
# Max available memory
#
# Changes the max initial value used in tracking and GetAvailableTextureMem
# Value in Megabytes
#
# Supported values:
# - Any int32_t
# d3d9.maxAvailableMemory = 4096
# Force enable/disable floating point quirk emulation
#
# Force toggle anything * 0 emulation
# Tristate
# Supported values:
# - True/False
# d3d9.floatEmulation =
# Enable dialog box mode
#
# Changes the default state of dialog box mode.
# *Disables* exclusive fullscreen when enabled.
#
# Supported values:
# - True, False: Always enable / disable
# d3d9.enableDialogMode = False

View file

@ -38,6 +38,7 @@ if dxvk_winelib
endif
wrc = find_program('wrc')
lib_vulkan = declare_dependency(link_args: [ '-lwinevulkan' ])
lib_d3d9 = declare_dependency(link_args: [ '-ld3d9' ])
lib_d3d11 = declare_dependency(link_args: [ '-ld3d11' ])
lib_dxgi = declare_dependency(link_args: [ '-ldxgi' ])
lib_d3dcompiler_43 = declare_dependency(link_args: [ '-L'+dxvk_library_path, '-ld3dcompiler_43' ])
@ -63,6 +64,7 @@ else
endif
lib_vulkan = dxvk_compiler.find_library('vulkan-1', dirs : dxvk_library_path)
lib_d3d9 = dxvk_compiler.find_library('d3d9')
lib_d3d11 = dxvk_compiler.find_library('d3d11')
lib_dxgi = dxvk_compiler.find_library('dxgi')
lib_d3dcompiler_43 = dxvk_compiler.find_library('d3dcompiler_43', dirs : dxvk_library_path)

View file

@ -1,4 +1,5 @@
option('enable_tests', type : 'boolean', value : false)
option('enable_dxgi', type : 'boolean', value : true, description: 'Build DXGI')
option('enable_d3d9', type : 'boolean', value : true, description: 'Build D3D9')
option('enable_d3d10', type : 'boolean', value : true, description: 'Build D3D10')
option('enable_d3d11', type : 'boolean', value : true, description: 'Build D3D11')

View file

@ -172,6 +172,7 @@ if [ $with_dxgi -ne 0 ] || [ "$action" == "uninstall" ]; then
$action dxgi
fi
$action d3d9
$action d3d10
$action d3d10_1
$action d3d10core

22
src/d3d9/d3d9.def Normal file
View file

@ -0,0 +1,22 @@
LIBRARY D3D9.DLL
EXPORTS
Direct3DShaderValidatorCreate9 @ 24
PSGPError @ 25
PSGPSampleTexture @ 26
D3DPERF_BeginEvent @ 27
D3DPERF_EndEvent @ 28
D3DPERF_GetStatus @ 29
D3DPERF_QueryRepeatFrame @ 30
D3DPERF_SetMarker @ 31
D3DPERF_SetOptions @ 32
D3DPERF_SetRegion @ 33
DebugSetLevel @ 34
DebugSetMute @ 35
Direct3D9EnableMaximizedWindowedModeShim @ 36
Direct3DCreate9 @ 37
Direct3DCreate9Ex @ 38

20
src/d3d9/d3d9.spec Normal file
View file

@ -0,0 +1,20 @@
@ stdcall Direct3DShaderValidatorCreate9()
@ stdcall PSGPError(ptr long long)
@ stdcall PSGPSampleTexture(ptr long ptr long ptr)
@ stdcall D3DPERF_BeginEvent(long wstr)
@ stdcall D3DPERF_EndEvent()
@ stdcall D3DPERF_GetStatus()
@ stdcall D3DPERF_QueryRepeatFrame()
@ stdcall D3DPERF_SetMarker(long wstr)
@ stdcall D3DPERF_SetOptions(long)
@ stdcall D3DPERF_SetRegion(long wstr)
@ stdcall DebugSetLevel()
@ stdcall DebugSetMute()
@ stdcall Direct3D9EnableMaximizedWindowedModeShim(long)
@ stdcall Direct3DCreate9(long)
@ stdcall Direct3DCreate9Ex(long ptr)

804
src/d3d9/d3d9_adapter.cpp Normal file
View file

@ -0,0 +1,804 @@
#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 <cfloat>
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<DxvkAdapter> Adapter,
UINT Ordinal)
: m_parent (pParent)
, m_adapter (Adapter)
, m_ordinal (Ordinal)
, m_modeCacheFormat (D3D9Format::Unknown)
, m_d3d9Formats (Adapter, m_parent->GetOptions()) {
m_adapter->logAdapterInfo();
}
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();
::MONITORINFOEXA monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoA(GetDefaultMonitor(), reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("D3D9Adapter::GetAdapterIdentifier: Failed to query monitor info");
return D3DERR_INVALIDCALL;
}
GUID guid = bit::cast<GUID>(m_adapter->devicePropertiesExt().coreDeviceId.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));
std::strncpy(pIdentifier->Description, desc, countof(pIdentifier->Description));
std::strncpy(pIdentifier->DeviceName, monInfo.szDevice, countof(pIdentifier->DeviceName)); // The GDI device name. Not the actual device name.
std::strncpy(pIdentifier->Driver, driver, countof(pIdentifier->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 (!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_INVALIDCALL;
if (!IsSupportedDisplayFormat(AdapterFormat, false))
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 D3D_OK;
// 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<uint32_t>(MultiSampleType, 1u);
// Check if this is a power of two...
if (sampleCount & (sampleCount - 1))
return D3DERR_NOTAVAILABLE;
// Therefore...
VkSampleCountFlags sampleFlags = VkSampleCountFlags(sampleCount);
auto availableFlags = !IsDepthFormat(SurfaceFormat)
? 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 (!IsSupportedAdapterFormat(AdapterFormat))
return D3DERR_NOTAVAILABLE;
if (!IsDepthFormat(DepthStencilFormat))
return D3DERR_NOTAVAILABLE;
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 = IsSupportedBackBufferFormat(SourceFormat, FALSE);
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 (pRotation != nullptr)
*pRotation = D3DDISPLAYROTATION_IDENTITY;
D3DDISPLAYMODEFILTER filter;
filter.Size = sizeof(filter);
filter.Format = D3DFMT_X8R8G8B8;
filter.ScanLineOrdering = D3DSCANLINEORDERING_PROGRESSIVE;
return this->EnumAdapterModesEx(&filter, 0, pMode);
}
HRESULT D3D9Adapter::GetAdapterLUID(LUID* pLUID) {
if (pLUID == nullptr)
return D3DERR_INVALIDCALL;
auto& deviceId = m_adapter->devicePropertiesExt().coreDeviceId;
if (deviceId.deviceLUIDValid)
*pLUID = bit::cast<LUID>(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.
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(GetDefaultMonitor(), reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("D3D9Adapter::CacheModes: failed to query monitor info");
return;
}
m_modes.clear();
m_modeCacheFormat = Format;
// Skip unsupported formats
if (!IsSupportedAdapterFormat(Format) || !IsSupportedDisplayFormat(Format, false))
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<DWORD>(options.forceAspectRatio);
while (::EnumDisplaySettingsW(monInfo.szDevice, 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<DWORD>(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<D3DFORMAT>(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;
});
}
}

112
src/d3d9/d3d9_adapter.h Normal file
View file

@ -0,0 +1,112 @@
#pragma once
#include "d3d9_include.h"
#include "d3d9_options.h"
#include "d3d9_format.h"
#include "../dxvk/dxvk_adapter.h"
namespace dxvk {
class D3D9InterfaceEx;
class D3D9Adapter {
public:
D3D9Adapter(
D3D9InterfaceEx* pParent,
Rc<DxvkAdapter> Adapter,
UINT Ordinal);
HRESULT GetAdapterIdentifier(
DWORD Flags,
D3DADAPTER_IDENTIFIER9* pIdentifier);
HRESULT CheckDeviceType(
D3DDEVTYPE DevType,
D3D9Format AdapterFormat,
D3D9Format BackBufferFormat,
BOOL bWindowed);
HRESULT CheckDeviceFormat(
D3DDEVTYPE DeviceType,
D3D9Format AdapterFormat,
DWORD Usage,
D3DRESOURCETYPE RType,
D3D9Format CheckFormat);
HRESULT CheckDeviceMultiSampleType(
D3DDEVTYPE DeviceType,
D3D9Format SurfaceFormat,
BOOL Windowed,
D3DMULTISAMPLE_TYPE MultiSampleType,
DWORD* pQualityLevels);
HRESULT CheckDepthStencilMatch(
D3DDEVTYPE DeviceType,
D3D9Format AdapterFormat,
D3D9Format RenderTargetFormat,
D3D9Format DepthStencilFormat);
HRESULT CheckDeviceFormatConversion(
D3DDEVTYPE DeviceType,
D3D9Format SourceFormat,
D3D9Format TargetFormat);
HRESULT GetDeviceCaps(
D3DDEVTYPE DeviceType,
D3DCAPS9* pCaps);
HMONITOR GetMonitor();
UINT GetAdapterModeCountEx(CONST D3DDISPLAYMODEFILTER* pFilter);
HRESULT EnumAdapterModesEx(
const D3DDISPLAYMODEFILTER* pFilter,
UINT Mode,
D3DDISPLAYMODEEX* pMode);
HRESULT GetAdapterDisplayModeEx(
D3DDISPLAYMODEEX* pMode,
D3DDISPLAYROTATION* pRotation);
HRESULT GetAdapterLUID(LUID* pLUID);
UINT GetOrdinal() { return m_ordinal; }
Rc<DxvkAdapter> GetDXVKAdapter() { return m_adapter; }
D3D9_VK_FORMAT_MAPPING GetFormatMapping(
D3D9Format Format) const {
return m_d3d9Formats.GetFormatMapping(Format);
}
DxvkFormatInfo GetUnsupportedFormatInfo(
D3D9Format Format) const {
return m_d3d9Formats.GetUnsupportedFormatInfo(Format);
}
private:
HRESULT CheckDeviceVkFormat(
VkFormat Format,
DWORD Usage,
D3DRESOURCETYPE RType);
void CacheModes(D3D9Format Format);
D3D9InterfaceEx* m_parent;
Rc<DxvkAdapter> m_adapter;
UINT m_ordinal;
std::vector<D3DDISPLAYMODEEX> m_modes;
D3D9Format m_modeCacheFormat;
const D3D9VkFormatTable m_d3d9Formats;
};
}

114
src/d3d9/d3d9_buffer.cpp Normal file
View file

@ -0,0 +1,114 @@
#include "d3d9_buffer.h"
namespace dxvk {
////////////////////////
// D3D9VertexBuffer
////////////////////////
D3D9VertexBuffer::D3D9VertexBuffer(
D3D9DeviceEx* pDevice,
const D3D9_BUFFER_DESC* pDesc)
: D3D9VertexBufferBase( pDevice, pDesc ) { }
HRESULT STDMETHODCALLTYPE D3D9VertexBuffer::QueryInterface(
REFIID riid,
void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3DResource9)
|| riid == __uuidof(IDirect3DVertexBuffer9)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9VertexBuffer::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
D3DRESOURCETYPE STDMETHODCALLTYPE D3D9VertexBuffer::GetType() {
return D3DRTYPE_VERTEXBUFFER;
}
HRESULT STDMETHODCALLTYPE D3D9VertexBuffer::GetDesc(
D3DVERTEXBUFFER_DESC* pDesc) {
if (pDesc == nullptr)
return D3DERR_INVALIDCALL;
D3D9_BUFFER_DESC desc;
m_buffer.GetDesc(&desc);
pDesc->Format = static_cast<D3DFORMAT>(desc.Format);
pDesc->Type = desc.Type;
pDesc->Usage = desc.Usage;
pDesc->Pool = desc.Pool;
pDesc->Size = desc.Size;
pDesc->FVF = desc.FVF;
return D3D_OK;
}
//////////////////////
// D3D9IndexBuffer
//////////////////////
D3D9IndexBuffer::D3D9IndexBuffer(
D3D9DeviceEx* pDevice,
const D3D9_BUFFER_DESC* pDesc)
: D3D9IndexBufferBase( pDevice, pDesc ) { }
HRESULT STDMETHODCALLTYPE D3D9IndexBuffer::QueryInterface(
REFIID riid,
void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3DResource9)
|| riid == __uuidof(IDirect3DIndexBuffer9)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9IndexBuffer::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
D3DRESOURCETYPE STDMETHODCALLTYPE D3D9IndexBuffer::GetType() {
return D3DRTYPE_INDEXBUFFER;
}
HRESULT STDMETHODCALLTYPE D3D9IndexBuffer::GetDesc(
D3DINDEXBUFFER_DESC* pDesc) {
if (pDesc == nullptr)
return D3DERR_INVALIDCALL;
D3D9_BUFFER_DESC desc;
m_buffer.GetDesc(&desc);
pDesc->Format = static_cast<D3DFORMAT>(desc.Format);
pDesc->Type = desc.Type;
pDesc->Usage = desc.Usage;
pDesc->Pool = desc.Pool;
pDesc->Size = desc.Size;
return D3D_OK;
}
}

92
src/d3d9/d3d9_buffer.h Normal file
View file

@ -0,0 +1,92 @@
#pragma once
#include "d3d9_resource.h"
#include "d3d9_common_buffer.h"
namespace dxvk {
template <typename... Type>
class D3D9Buffer : public D3D9Resource<Type...> {
public:
D3D9Buffer(
D3D9DeviceEx* pDevice,
const D3D9_BUFFER_DESC* pDesc)
: D3D9Resource<Type...> ( pDevice )
, m_buffer ( pDevice, pDesc ) { }
HRESULT STDMETHODCALLTYPE Lock(
UINT OffsetToLock,
UINT SizeToLock,
void** ppbData,
DWORD Flags) final {
return m_buffer.Lock(
OffsetToLock,
SizeToLock,
ppbData,
Flags);
}
HRESULT STDMETHODCALLTYPE Unlock() final {
return m_buffer.Unlock();
}
D3D9CommonBuffer* GetCommonBuffer() {
return &m_buffer;
}
protected:
D3D9CommonBuffer m_buffer;
};
using D3D9VertexBufferBase = D3D9Buffer<IDirect3DVertexBuffer9>;
class D3D9VertexBuffer final : public D3D9VertexBufferBase {
public:
D3D9VertexBuffer(
D3D9DeviceEx* pDevice,
const D3D9_BUFFER_DESC* pDesc);
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid,
void** ppvObject) final;
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;
HRESULT STDMETHODCALLTYPE GetDesc(
D3DVERTEXBUFFER_DESC* pDesc) final;
};
using D3D9IndexBufferBase = D3D9Buffer<IDirect3DIndexBuffer9>;
class D3D9IndexBuffer final : public D3D9IndexBufferBase {
public:
D3D9IndexBuffer(
D3D9DeviceEx* pDevice,
const D3D9_BUFFER_DESC* pDesc);
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid,
void** ppvObject) final;
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;
HRESULT STDMETHODCALLTYPE GetDesc(
D3DINDEXBUFFER_DESC* pDesc) final;
};
template <typename T>
inline D3D9CommonBuffer* GetCommonBuffer(const T& pResource) {
return pResource != nullptr ? pResource->GetCommonBuffer() : nullptr;
}
}

32
src/d3d9/d3d9_caps.h Normal file
View file

@ -0,0 +1,32 @@
#pragma once
#include "d3d9_include.h"
namespace dxvk::caps {
constexpr uint32_t MaxClipPlanes = 6;
constexpr uint32_t MaxSamplers = 16;
constexpr uint32_t MaxStreams = 16;
constexpr uint32_t MaxSimultaneousTextures = 8;
constexpr uint32_t MaxTextureBlendStages = MaxSimultaneousTextures;
constexpr uint32_t MaxSimultaneousRenderTargets = D3D_MAX_SIMULTANEOUS_RENDERTARGETS;
constexpr uint32_t MaxFloatConstantsVS = 256;
constexpr uint32_t MaxFloatConstantsPS = 224;
constexpr uint32_t MaxOtherConstants = 16;
constexpr uint32_t MaxFloatConstantsSoftware = 8192;
constexpr uint32_t MaxOtherConstantsSoftware = 2048;
constexpr uint32_t InputRegisterCount = 16;
constexpr uint32_t MaxTextureDimension = 16384;
constexpr uint32_t MaxMipLevels = 15;
constexpr uint32_t MaxSubresources = 15 * 6;
constexpr uint32_t MaxTransforms = 10 + 256;
constexpr uint32_t TextureStageCount = MaxSimultaneousTextures;
constexpr uint32_t MaxEnabledLights = 8;
}

View file

@ -0,0 +1,124 @@
#include "d3d9_common_buffer.h"
#include "d3d9_device.h"
#include "d3d9_util.h"
namespace dxvk {
D3D9CommonBuffer::D3D9CommonBuffer(
D3D9DeviceEx* pDevice,
const D3D9_BUFFER_DESC* pDesc)
: m_parent ( pDevice ), m_desc ( *pDesc ) {
m_buffer = CreateBuffer();
if (GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER)
m_stagingBuffer = CreateStagingBuffer();
m_sliceHandle = GetMapBuffer()->getSliceHandle();
}
HRESULT D3D9CommonBuffer::Lock(
UINT OffsetToLock,
UINT SizeToLock,
void** ppbData,
DWORD Flags) {
return m_parent->LockBuffer(
this,
OffsetToLock,
SizeToLock,
ppbData,
Flags);
}
HRESULT D3D9CommonBuffer::Unlock() {
return m_parent->UnlockBuffer(this);
}
void D3D9CommonBuffer::GetDesc(
D3D9_BUFFER_DESC* pDesc) {
*pDesc = m_desc;
}
HRESULT D3D9CommonBuffer::ValidateBufferProperties(const D3D9_BUFFER_DESC* pDesc) {
if (pDesc->Size == 0)
return D3DERR_INVALIDCALL;
return D3D_OK;
}
Rc<DxvkBuffer> D3D9CommonBuffer::CreateBuffer() const {
DxvkBufferCreateInfo info;
info.size = m_desc.Size;
info.usage = 0;
info.stages = 0;
info.access = 0;
VkMemoryPropertyFlags memoryFlags = 0;
if (m_desc.Type == D3DRTYPE_VERTEXBUFFER) {
info.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
info.access |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
if (m_parent->SupportsSWVP()) {
info.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
info.stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
info.access |= VK_ACCESS_SHADER_WRITE_BIT;
}
}
else if (m_desc.Type == D3DRTYPE_INDEXBUFFER) {
info.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
info.access |= VK_ACCESS_INDEX_READ_BIT;
}
if (GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_DIRECT) {
info.stages |= VK_PIPELINE_STAGE_HOST_BIT;
info.access |= VK_ACCESS_HOST_WRITE_BIT;
if (!(m_desc.Usage & D3DUSAGE_WRITEONLY))
info.access |= VK_ACCESS_HOST_READ_BIT;
memoryFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
}
else {
info.stages |= VK_PIPELINE_STAGE_TRANSFER_BIT;
info.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
info.access |= VK_ACCESS_TRANSFER_WRITE_BIT;
memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
}
return m_parent->GetDXVKDevice()->createBuffer(info, memoryFlags);
}
Rc<DxvkBuffer> D3D9CommonBuffer::CreateStagingBuffer() const {
DxvkBufferCreateInfo info;
info.size = m_desc.Size;
info.stages = VK_PIPELINE_STAGE_HOST_BIT
| VK_PIPELINE_STAGE_TRANSFER_BIT;
info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
info.access = VK_ACCESS_HOST_WRITE_BIT
| VK_ACCESS_TRANSFER_READ_BIT;
if (!(m_desc.Usage & D3DUSAGE_WRITEONLY))
info.access |= VK_ACCESS_HOST_READ_BIT;
VkMemoryPropertyFlags memoryFlags =
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
| VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
return m_parent->GetDXVKDevice()->createBuffer(info, memoryFlags);
}
}

View file

@ -0,0 +1,202 @@
#pragma once
#include "../dxvk/dxvk_device.h"
#include "d3d9_device_child.h"
#include "d3d9_format.h"
namespace dxvk {
/**
* \brief Buffer map mode
*/
enum D3D9_COMMON_BUFFER_MAP_MODE {
D3D9_COMMON_BUFFER_MAP_MODE_BUFFER,
D3D9_COMMON_BUFFER_MAP_MODE_DIRECT
};
/**
* \brief Common buffer descriptor
*/
struct D3D9_BUFFER_DESC {
D3DRESOURCETYPE Type;
UINT Size;
DWORD Usage;
D3D9Format Format;
D3DPOOL Pool;
DWORD FVF;
};
/**
* \brief The type of buffer you want to use
*/
enum D3D9_COMMON_BUFFER_TYPE {
D3D9_COMMON_BUFFER_TYPE_MAPPING,
D3D9_COMMON_BUFFER_TYPE_STAGING,
D3D9_COMMON_BUFFER_TYPE_REAL
};
struct D3D9Range {
D3D9Range() { Clear(); }
D3D9Range(uint32_t min, uint32_t max)
: min(min), max(max) { }
bool IsDegenerate() { return min == max; }
void Conjoin(D3D9Range range) {
if (IsDegenerate())
*this = range;
else {
min = std::min(range.min, min);
max = std::max(range.max, max);
}
}
bool Overlaps(D3D9Range range) {
if (IsDegenerate())
return false;
return range.max > min && range.min < max;
}
void Clear() { min = 0; max = 0; }
uint32_t min = 0;
uint32_t max = 0;
};
class D3D9CommonBuffer {
static constexpr VkDeviceSize BufferSliceAlignment = 64;
public:
D3D9CommonBuffer(
D3D9DeviceEx* pDevice,
const D3D9_BUFFER_DESC* pDesc);
HRESULT Lock(
UINT OffsetToLock,
UINT SizeToLock,
void** ppbData,
DWORD Flags);
HRESULT Unlock();
void GetDesc(
D3D9_BUFFER_DESC* pDesc);
D3D9_COMMON_BUFFER_MAP_MODE GetMapMode() const {
return (m_desc.Pool == D3DPOOL_DEFAULT && (m_desc.Usage & (D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY)))
? D3D9_COMMON_BUFFER_MAP_MODE_DIRECT
: D3D9_COMMON_BUFFER_MAP_MODE_BUFFER;
}
template <D3D9_COMMON_BUFFER_TYPE Type>
Rc<DxvkBuffer> GetBuffer() const {
if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_MAPPING)
return GetMapBuffer();
else if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_STAGING)
return GetStagingBuffer();
else //if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_REAL)
return GetRealBuffer();
}
template <D3D9_COMMON_BUFFER_TYPE Type>
DxvkBufferSlice GetBufferSlice() const {
return GetBufferSlice<Type>(0, m_desc.Size);
}
template <D3D9_COMMON_BUFFER_TYPE Type>
DxvkBufferSlice GetBufferSlice(VkDeviceSize offset) const {
return GetBufferSlice<Type>(offset, m_desc.Size - offset);
}
template <D3D9_COMMON_BUFFER_TYPE Type>
DxvkBufferSlice GetBufferSlice(VkDeviceSize offset, VkDeviceSize length) const {
return DxvkBufferSlice(GetBuffer<Type>(), offset, length);
}
DxvkBufferSliceHandle AllocMapSlice() {
return GetMapBuffer()->allocSlice();
}
DxvkBufferSliceHandle DiscardMapSlice() {
m_sliceHandle = GetMapBuffer()->allocSlice();
return m_sliceHandle;
}
DxvkBufferSliceHandle GetMappedSlice() const {
return m_sliceHandle;
}
DWORD GetMapFlags() const { return m_mapFlags; }
void SetMapFlags(DWORD Flags) { m_mapFlags = Flags; }
const D3D9_BUFFER_DESC* Desc() const {
return &m_desc;
}
static HRESULT ValidateBufferProperties(const D3D9_BUFFER_DESC* pDesc);
D3D9Range& LockRange() { return m_lockRange; }
D3D9Range& DirtyRange() { return m_dirtyRange; }
bool GetReadLocked() const { return m_readLocked; }
void SetReadLocked(bool state) { m_readLocked = state; }
uint32_t IncrementLockCount() { return ++m_lockCount; }
uint32_t DecrementLockCount() {
if (m_lockCount == 0)
return 0;
return --m_lockCount;
}
void MarkUploaded() { m_needsUpload = false; }
void MarkNeedsUpload() { m_needsUpload = true; }
bool NeedsUpload() const { return m_needsUpload; }
bool MarkLocked() {
bool locked = m_readLocked;
m_readLocked = true;
return locked;
}
private:
Rc<DxvkBuffer> CreateBuffer() const;
Rc<DxvkBuffer> CreateStagingBuffer() const;
Rc<DxvkBuffer> GetMapBuffer() const {
return m_stagingBuffer != nullptr ? m_stagingBuffer : m_buffer;
}
Rc<DxvkBuffer> GetStagingBuffer() const {
return m_stagingBuffer;
}
Rc<DxvkBuffer> GetRealBuffer() const {
return m_buffer;
}
D3D9DeviceEx* m_parent;
const D3D9_BUFFER_DESC m_desc;
DWORD m_mapFlags;
bool m_readLocked = false;
Rc<DxvkBuffer> m_buffer;
Rc<DxvkBuffer> m_stagingBuffer;
DxvkBufferSliceHandle m_sliceHandle;
D3D9Range m_lockRange;
D3D9Range m_dirtyRange;
uint32_t m_lockCount = 0;
bool m_needsUpload = false;
};
}

View file

@ -0,0 +1,508 @@
#include "d3d9_common_texture.h"
#include "d3d9_util.h"
#include "d3d9_device.h"
#include <algorithm>
namespace dxvk {
D3D9CommonTexture::D3D9CommonTexture(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3DRESOURCETYPE ResourceType,
D3D9_VK_FORMAT_MAPPING Mapping)
: m_device(pDevice), m_desc(*pDesc), m_type(ResourceType), m_mapping(Mapping) {
if (m_desc.Format == D3D9Format::Unknown)
m_desc.Format = (m_desc.Usage & D3DUSAGE_DEPTHSTENCIL)
? D3D9Format::D32
: D3D9Format::X8R8G8B8;
auto pxSize = m_mapping.VideoFormatInfo.MacroPixelSize;
m_adjustedExtent = VkExtent3D{ m_desc.Width / pxSize.width, m_desc.Height / pxSize.height, m_desc.Depth };
m_mapMode = DetermineMapMode();
m_shadow = DetermineShadowState();
if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED) {
try {
m_image = CreatePrimaryImage(ResourceType);
}
catch (const DxvkError& e) {
if (m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP) {
m_desc.Usage &= ~D3DUSAGE_AUTOGENMIPMAP;
m_desc.MipLevels = 1;
m_image = CreatePrimaryImage(ResourceType);
}
else
throw e;
}
CreateInitialViews();
if (!IsManaged()) {
m_size = m_image->memSize();
if (!m_device->ChangeReportedMemory(-m_size))
throw DxvkError("D3D9: Reporting out of memory from tracking.");
}
}
if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM)
CreateBuffers();
}
D3D9CommonTexture::~D3D9CommonTexture() {
if (m_size != 0)
m_device->ChangeReportedMemory(m_size);
}
VkImageSubresource D3D9CommonTexture::GetSubresourceFromIndex(
VkImageAspectFlags Aspect,
UINT Subresource) const {
VkImageSubresource result;
result.aspectMask = Aspect;
result.mipLevel = Subresource % m_desc.MipLevels;
result.arrayLayer = Subresource / m_desc.MipLevels;
return result;
}
HRESULT D3D9CommonTexture::NormalizeTextureProperties(
D3D9DeviceEx* pDevice,
D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING* pMapping) {
auto* options = pDevice->GetOptions();
//////////////////////
// Mapping Validation
*pMapping = pDevice->LookupFormat(pDesc->Format);
// Handle DisableA8RT hack for The Sims 2
if (pDesc->Format == D3D9Format::A8 &&
(pDesc->Usage & D3DUSAGE_RENDERTARGET) &&
options->disableA8RT)
return D3DERR_INVALIDCALL;
// If the mapping is invalid then lets return invalid
// Some edge cases:
// NULL format does not map to anything, but should succeed
// SCRATCH textures can still be made if the device does not support
// the format at all.
if (!pMapping->IsValid() && pDesc->Format != D3D9Format::NULL_FORMAT) {
auto info = pDevice->UnsupportedFormatInfo(pDesc->Format);
if (pDesc->Pool != D3DPOOL_SCRATCH || info.elementSize == 0)
return D3DERR_INVALIDCALL;
}
///////////////////
// Desc Validation
if (pDesc->Width == 0 || pDesc->Height == 0 || pDesc->Depth == 0)
return D3DERR_INVALIDCALL;
if (FAILED(DecodeMultiSampleType(pDesc->MultiSample, pDesc->MultisampleQuality, nullptr)))
return D3DERR_INVALIDCALL;
// Using MANAGED pool with DYNAMIC usage is illegal
if (IsPoolManaged(pDesc->Pool) && (pDesc->Usage & D3DUSAGE_DYNAMIC))
return D3DERR_INVALIDCALL;
// D3DUSAGE_WRITEONLY doesn't apply to textures.
if (pDesc->Usage & D3DUSAGE_WRITEONLY)
return D3DERR_INVALIDCALL;
// RENDERTARGET and DEPTHSTENCIL must be default pool
constexpr DWORD incompatibleUsages = D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL;
if (pDesc->Pool != D3DPOOL_DEFAULT && (pDesc->Usage & incompatibleUsages))
return D3DERR_INVALIDCALL;
// Use the maximum possible mip level count if the supplied
// mip level count is either unspecified (0) or invalid
const uint32_t maxMipLevelCount =
(pDesc->MultiSample <= D3DMULTISAMPLE_NONMASKABLE && !(pDesc->Usage & D3DUSAGE_AUTOGENMIPMAP))
? util::computeMipLevelCount({ pDesc->Width, pDesc->Height, pDesc->Depth })
: 1u;
if (pDesc->MipLevels == 0 || pDesc->MipLevels > maxMipLevelCount)
pDesc->MipLevels = maxMipLevelCount;
return D3D_OK;
}
bool D3D9CommonTexture::CreateBufferSubresource(UINT Subresource) {
if (m_buffers[Subresource] != nullptr)
return false;
DxvkBufferCreateInfo info;
info.size = GetMipSize(Subresource);
info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
info.access = VK_ACCESS_TRANSFER_READ_BIT
| VK_ACCESS_TRANSFER_WRITE_BIT;
if (m_mapping.VideoFormatInfo.FormatType != D3D9VideoFormat_None) {
info.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
info.stages |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
}
VkMemoryPropertyFlags memType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM || IsManaged())
memType |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
m_buffers[Subresource] = m_device->GetDXVKDevice()->createBuffer(info, memType);
m_mappedSlices[Subresource] = m_buffers[Subresource]->getSliceHandle();
return true;
}
VkDeviceSize D3D9CommonTexture::GetMipSize(UINT Subresource) const {
const UINT MipLevel = Subresource % m_desc.MipLevels;
const DxvkFormatInfo formatInfo = m_mapping.FormatColor != VK_FORMAT_UNDEFINED
? *imageFormatInfo(m_mapping.FormatColor)
: m_device->UnsupportedFormatInfo(m_desc.Format);
const VkExtent3D mipExtent = util::computeMipLevelExtent(
m_adjustedExtent, MipLevel);
const VkExtent3D blockCount = util::computeBlockCount(
mipExtent, formatInfo.blockSize);
return formatInfo.elementSize
* blockCount.width
* blockCount.height
* blockCount.depth;
}
Rc<DxvkImage> D3D9CommonTexture::CreatePrimaryImage(D3DRESOURCETYPE ResourceType) const {
DxvkImageCreateInfo imageInfo;
imageInfo.type = GetImageTypeFromResourceType(ResourceType);
imageInfo.format = m_mapping.FormatColor;
imageInfo.flags = 0;
imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
imageInfo.extent.width = m_desc.Width;
imageInfo.extent.height = m_desc.Height;
imageInfo.extent.depth = m_desc.Depth;
imageInfo.numLayers = m_desc.ArraySize;
imageInfo.mipLevels = m_desc.MipLevels;
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT
| VK_IMAGE_USAGE_TRANSFER_DST_BIT
| VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
| m_device->GetEnabledShaderStages();
imageInfo.access = VK_ACCESS_TRANSFER_READ_BIT
| VK_ACCESS_TRANSFER_WRITE_BIT
| VK_ACCESS_SHADER_READ_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.layout = VK_IMAGE_LAYOUT_GENERAL;
if (m_mapping.VideoFormatInfo.FormatType != D3D9VideoFormat_None) {
imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
imageInfo.stages |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
}
DecodeMultiSampleType(m_desc.MultiSample, m_desc.MultisampleQuality, &imageInfo.sampleCount);
// The image must be marked as mutable if it can be reinterpreted
// by a view with a different format. Depth-stencil formats cannot
// be reinterpreted in Vulkan, so we'll ignore those.
auto formatProperties = imageFormatInfo(m_mapping.FormatColor);
bool isMutable = m_mapping.FormatSrgb != VK_FORMAT_UNDEFINED;
bool isColorFormat = (formatProperties->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
if (isMutable && isColorFormat) {
imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
imageInfo.viewFormatCount = 2;
imageInfo.viewFormats = m_mapping.Formats;
}
if (m_desc.Usage & D3DUSAGE_RENDERTARGET || m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP) {
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
imageInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
imageInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
}
if (m_desc.Usage & D3DUSAGE_DEPTHSTENCIL) {
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
imageInfo.stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
imageInfo.access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
}
if (ResourceType == D3DRTYPE_CUBETEXTURE)
imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
// Some image formats (i.e. the R32G32B32 ones) are
// only supported with linear tiling on most GPUs
if (!CheckImageSupport(&imageInfo, VK_IMAGE_TILING_OPTIMAL))
imageInfo.tiling = VK_IMAGE_TILING_LINEAR;
// We must keep LINEAR images in GENERAL layout, but we
// can choose a better layout for the image based on how
// it is going to be used by the game.
if (imageInfo.tiling == VK_IMAGE_TILING_OPTIMAL)
imageInfo.layout = OptimizeLayout(imageInfo.usage);
// For some formats, we need to enable render target
// capabilities if available, but these should
// in no way affect the default image layout
imageInfo.usage |= EnableMetaCopyUsage(imageInfo.format, imageInfo.tiling);
// Check if we can actually create the image
if (!CheckImageSupport(&imageInfo, imageInfo.tiling)) {
throw DxvkError(str::format(
"D3D9: Cannot create texture:",
"\n Type: ", std::hex, ResourceType,
"\n Format: ", m_desc.Format,
"\n Extent: ", m_desc.Width,
"x", m_desc.Height,
"x", m_desc.Depth,
"\n Samples: ", m_desc.MultiSample,
"\n Layers: ", m_desc.ArraySize,
"\n Levels: ", m_desc.MipLevels,
"\n Usage: ", std::hex, m_desc.Usage,
"\n Pool: ", std::hex, m_desc.Pool));
}
return m_device->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
Rc<DxvkImage> D3D9CommonTexture::CreateResolveImage() const {
DxvkImageCreateInfo imageInfo = m_image->info();
imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
return m_device->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
void D3D9CommonTexture::RecreateSampledView(UINT Lod) {
// This will be a no-op for SYSTEMMEM types given we
// don't expose the cap to allow texturing with them.
if (unlikely(m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM))
return;
const D3D9_VK_FORMAT_MAPPING formatInfo = m_device->LookupFormat(m_desc.Format);
m_views.Sample = CreateColorViewPair(formatInfo, AllLayers, VK_IMAGE_USAGE_SAMPLED_BIT, Lod);
}
BOOL D3D9CommonTexture::DetermineShadowState() const {
static std::array<D3D9Format, 3> blacklist = {
D3D9Format::INTZ, D3D9Format::DF16, D3D9Format::DF24
};
return IsDepthFormat(m_desc.Format)
&& std::find(blacklist.begin(), blacklist.end(), m_desc.Format) == blacklist.end();
}
BOOL D3D9CommonTexture::CheckImageSupport(
const DxvkImageCreateInfo* pImageInfo,
VkImageTiling Tiling) const {
const Rc<DxvkAdapter> adapter = m_device->GetDXVKDevice()->adapter();
VkImageFormatProperties formatProps = { };
VkResult status = adapter->imageFormatProperties(
pImageInfo->format, pImageInfo->type, Tiling,
pImageInfo->usage, pImageInfo->flags, formatProps);
if (status != VK_SUCCESS)
return FALSE;
return (pImageInfo->extent.width <= formatProps.maxExtent.width)
&& (pImageInfo->extent.height <= formatProps.maxExtent.height)
&& (pImageInfo->extent.depth <= formatProps.maxExtent.depth)
&& (pImageInfo->numLayers <= formatProps.maxArrayLayers)
&& (pImageInfo->mipLevels <= formatProps.maxMipLevels)
&& (pImageInfo->sampleCount & formatProps.sampleCounts);
}
VkImageUsageFlags D3D9CommonTexture::EnableMetaCopyUsage(
VkFormat Format,
VkImageTiling Tiling) const {
VkFormatFeatureFlags requestedFeatures = 0;
if (Format == VK_FORMAT_D16_UNORM || Format == VK_FORMAT_D32_SFLOAT)
requestedFeatures |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (Format == VK_FORMAT_R16_UNORM || Format == VK_FORMAT_R32_SFLOAT)
requestedFeatures |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
if (requestedFeatures == 0)
return 0;
// Enable usage flags for all supported and requested features
VkFormatProperties properties = m_device->GetDXVKDevice()->adapter()->formatProperties(Format);
requestedFeatures &= Tiling == VK_IMAGE_TILING_OPTIMAL
? properties.optimalTilingFeatures
: properties.linearTilingFeatures;
VkImageUsageFlags requestedUsage = 0;
if (requestedFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
requestedUsage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (requestedFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)
requestedUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
return requestedUsage;
}
VkImageType D3D9CommonTexture::GetImageTypeFromResourceType(D3DRESOURCETYPE Type) {
switch (Type) {
case D3DRTYPE_TEXTURE: return VK_IMAGE_TYPE_2D;
case D3DRTYPE_VOLUMETEXTURE: return VK_IMAGE_TYPE_3D;
case D3DRTYPE_CUBETEXTURE: return VK_IMAGE_TYPE_2D;
default: throw DxvkError("D3D9CommonTexture: Unhandled resource type");
}
}
VkImageViewType D3D9CommonTexture::GetImageViewTypeFromResourceType(
D3DRESOURCETYPE Dimension,
UINT Layer) {
switch (Dimension) {
case D3DRTYPE_TEXTURE: return VK_IMAGE_VIEW_TYPE_2D;
case D3DRTYPE_VOLUMETEXTURE: return VK_IMAGE_VIEW_TYPE_3D;
case D3DRTYPE_CUBETEXTURE: return Layer == AllLayers
? VK_IMAGE_VIEW_TYPE_CUBE
: VK_IMAGE_VIEW_TYPE_2D;
default: throw DxvkError("D3D9CommonTexture: Unhandled resource type");
}
}
VkImageLayout D3D9CommonTexture::OptimizeLayout(VkImageUsageFlags Usage) {
const VkImageUsageFlags usageFlags = Usage;
// Filter out unnecessary flags. Transfer operations
// are handled by the backend in a transparent manner.
Usage &= ~(VK_IMAGE_USAGE_TRANSFER_DST_BIT
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
// If the image is used only as an attachment, we never
// have to transform the image back to a different layout
if (Usage == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
if (Usage == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
Usage &= ~(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
| VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
// If the image is used for reading but not as a storage
// image, we can optimize the image for texture access
if (Usage == VK_IMAGE_USAGE_SAMPLED_BIT) {
return usageFlags & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT
? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL
: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
// Otherwise, we have to stick with the default layout
return VK_IMAGE_LAYOUT_GENERAL;
}
Rc<DxvkImageView> D3D9CommonTexture::CreateView(
D3D9_VK_FORMAT_MAPPING FormatInfo,
UINT Layer,
VkImageUsageFlags UsageFlags,
UINT Lod,
BOOL Srgb) {
DxvkImageViewCreateInfo viewInfo;
viewInfo.format = PickSRGB(FormatInfo.FormatColor, FormatInfo.FormatSrgb, Srgb);
viewInfo.aspect = imageFormatInfo(viewInfo.format)->aspectMask;
viewInfo.swizzle = FormatInfo.Swizzle;
viewInfo.usage = UsageFlags;
viewInfo.type = GetImageViewTypeFromResourceType(m_type, Layer);
viewInfo.minLevel = Lod;
viewInfo.numLevels = m_desc.MipLevels - Lod;
viewInfo.minLayer = Layer == AllLayers ? 0 : Layer;
viewInfo.numLayers = Layer == AllLayers ? m_desc.ArraySize : 1;
// Remove the stencil aspect if we are trying to create a regular image
// view of a depth stencil format
if (UsageFlags != VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
viewInfo.aspect &= ~VK_IMAGE_ASPECT_STENCIL_BIT;
if (UsageFlags == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT ||
UsageFlags == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
viewInfo.numLevels = 1;
// Remove swizzle on depth views.
if (UsageFlags == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
viewInfo.swizzle = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
// Create the underlying image view object
return m_device->GetDXVKDevice()->createImageView(GetImage(), viewInfo);
}
D3D9ColorView D3D9CommonTexture::CreateColorViewPair(
D3D9_VK_FORMAT_MAPPING FormatInfo,
UINT Layer,
VkImageUsageFlags UsageFlags,
UINT Lod) {
D3D9ColorView pair;
pair.Color = CreateView(FormatInfo, Layer, UsageFlags, Lod, FALSE);
if (FormatInfo.FormatSrgb != VK_FORMAT_UNDEFINED)
pair.Srgb = CreateView(FormatInfo, Layer, UsageFlags, Lod, TRUE);
else
pair.Srgb = pair.Color;
return pair;
}
void D3D9CommonTexture::CreateInitialViews() {
const D3D9_VK_FORMAT_MAPPING formatInfo = m_device->LookupFormat(m_desc.Format);
m_views.Sample = CreateColorViewPair(formatInfo, AllLayers, VK_IMAGE_USAGE_SAMPLED_BIT, 0);
for (uint32_t i = 0; i < m_desc.ArraySize; i++) {
for (uint32_t j = 0; j < m_desc.MipLevels; j++)
m_views.SubresourceSample[i][j] = CreateColorViewPair(formatInfo, i, VK_IMAGE_USAGE_SAMPLED_BIT, j);
}
if (m_desc.Usage & D3DUSAGE_RENDERTARGET) {
for (uint32_t i = 0; i < m_desc.ArraySize; i++) {
for (uint32_t j = 0; j < m_desc.MipLevels; j++)
m_views.SubresourceRenderTarget[i][j] = CreateColorViewPair(formatInfo, i, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, j);
}
}
if (m_desc.Usage & D3DUSAGE_DEPTHSTENCIL) {
for (uint32_t i = 0; i < m_desc.ArraySize; i++) {
for (uint32_t j = 0; j < m_desc.MipLevels; j++)
m_views.SubresourceDepth[i][j] = CreateView(formatInfo, i, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, j, FALSE);
}
}
}
}

View file

@ -0,0 +1,431 @@
#pragma once
#include "d3d9_format.h"
#include "d3d9_util.h"
#include "d3d9_caps.h"
#include "../dxvk/dxvk_device.h"
namespace dxvk {
class D3D9DeviceEx;
/**
* \brief Image memory mapping mode
*
* Determines how exactly \c LockBox will
* behave when mapping an image.
*/
enum D3D9_COMMON_TEXTURE_MAP_MODE {
D3D9_COMMON_TEXTURE_MAP_MODE_NONE, ///< No mapping available
D3D9_COMMON_TEXTURE_MAP_MODE_BACKED, ///< Mapped image through buffer
D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM, ///< Only a buffer - no image
};
/**
* \brief Common texture description
*
* Contains all members that can be
* defined for 2D, Cube and 3D textures.
*/
struct D3D9_COMMON_TEXTURE_DESC {
UINT Width;
UINT Height;
UINT Depth;
UINT ArraySize;
UINT MipLevels;
DWORD Usage;
D3D9Format Format;
D3DPOOL Pool;
BOOL Discard;
D3DMULTISAMPLE_TYPE MultiSample;
DWORD MultisampleQuality;
};
struct D3D9ColorView {
inline Rc<DxvkImageView> Pick(bool Srgb) const {
return Srgb ? this->Srgb : this->Color;
}
Rc<DxvkImageView> Color;
Rc<DxvkImageView> Srgb;
};
struct D3D9ViewSet {
D3D9ColorView Sample;
std::array<
std::array<D3D9ColorView, 15>, 6> SubresourceSample;
std::array<
std::array<D3D9ColorView, 15>, 6> SubresourceRenderTarget;
std::array<
std::array<Rc<DxvkImageView>, 15>, 6> SubresourceDepth;
bool Hazardous = false;
VkImageLayout GetRTLayout() const {
return SubresourceRenderTarget[0][0].Color != nullptr
&& SubresourceRenderTarget[0][0].Color->imageInfo().tiling == VK_IMAGE_TILING_OPTIMAL
&& !Hazardous
? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
: VK_IMAGE_LAYOUT_GENERAL;
}
VkImageLayout GetDepthLayout() const {
return SubresourceDepth[0][0] != nullptr
&& SubresourceDepth[0][0]->imageInfo().tiling == VK_IMAGE_TILING_OPTIMAL
? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
: VK_IMAGE_LAYOUT_GENERAL;
}
};
template <typename T>
using D3D9SubresourceArray = std::array<T, caps::MaxSubresources>;
class D3D9CommonTexture {
public:
D3D9CommonTexture(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3DRESOURCETYPE ResourceType,
D3D9_VK_FORMAT_MAPPING Mapping);
~D3D9CommonTexture();
/**
* \brief Device
* \returns The parent device
*/
D3D9DeviceEx* Device() const {
return m_device;
}
/**
* \brief Texture properties
*
* The returned data can be used to fill in
* \c D3D11_TEXTURE2D_DESC and similar structs.
* \returns Pointer to texture description
*/
const D3D9_COMMON_TEXTURE_DESC* Desc() const {
return &m_desc;
}
/**
* \brief Vulkan Format
* \returns The Vulkan format of the resource
*/
const D3D9_VK_FORMAT_MAPPING GetFormatMapping() const {
return m_mapping;
}
/**
* \brief Counts number of subresources
* \returns Number of subresources
*/
UINT CountSubresources() const {
return m_desc.ArraySize * m_desc.MipLevels;
}
/**
* \brief Map mode
* \returns Map mode
*/
D3D9_COMMON_TEXTURE_MAP_MODE GetMapMode() const {
return m_mapMode;
}
/**
* \brief The DXVK image
* Note, this will be nullptr if the map mode is D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM
* \returns The DXVK image
*/
Rc<DxvkImage> GetImage() const {
return m_image;
}
/**
* \brief Get a copy of the main image, but with a single sample
* This function will allocate/reuse an image with the same info
* as the main image
* \returns An image with identical info, but 1 sample
*/
Rc<DxvkImage> GetResolveImage() {
if (unlikely(m_resolveImage == nullptr))
m_resolveImage = CreateResolveImage();
return m_resolveImage;
}
Rc<DxvkBuffer> GetBuffer(UINT Subresource) {
return m_buffers[Subresource];
}
DxvkBufferSliceHandle GetMappedSlice(UINT Subresource) {
return m_mappedSlices[Subresource];
}
DxvkBufferSliceHandle DiscardMapSlice(UINT Subresource) {
DxvkBufferSliceHandle handle = m_buffers[Subresource]->allocSlice();
m_mappedSlices[Subresource] = handle;
return handle;
}
/**
* \brief Computes subresource from the subresource index
*
* Used by some functions that operate on only
* one subresource, such as \c UpdateSurface.
* \param [in] Aspect The image aspect
* \param [in] Subresource Subresource index
* \returns The Vulkan image subresource
*/
VkImageSubresource GetSubresourceFromIndex(
VkImageAspectFlags Aspect,
UINT Subresource) const;
/**
* \brief Normalizes and validates texture description
*
* Fills in undefined values and validates the texture
* parameters. Any error returned by this method should
* be forwarded to the application.
* \param [in,out] pDesc Texture description
* \returns \c S_OK if the parameters are valid
*/
static HRESULT NormalizeTextureProperties(
D3D9DeviceEx* pDevice,
D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING* pMapping);
/**
* \brief Lock Flags
* Set the lock flags for a given subresource
*/
void SetLockFlags(UINT Subresource, DWORD Flags) {
m_lockFlags[Subresource] = Flags;
}
/**
* \brief Lock Flags
* \returns The log flags for a given subresource
*/
DWORD GetLockFlags(UINT Subresource) const {
return m_lockFlags[Subresource];
}
/**
* \brief Shadow
* \returns Whether the texture is to be depth compared
*/
bool IsShadow() const {
return m_shadow;
}
/**
* \brief Subresource
* \returns The subresource idx of a given face and mip level
*/
UINT CalcSubresource(UINT Face, UINT MipLevel) const {
return Face * m_desc.MipLevels + MipLevel;
}
/**
* \brief Creates buffers
* Creates mapping and staging buffers for all subresources
* allocates new buffers if necessary
*/
void CreateBuffers() {
const uint32_t count = CountSubresources();
for (uint32_t i = 0; i < count; i++)
CreateBufferSubresource(i);
}
/**
* \brief Creates a buffer
* Creates mapping and staging buffers for a given subresource
* allocates new buffers if necessary
* \returns Whether an allocation happened
*/
bool CreateBufferSubresource(UINT Subresource);
/**
* \brief Destroys a buffer
* Destroys mapping and staging buffers for a given subresource
*/
void DestroyBufferSubresource(UINT Subresource) {
m_buffers[Subresource] = nullptr;
SetDirty(Subresource, true);
}
bool IsDynamic() const {
return m_desc.Usage & D3DUSAGE_DYNAMIC;
}
/**
* \brief Managed
* \returns Whether a resource is managed (pool) or not
*/
bool IsManaged() const {
return IsPoolManaged(m_desc.Pool);
}
/**
* \brief Render Target
* \returns Whether a resource is a render target or not
*/
bool IsRenderTarget() const {
return m_desc.Usage & D3DUSAGE_RENDERTARGET;
}
/**
* \brief Autogen Mipmap
* \returns Whether the texture is to have automatic mip generation
*/
bool IsAutomaticMip() const {
return m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP;
}
/**
* \brief Autogen Mipmap
* \returns Whether the texture is to have automatic mip generation
*/
const D3D9ViewSet& GetViews() const {
return m_views;
}
/**
* \brief Recreate main image view
* Recreates the main view of the sampler w/ a specific LOD.
* SetLOD only works on MANAGED textures so this is A-okay.
*/
void RecreateSampledView(UINT Lod);
/**
* \brief Extent
* \returns The extent of the top-level mip
*/
VkExtent3D GetExtent() const {
return m_adjustedExtent;
}
/**
* \brief Mip Extent
* \returns The extent of a mip or subresource
*/
VkExtent3D GetExtentMip(UINT Subresource) const {
UINT MipLevel = Subresource % m_desc.MipLevels;
return util::computeMipLevelExtent(GetExtent(), MipLevel);
}
bool MarkHazardous() {
return std::exchange(m_views.Hazardous, true);
}
D3DRESOURCETYPE GetType() {
return m_type;
}
const D3D9_VK_FORMAT_MAPPING& GetMapping() { return m_mapping; }
bool MarkLocked(UINT Subresource, bool value) { return std::exchange(m_locked[Subresource], value); }
bool SetDirty(UINT Subresource, bool value) { return std::exchange(m_dirty[Subresource], value); }
void MarkAllDirty() { for (uint32_t i = 0; i < m_dirty.size(); i++) m_dirty[i] = true; }
private:
D3D9DeviceEx* m_device;
D3D9_COMMON_TEXTURE_DESC m_desc;
D3DRESOURCETYPE m_type;
D3D9_COMMON_TEXTURE_MAP_MODE m_mapMode;
Rc<DxvkImage> m_image;
Rc<DxvkImage> m_resolveImage;
D3D9SubresourceArray<
Rc<DxvkBuffer>> m_buffers;
D3D9SubresourceArray<
DxvkBufferSliceHandle> m_mappedSlices;
D3D9SubresourceArray<DWORD> m_lockFlags;
D3D9ViewSet m_views;
D3D9_VK_FORMAT_MAPPING m_mapping;
VkExtent3D m_adjustedExtent;
bool m_shadow; //< Depth Compare-ness
int64_t m_size = 0;
bool m_systemmemModified = false;
D3D9SubresourceArray<
bool> m_locked = { };
D3D9SubresourceArray<
bool> m_dirty = { };
/**
* \brief Mip level
* \returns Size of packed mip level in bytes
*/
VkDeviceSize GetMipSize(UINT Subresource) const;
Rc<DxvkImage> CreatePrimaryImage(D3DRESOURCETYPE ResourceType) const;
Rc<DxvkImage> CreateResolveImage() const;
BOOL DetermineShadowState() const;
BOOL CheckImageSupport(
const DxvkImageCreateInfo* pImageInfo,
VkImageTiling Tiling) const;
VkImageUsageFlags EnableMetaCopyUsage(
VkFormat Format,
VkImageTiling Tiling) const;
D3D9_COMMON_TEXTURE_MAP_MODE DetermineMapMode() const {
if (m_desc.Format == D3D9Format::NULL_FORMAT)
return D3D9_COMMON_TEXTURE_MAP_MODE_NONE;
if (m_desc.Pool == D3DPOOL_SYSTEMMEM || m_desc.Pool == D3DPOOL_SCRATCH)
return D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM;
return D3D9_COMMON_TEXTURE_MAP_MODE_BACKED;
}
static VkImageType GetImageTypeFromResourceType(
D3DRESOURCETYPE Dimension);
static VkImageViewType GetImageViewTypeFromResourceType(
D3DRESOURCETYPE Dimension,
UINT Layer);
static VkImageLayout OptimizeLayout(
VkImageUsageFlags Usage);
static constexpr UINT AllLayers = UINT32_MAX;
Rc<DxvkImageView> CreateView(
D3D9_VK_FORMAT_MAPPING FormatInfo,
UINT Layer,
VkImageUsageFlags UsageFlags,
UINT Lod,
BOOL Srgb);
D3D9ColorView CreateColorViewPair(
D3D9_VK_FORMAT_MAPPING FormatInfo,
UINT Layer,
VkImageUsageFlags UsageFlags,
UINT Lod);
void CreateInitialViews();
};
}

View file

@ -0,0 +1,26 @@
#pragma once
#include <cstdint>
#include "d3d9_caps.h"
namespace dxvk {
struct D3D9ConstantLayout {
uint32_t floatCount;
uint32_t intCount;
uint32_t boolCount;
uint32_t bitmaskCount;
uint32_t floatSize() const { return floatCount * 4 * sizeof(float); }
uint32_t intSize() const { return intCount * 4 * sizeof(int); }
uint32_t bitmaskSize() const { return bitmaskCount * 1 * sizeof(uint32_t); }
uint32_t floatOffset() const { return 0; }
uint32_t intOffset() const { return floatOffset() + floatSize(); }
uint32_t bitmaskOffset() const { return intOffset() + intSize(); }
uint32_t totalSize() const { return floatSize() + intSize() + bitmaskSize(); }
};
}

View file

@ -0,0 +1,47 @@
#pragma once
#include "d3d9_caps.h"
#include "../dxvk/dxvk_buffer.h"
#include "../dxso/dxso_isgn.h"
#include "../util/util_math.h"
#include "../util/util_vector.h"
#include <cstdint>
namespace dxvk {
enum class D3D9ConstantType {
Float,
Int,
Bool
};
// We make an assumption later based on the packing of this struct for copying.
struct D3D9ShaderConstantsVSSoftware {
Vector4 fConsts[caps::MaxFloatConstantsSoftware];
Vector4i iConsts[caps::MaxOtherConstantsSoftware];
uint32_t bConsts[caps::MaxOtherConstantsSoftware / 32];
};
struct D3D9ShaderConstantsVSHardware {
Vector4 fConsts[caps::MaxFloatConstantsVS];
Vector4i iConsts[caps::MaxOtherConstants];
uint32_t bConsts[1];
};
struct D3D9ShaderConstantsPS {
Vector4 fConsts[caps::MaxFloatConstantsPS];
Vector4i iConsts[caps::MaxOtherConstants];
uint32_t bConsts[1];
};
struct D3D9ConstantSets {
Rc<DxvkBuffer> buffer;
const DxsoShaderMetaInfo* meta = nullptr;
bool dirty = true;
};
}

42
src/d3d9/d3d9_cursor.cpp Normal file
View file

@ -0,0 +1,42 @@
#include "d3d9_cursor.h"
#include <utility>
namespace dxvk {
void D3D9Cursor::UpdateCursor(int X, int Y) {
::SetCursorPos(X, Y);
}
BOOL D3D9Cursor::ShowCursor(BOOL bShow) {
::SetCursor(bShow ? m_hCursor : nullptr);
return std::exchange(m_visible, bShow);
}
HRESULT D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) {
DWORD mask[32];
std::memset(mask, ~0, sizeof(mask));
ICONINFO info;
info.fIcon = FALSE;
info.xHotspot = XHotSpot;
info.yHotspot = YHotSpot;
info.hbmMask = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 1, mask);
info.hbmColor = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 32, &bitmap[0]);
if (m_hCursor != nullptr)
::DestroyCursor(m_hCursor);
m_hCursor = ::CreateIconIndirect(&info);
::DeleteObject(info.hbmMask);
::DeleteObject(info.hbmColor);
ShowCursor(m_visible);
return D3D_OK;
}
}

33
src/d3d9/d3d9_cursor.h Normal file
View file

@ -0,0 +1,33 @@
#pragma once
#include "d3d9_include.h"
namespace dxvk {
constexpr uint32_t HardwareCursorWidth = 32u;
constexpr uint32_t HardwareCursorHeight = 32u;
constexpr uint32_t HardwareCursorFormatSize = 4u;
constexpr uint32_t HardwareCursorPitch = HardwareCursorWidth * HardwareCursorFormatSize;
// Format Size of 4 bytes (ARGB)
using CursorBitmap = uint8_t[HardwareCursorHeight * HardwareCursorPitch];
class D3D9Cursor {
public:
void UpdateCursor(int X, int Y);
BOOL ShowCursor(BOOL bShow);
HRESULT SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap);
private:
BOOL m_visible = FALSE;
HCURSOR m_hCursor = nullptr;
};
}

6530
src/d3d9/d3d9_device.cpp Normal file

File diff suppressed because it is too large Load diff

1154
src/d3d9/d3d9_device.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,61 @@
#pragma once
#include "d3d9_include.h"
namespace dxvk {
class D3D9DeviceEx;
template <typename Base>
class D3D9DeviceChild : public ComObjectClamp<Base> {
public:
D3D9DeviceChild(D3D9DeviceEx* pDevice)
: m_parent( pDevice ) { }
ULONG STDMETHODCALLTYPE AddRef() {
uint32_t refCount = this->m_refCount++;
if (unlikely(!refCount)) {
this->AddRefPrivate();
GetDevice()->AddRef();
}
return refCount + 1;
}
ULONG STDMETHODCALLTYPE Release() {
uint32_t refCount = --this->m_refCount;
if (unlikely(!refCount)) {
auto* pDevice = GetDevice();
this->ReleasePrivate();
pDevice->Release();
}
return refCount;
}
HRESULT STDMETHODCALLTYPE GetDevice(IDirect3DDevice9** ppDevice) {
InitReturnPtr(ppDevice);
if (ppDevice == nullptr)
return D3DERR_INVALIDCALL;
*ppDevice = ref(GetDevice());
return D3D_OK;
}
IDirect3DDevice9Ex* GetDevice() {
return reinterpret_cast<IDirect3DDevice9Ex*>(m_parent);
}
D3D9DeviceEx* GetParent() {
return m_parent;
}
protected:
D3D9DeviceEx* m_parent;
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,247 @@
#pragma once
#include "d3d9_include.h"
#include "d3d9_caps.h"
#include "../dxvk/dxvk_shader.h"
#include "../dxso/dxso_isgn.h"
#include <unordered_map>
#include <bitset>
namespace dxvk {
class D3D9DeviceEx;
class SpirvModule;
struct D3D9Options;
struct D3D9FogContext {
// General inputs...
bool IsPixel;
bool RangeFog;
uint32_t RenderState;
uint32_t vPos;
uint32_t vFog;
uint32_t oColor;
bool HasFogInput;
};
struct D3D9FixedFunctionOptions {
D3D9FixedFunctionOptions(const D3D9Options* options);
bool invariantPosition;
};
// Returns new oFog if VS
// Returns new oColor if PS
uint32_t DoFixedFunctionFog(SpirvModule& spvModule, const D3D9FogContext& fogCtx);
// Returns a render state block
uint32_t SetupRenderStateBlock(SpirvModule& spvModule);
struct D3D9PointSizeInfoVS {
uint32_t defaultValue;
uint32_t min;
uint32_t max;
};
// Default point size and point scale magic!
D3D9PointSizeInfoVS GetPointSizeInfoVS(SpirvModule& spvModule, uint32_t vPos, uint32_t vtx, uint32_t perVertPointSize, uint32_t rsBlock);
struct D3D9PointSizeInfoPS {
uint32_t isSprite;
};
D3D9PointSizeInfoPS GetPointSizeInfoPS(SpirvModule& spvModule, uint32_t rsBlock);
uint32_t GetPointCoord(SpirvModule& spvModule, std::vector<uint32_t>& entryPointInterfaces);
uint32_t GetSharedConstants(SpirvModule& spvModule);
constexpr uint32_t TCIOffset = 16;
constexpr uint32_t TCIMask = 0b111 << TCIOffset;
enum D3D9FF_VertexBlendMode {
D3D9FF_VertexBlendMode_Disabled,
D3D9FF_VertexBlendMode_Normal,
D3D9FF_VertexBlendMode_Tween,
};
struct D3D9FFShaderKeyVSData {
union {
struct {
uint32_t TexcoordIndices : 24;
uint32_t HasPositionT : 1;
uint32_t HasColor0 : 1; // Diffuse
uint32_t HasColor1 : 1; // Specular
uint32_t HasPointSize : 1;
uint32_t UseLighting : 1;
uint32_t NormalizeNormals : 1;
uint32_t LocalViewer : 1;
uint32_t RangeFog : 1;
uint32_t TexcoordFlags : 24;
uint32_t DiffuseSource : 2;
uint32_t AmbientSource : 2;
uint32_t SpecularSource : 2;
uint32_t EmissiveSource : 2;
uint32_t TransformFlags : 24;
uint32_t LightCount : 4;
uint32_t TexcoordDeclMask : 24;
uint32_t HasFog : 1;
uint32_t VertexBlendMode : 2;
uint32_t VertexBlendIndexed : 1;
uint32_t VertexBlendCount : 3;
} Contents;
uint32_t Primitive[4];
};
};
struct D3D9FFShaderKeyVS {
D3D9FFShaderKeyVS() {
// memcmp safety
std::memset(&Data, 0, sizeof(Data));
}
D3D9FFShaderKeyVSData Data;
};
constexpr uint32_t TextureArgCount = 3;
struct D3D9FFShaderStage {
union {
struct {
uint32_t ColorOp : 5;
uint32_t ColorArg0 : 6;
uint32_t ColorArg1 : 6;
uint32_t ColorArg2 : 6;
uint32_t AlphaOp : 5;
uint32_t AlphaArg0 : 6;
uint32_t AlphaArg1 : 6;
uint32_t AlphaArg2 : 6;
uint32_t Type : 2;
uint32_t ResultIsTemp : 1;
uint32_t Projected : 1;
uint32_t ProjectedCount : 3;
// Included in here, read from Stage 0 for packing reasons
// Affects all stages.
uint32_t GlobalSpecularEnable : 1;
uint32_t GlobalFlatShade : 1;
} Contents;
uint32_t Primitive[2];
};
};
struct D3D9FFShaderKeyFS {
D3D9FFShaderKeyFS() {
// memcmp safety
std::memset(Stages, 0, sizeof(Stages));
// Normalize this. DISABLE != 0.
for (uint32_t i = 0; i < caps::TextureStageCount; i++) {
Stages[i].Contents.ColorOp = D3DTOP_DISABLE;
Stages[i].Contents.AlphaOp = D3DTOP_DISABLE;
}
}
D3D9FFShaderStage Stages[caps::TextureStageCount];
};
struct D3D9FFShaderKeyHash {
size_t operator () (const D3D9FFShaderKeyVS& key) const;
size_t operator () (const D3D9FFShaderKeyFS& key) const;
};
bool operator == (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b);
bool operator != (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b);
bool operator == (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b);
bool operator != (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b);
struct D3D9FFShaderKeyEq {
bool operator () (const D3D9FFShaderKeyVS& a, const D3D9FFShaderKeyVS& b) const;
bool operator () (const D3D9FFShaderKeyFS& a, const D3D9FFShaderKeyFS& b) const;
};
class D3D9FFShader {
public:
D3D9FFShader(
D3D9DeviceEx* pDevice,
const D3D9FFShaderKeyVS& Key);
D3D9FFShader(
D3D9DeviceEx* pDevice,
const D3D9FFShaderKeyFS& Key);
template <typename T>
void Dump(const T& Key, const std::string& Name);
Rc<DxvkShader> GetShader() const {
return m_shader;
}
private:
Rc<DxvkShader> m_shader;
DxsoIsgn m_isgn;
};
class D3D9FFShaderModuleSet : public RcObject {
public:
D3D9FFShader GetShaderModule(
D3D9DeviceEx* pDevice,
const D3D9FFShaderKeyVS& ShaderKey);
D3D9FFShader GetShaderModule(
D3D9DeviceEx* pDevice,
const D3D9FFShaderKeyFS& ShaderKey);
private:
std::unordered_map<
D3D9FFShaderKeyVS,
D3D9FFShader,
D3D9FFShaderKeyHash, D3D9FFShaderKeyEq> m_vsModules;
std::unordered_map<
D3D9FFShaderKeyFS,
D3D9FFShader,
D3D9FFShaderKeyHash, D3D9FFShaderKeyEq> m_fsModules;
};
inline const DxsoIsgn& GetFixedFunctionIsgn() {
extern DxsoIsgn g_ffIsgn;
return g_ffIsgn;
}
}

500
src/d3d9/d3d9_format.cpp Normal file
View file

@ -0,0 +1,500 @@
#include "d3d9_format.h"
namespace dxvk {
// It is also worth noting that the msb/lsb-ness is flipped between VK and D3D9.
D3D9_VK_FORMAT_MAPPING ConvertFormatUnfixed(D3D9Format Format) {
switch (Format) {
case D3D9Format::Unknown: return {};
case D3D9Format::R8G8B8: return {}; // Unsupported
case D3D9Format::A8R8G8B8: return {
VK_FORMAT_B8G8R8A8_UNORM,
VK_FORMAT_B8G8R8A8_SRGB,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::X8R8G8B8: return {
VK_FORMAT_B8G8R8A8_UNORM,
VK_FORMAT_B8G8R8A8_SRGB,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::R5G6B5: return {
VK_FORMAT_R5G6B5_UNORM_PACK16,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT};
case D3D9Format::X1R5G5B5: return {
VK_FORMAT_A1R5G5B5_UNORM_PACK16,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::A1R5G5B5: return {
VK_FORMAT_A1R5G5B5_UNORM_PACK16,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::A4R4G4B4: return {
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_R }};
case D3D9Format::R3G3B2: return {}; // Unsupported
case D3D9Format::A8: return {
VK_FORMAT_R8_UNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO,
VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R }};
case D3D9Format::A8R3G3B2: return {}; // Unsupported
case D3D9Format::X4R4G4B4: return {
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::A2B10G10R10: return {
VK_FORMAT_A2B10G10R10_UNORM_PACK32, // The A2 is out of place here. This should be investigated.
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::A8B8G8R8: return {
VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::X8B8G8R8: return {
VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::G16R16: return {
VK_FORMAT_R16G16_UNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::A2R10G10B10: return {
VK_FORMAT_A2R10G10B10_UNORM_PACK32,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::A16B16G16R16: return {
VK_FORMAT_R16G16B16A16_UNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::A8P8: return {}; // Unsupported
case D3D9Format::P8: return {}; // Unsupported
case D3D9Format::L8: return {
VK_FORMAT_R8_UNORM,
VK_FORMAT_R8_SRGB,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::A8L8: return {
VK_FORMAT_R8G8_UNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G }};
case D3D9Format::A4L4: return {
VK_FORMAT_R4G4_UNORM_PACK8,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R }};
case D3D9Format::V8U8: return {
VK_FORMAT_R8G8_SNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::L6V5U5: return {}; // Unsupported
case D3D9Format::X8L8V8U8: return {}; // Unsupported
case D3D9Format::Q8W8V8U8: return {
VK_FORMAT_R8G8B8A8_SNORM,
VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::V16U16: return {
VK_FORMAT_R16G16_SNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::A2W10V10U10: return {}; // Unsupported
case D3D9Format::UYVY: return {
VK_FORMAT_B8G8R8A8_UNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY },
{ D3D9VideoFormat_UYVY, { 2u, 1u } }
};
case D3D9Format::R8G8_B8G8: return {
VK_FORMAT_G8B8G8R8_422_UNORM, // This format may have been _SCALED in DX9.
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::YUY2: return {
VK_FORMAT_B8G8R8A8_UNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY },
{ D3D9VideoFormat_YUY2, { 2u, 1u } }
};
case D3D9Format::G8R8_G8B8: return {
VK_FORMAT_B8G8R8G8_422_UNORM, // This format may have been _SCALED in DX9.
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::DXT1: return {
VK_FORMAT_BC1_RGBA_UNORM_BLOCK,
VK_FORMAT_BC1_RGBA_SRGB_BLOCK,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::DXT2: return {
VK_FORMAT_BC2_UNORM_BLOCK,
VK_FORMAT_BC2_SRGB_BLOCK,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::DXT3: return {
VK_FORMAT_BC2_UNORM_BLOCK,
VK_FORMAT_BC2_SRGB_BLOCK,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::DXT4: return {
VK_FORMAT_BC3_UNORM_BLOCK,
VK_FORMAT_BC3_SRGB_BLOCK,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::DXT5: return {
VK_FORMAT_BC3_UNORM_BLOCK,
VK_FORMAT_BC3_SRGB_BLOCK,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::D16_LOCKABLE: return {
VK_FORMAT_D16_UNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_DEPTH_BIT };
case D3D9Format::D32: return {
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_DEPTH_BIT };
case D3D9Format::D15S1: return {}; // Unsupported (everywhere)
case D3D9Format::D24S8: return {
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT };
case D3D9Format::D24X8: return {
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_DEPTH_BIT };
case D3D9Format::D24X4S4: return {}; // Unsupported (everywhere)
case D3D9Format::D16: return {
VK_FORMAT_D16_UNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_DEPTH_BIT };
case D3D9Format::D32F_LOCKABLE: return {
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_DEPTH_BIT };
case D3D9Format::D24FS8: return {
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT };
case D3D9Format::D32_LOCKABLE: return {
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_DEPTH_BIT };
case D3D9Format::S8_LOCKABLE: return {
VK_FORMAT_S8_UINT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_STENCIL_BIT };
case D3D9Format::L16: return {
VK_FORMAT_R16_UNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::VERTEXDATA: return {
VK_FORMAT_R8_UINT,
VK_FORMAT_UNDEFINED,
0 };
case D3D9Format::INDEX16: return {
VK_FORMAT_R16_UINT,
VK_FORMAT_UNDEFINED,
0 };
case D3D9Format::INDEX32: return {
VK_FORMAT_R32_UINT,
VK_FORMAT_UNDEFINED,
0 };
case D3D9Format::Q16W16V16U16: return {
VK_FORMAT_R16G16B16A16_SNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::MULTI2_ARGB8: return {}; // Unsupported
case D3D9Format::R16F: return {
VK_FORMAT_R16_SFLOAT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE,
VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::G16R16F: return {
VK_FORMAT_R16G16_SFLOAT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::A16B16G16R16F: return {
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::R32F: return {
VK_FORMAT_R32_SFLOAT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ONE,
VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::G32R32F: return {
VK_FORMAT_R32G32_SFLOAT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::A32B32G32R32F: return {
VK_FORMAT_R32G32B32A32_SFLOAT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::CxV8U8: return {}; // Unsupported
case D3D9Format::A1: return {}; // Unsupported
case D3D9Format::A2B10G10R10_XR_BIAS: return {
VK_FORMAT_A2B10G10R10_SNORM_PACK32,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::BINARYBUFFER: return {
VK_FORMAT_R8_UINT,
VK_FORMAT_UNDEFINED,
0 };
case D3D9Format::ATI1: return {
VK_FORMAT_BC4_UNORM_BLOCK,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO,
VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::ATI2: return {
VK_FORMAT_BC5_UNORM_BLOCK,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_COLOR_BIT,
{ VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::INST: return {}; // Driver hack, handled elsewhere
case D3D9Format::DF24: return {
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_DEPTH_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO,
VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::DF16: return {
VK_FORMAT_D16_UNORM,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_DEPTH_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_ZERO,
VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ONE }};
case D3D9Format::NULL_FORMAT: return {}; // Driver hack, handled elsewhere
case D3D9Format::GET4: return {}; // Unsupported
case D3D9Format::GET1: return {}; // Unsupported
case D3D9Format::NVDB: return {}; // Driver hack, handled elsewhere
case D3D9Format::A2M1: return {}; // Driver hack, handled elsewhere
case D3D9Format::A2M0: return {}; // Driver hack, handled elsewhere
case D3D9Format::ATOC: return {}; // Driver hack, handled elsewhere
case D3D9Format::INTZ: return {
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_UNDEFINED,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R }};
case D3D9Format::RAWZ: return {}; // Unsupported
default:
Logger::warn(str::format("ConvertFormat: Unknown format encountered: ", Format));
return {}; // Unsupported
}
}
D3D9VkFormatTable::D3D9VkFormatTable(
const Rc<DxvkAdapter>& adapter,
const D3D9Options& options) {
m_dfSupport = options.supportDFFormats;
m_x4r4g4b4Support = options.supportX4R4G4B4;
m_d32supportFinal = options.supportD32;
// AMD do not support 24-bit depth buffers on Vulkan,
// so we have to fall back to a 32-bit depth format.
m_d24s8Support = CheckImageFormatSupport(adapter, VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
// NVIDIA do not support 16-bit depth buffers with stencil on Vulkan,
// so we have to fall back to a 32-bit depth format.
m_d16s8Support = CheckImageFormatSupport(adapter, VK_FORMAT_D16_UNORM_S8_UINT,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
if (!m_d24s8Support)
Logger::warn("D3D9: VK_FORMAT_D24_UNORM_S8_UINT -> VK_FORMAT_D32_SFLOAT_S8_UINT");
if (!m_d16s8Support) {
if (m_d24s8Support)
Logger::warn("D3D9: VK_FORMAT_D16_UNORM_S8_UINT -> VK_FORMAT_D24_UNORM_S8_UINT");
else
Logger::warn("D3D9: VK_FORMAT_D16_UNORM_S8_UINT -> VK_FORMAT_D32_SFLOAT_S8_UINT");
}
}
D3D9_VK_FORMAT_MAPPING D3D9VkFormatTable::GetFormatMapping(
D3D9Format Format) const {
D3D9_VK_FORMAT_MAPPING mapping = ConvertFormatUnfixed(Format);
if (Format == D3D9Format::X4R4G4B4 && !m_x4r4g4b4Support)
return D3D9_VK_FORMAT_MAPPING();
if (Format == D3D9Format::DF16 && !m_dfSupport)
return D3D9_VK_FORMAT_MAPPING();
if (Format == D3D9Format::DF24 && !m_dfSupport)
return D3D9_VK_FORMAT_MAPPING();
if (Format == D3D9Format::D32 && !m_d32supportFinal)
return D3D9_VK_FORMAT_MAPPING();
if (!m_d24s8Support && mapping.FormatColor == VK_FORMAT_D24_UNORM_S8_UINT)
mapping.FormatColor = VK_FORMAT_D32_SFLOAT_S8_UINT;
if (!m_d16s8Support && mapping.FormatColor == VK_FORMAT_D16_UNORM_S8_UINT)
mapping.FormatColor = m_d24s8Support ? VK_FORMAT_D24_UNORM_S8_UINT : VK_FORMAT_D32_SFLOAT_S8_UINT;
return mapping;
}
DxvkFormatInfo D3D9VkFormatTable::GetUnsupportedFormatInfo(
D3D9Format Format) const {
switch (Format) {
case D3D9Format::R8G8B8:
return { 3, VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::R3G3B2:
return { 1, VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::A8R3G3B2:
return { 2, VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::A8P8:
return { 2, VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::P8:
return { 1, VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::L6V5U5:
return { 2, VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::X8L8V8U8:
return { 4, VK_IMAGE_ASPECT_COLOR_BIT };
case D3D9Format::A2W10V10U10:
return { 4, VK_IMAGE_ASPECT_COLOR_BIT };
// MULTI2_ARGB8 -> Don't have a clue what this is.
case D3D9Format::CxV8U8:
return { 2, VK_IMAGE_ASPECT_COLOR_BIT };
// A1 -> Doesn't map nicely here cause it's not byte aligned.
// Gonna just pretend that doesn't exist until something
// depends on that.
default:
return {};
}
}
bool D3D9VkFormatTable::CheckImageFormatSupport(
const Rc<DxvkAdapter>& Adapter,
VkFormat Format,
VkFormatFeatureFlags Features) const {
VkFormatProperties supported = Adapter->formatProperties(Format);
return (supported.linearTilingFeatures & Features) == Features
|| (supported.optimalTilingFeatures & Features) == Features;
}
}

215
src/d3d9/d3d9_format.h Normal file
View file

@ -0,0 +1,215 @@
#pragma once
#include "d3d9_include.h"
#include "d3d9_options.h"
#include "../dxvk/dxvk_adapter.h"
#include "../dxvk/dxvk_format.h"
#include <unordered_map>
namespace dxvk {
enum class D3D9Format : uint32_t {
Unknown = 0,
R8G8B8 = 20,
A8R8G8B8 = 21,
X8R8G8B8 = 22,
R5G6B5 = 23,
X1R5G5B5 = 24,
A1R5G5B5 = 25,
A4R4G4B4 = 26,
R3G3B2 = 27,
A8 = 28,
A8R3G3B2 = 29,
X4R4G4B4 = 30,
A2B10G10R10 = 31,
A8B8G8R8 = 32,
X8B8G8R8 = 33,
G16R16 = 34,
A2R10G10B10 = 35,
A16B16G16R16 = 36,
A8P8 = 40,
P8 = 41,
L8 = 50,
A8L8 = 51,
A4L4 = 52,
V8U8 = 60,
L6V5U5 = 61,
X8L8V8U8 = 62,
Q8W8V8U8 = 63,
V16U16 = 64,
A2W10V10U10 = 67,
UYVY = MAKEFOURCC('U', 'Y', 'V', 'Y'),
R8G8_B8G8 = MAKEFOURCC('R', 'G', 'B', 'G'),
YUY2 = MAKEFOURCC('Y', 'U', 'Y', '2'),
G8R8_G8B8 = MAKEFOURCC('G', 'R', 'G', 'B'),
DXT1 = MAKEFOURCC('D', 'X', 'T', '1'),
DXT2 = MAKEFOURCC('D', 'X', 'T', '2'),
DXT3 = MAKEFOURCC('D', 'X', 'T', '3'),
DXT4 = MAKEFOURCC('D', 'X', 'T', '4'),
DXT5 = MAKEFOURCC('D', 'X', 'T', '5'),
D16_LOCKABLE = 70,
D32 = 71,
D15S1 = 73,
D24S8 = 75,
D24X8 = 77,
D24X4S4 = 79,
D16 = 80,
D32F_LOCKABLE = 82,
D24FS8 = 83,
D32_LOCKABLE = 84,
S8_LOCKABLE = 85,
L16 = 81,
VERTEXDATA = 100,
INDEX16 = 101,
INDEX32 = 102,
Q16W16V16U16 = 110,
MULTI2_ARGB8 = MAKEFOURCC('M', 'E', 'T', '1'),
R16F = 111,
G16R16F = 112,
A16B16G16R16F = 113,
R32F = 114,
G32R32F = 115,
A32B32G32R32F = 116,
CxV8U8 = 117,
A1 = 118,
A2B10G10R10_XR_BIAS = 119,
BINARYBUFFER = 199,
// Driver Hacks / Unofficial Formats
ATI1 = MAKEFOURCC('A', 'T', 'I', '1'),
ATI2 = MAKEFOURCC('A', 'T', 'I', '2'),
INST = MAKEFOURCC('I', 'N', 'S', 'T'),
DF24 = MAKEFOURCC('D', 'F', '2', '4'),
DF16 = MAKEFOURCC('D', 'F', '1', '6'),
NULL_FORMAT = MAKEFOURCC('N', 'U', 'L', 'L'),
GET4 = MAKEFOURCC('G', 'E', 'T', '4'),
GET1 = MAKEFOURCC('G', 'E', 'T', '1'),
NVDB = MAKEFOURCC('N', 'V', 'D', 'B'),
A2M1 = MAKEFOURCC('A', '2', 'M', '1'),
A2M0 = MAKEFOURCC('A', '2', 'M', '0'),
ATOC = MAKEFOURCC('A', 'T', 'O', 'C'),
INTZ = MAKEFOURCC('I', 'N', 'T', 'Z'),
RAWZ = MAKEFOURCC('R', 'A', 'W', 'Z'),
RESZ = MAKEFOURCC('R', 'E', 'S', 'Z'),
NV11 = MAKEFOURCC('N', 'V', '1', '1'),
NV12 = MAKEFOURCC('N', 'V', '1', '2'),
P010 = MAKEFOURCC('P', '0', '1', '0'), // Same as NV12 but 10 bit
P016 = MAKEFOURCC('P', '0', '1', '6'), // Same as NV12 but 16 bit
Y210 = MAKEFOURCC('Y', '2', '1', '0'),
Y216 = MAKEFOURCC('Y', '2', '1', '6'),
Y410 = MAKEFOURCC('Y', '4', '1', '0'),
AYUV = MAKEFOURCC('A', 'Y', 'U', 'V'),
YV12 = MAKEFOURCC('Y', 'V', '1', '2'),
OPAQUE_420 = MAKEFOURCC('4', '2', '0', 'O'),
// Not supported but exist
AI44 = MAKEFOURCC('A', 'I', '4', '4'),
IA44 = MAKEFOURCC('I', 'A', '4', '4'),
R2VB = MAKEFOURCC('R', '2', 'V', 'B'),
COPM = MAKEFOURCC('C', 'O', 'P', 'M'),
SSAA = MAKEFOURCC('S', 'S', 'A', 'A'),
AL16 = MAKEFOURCC('A', 'L', '1', '6'),
R16 = MAKEFOURCC(' ', 'R', '1', '6'),
EXT1 = MAKEFOURCC('E', 'X', 'T', '1'),
FXT1 = MAKEFOURCC('F', 'X', 'T', '1'),
GXT1 = MAKEFOURCC('G', 'X', 'T', '1'),
HXT1 = MAKEFOURCC('H', 'X', 'T', '1'),
};
inline D3D9Format EnumerateFormat(D3DFORMAT format) {
return static_cast<D3D9Format>(format);
}
std::ostream& operator << (std::ostream& os, D3D9Format format);
enum D3D9VideoFormat : uint32_t {
D3D9VideoFormat_None = 0,
D3D9VideoFormat_YUY2 = 1,
D3D9VideoFormat_UYVY,
D3D9VideoFormat_Count
};
struct D3D9_VIDEO_FORMAT_INFO {
D3D9VideoFormat FormatType = D3D9VideoFormat_None;
VkExtent2D MacroPixelSize = { 1u, 1u };
};
/**
* \brief Format mapping
*
* Maps a D3D9 format to a set of Vulkan formats.
*/
struct D3D9_VK_FORMAT_MAPPING {
union {
struct {
VkFormat FormatColor; ///< Corresponding color format
VkFormat FormatSrgb; ///< Corresponding color format
};
VkFormat Formats[2];
};
VkImageAspectFlags Aspect = 0; ///< Defined aspects for the color format
VkComponentMapping Swizzle = { ///< Color component swizzle
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
D3D9_VIDEO_FORMAT_INFO VideoFormatInfo = { };
bool IsValid() { return FormatColor != VK_FORMAT_UNDEFINED; }
};
D3D9_VK_FORMAT_MAPPING ConvertFormatUnfixed(D3D9Format Format);
/**
* \brief Format table
*
* Initializes a format table for a specific
* device and provides methods to look up
* formats.
*/
class D3D9VkFormatTable {
public:
D3D9VkFormatTable(
const Rc<DxvkAdapter>& adapter,
const D3D9Options& options);
/**
* \brief Retrieves info for a given D3D9 format
*
* \param [in] Format The D3D9 format to look up
* \param [in] Mode the format lookup mode
* \returns Format info
*/
D3D9_VK_FORMAT_MAPPING GetFormatMapping(
D3D9Format Format) const;
/**
* \brief Retrieves format info for unsupported
* formats.
*
* \param [in] Format The D3D9 format to look up
*/
DxvkFormatInfo GetUnsupportedFormatInfo(
D3D9Format Format) const;
private:
bool CheckImageFormatSupport(
const Rc<DxvkAdapter>& Adapter,
VkFormat Format,
VkFormatFeatureFlags Features) const;
bool m_d24s8Support;
bool m_d16s8Support;
bool m_dfSupport;
bool m_x4r4g4b4Support;
bool m_d32supportFinal;
};
}

View file

@ -0,0 +1,82 @@
#include "d3d9_format_helpers.h"
#include <d3d9_convert_yuy2_uyvy.h>
namespace dxvk {
D3D9FormatHelper::D3D9FormatHelper(const Rc<DxvkDevice>& device)
: m_device(device), m_context(m_device->createContext()) {
m_context->beginRecording(
m_device->createCommandList());
InitShaders();
}
void D3D9FormatHelper::ConvertVideoFormat(
D3D9_VIDEO_FORMAT_INFO videoFormat,
const Rc<DxvkImage>& dstImage,
VkImageSubresourceLayers dstSubresource,
const Rc<DxvkBuffer>& srcBuffer) {
DxvkImageViewCreateInfo imageViewInfo;
imageViewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
imageViewInfo.format = dstImage->info().format;
imageViewInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT;
imageViewInfo.aspect = dstSubresource.aspectMask;
imageViewInfo.minLevel = dstSubresource.mipLevel;
imageViewInfo.numLevels = 1;
imageViewInfo.minLayer = dstSubresource.baseArrayLayer;
imageViewInfo.numLayers = dstSubresource.layerCount;
auto tmpImageView = m_device->createImageView(dstImage, imageViewInfo);
VkExtent3D imageExtent = dstImage->mipLevelExtent(dstSubresource.mipLevel);
imageExtent = VkExtent3D{ imageExtent.width / videoFormat.MacroPixelSize.width,
imageExtent.height / videoFormat.MacroPixelSize.height,
1 };
DxvkBufferViewCreateInfo bufferViewInfo;
bufferViewInfo.format = VK_FORMAT_R32_UINT;
bufferViewInfo.rangeOffset = 0;
bufferViewInfo.rangeLength = srcBuffer->info().size;
auto tmpBufferView = m_device->createBufferView(srcBuffer, bufferViewInfo);
if (videoFormat.FormatType == D3D9VideoFormat_UYVY
|| videoFormat.FormatType == D3D9VideoFormat_YUY2) {
m_context->setSpecConstant(VK_PIPELINE_BIND_POINT_COMPUTE, 0, videoFormat.FormatType == D3D9VideoFormat_UYVY);
}
m_context->bindResourceView(BindingIds::Image, tmpImageView, nullptr);
m_context->bindResourceView(BindingIds::Buffer, nullptr, tmpBufferView);
m_context->bindShader(VK_SHADER_STAGE_COMPUTE_BIT, m_shaders[videoFormat.FormatType]);
m_context->pushConstants(0, sizeof(VkExtent2D), &imageExtent);
m_context->dispatch(
(imageExtent.width / 8) + (imageExtent.width % 8),
(imageExtent.height / 8) + (imageExtent.height % 8),
1);
// Reset the spec constants used...
m_context->setSpecConstant(VK_PIPELINE_BIND_POINT_COMPUTE, 0, 0);
m_context->flushCommandList();
}
void D3D9FormatHelper::InitShaders() {
m_shaders[D3D9VideoFormat_YUY2] = InitShader(d3d9_convert_yuy2_uyvy);
m_shaders[D3D9VideoFormat_UYVY] = m_shaders[D3D9VideoFormat_YUY2];
}
Rc<DxvkShader> D3D9FormatHelper::InitShader(SpirvCodeBuffer code) {
const std::array<DxvkResourceSlot, 2> resourceSlots = { {
{ BindingIds::Image, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_IMAGE_VIEW_TYPE_2D },
{ BindingIds::Buffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_IMAGE_VIEW_TYPE_1D },
} };
return m_device->createShader(
VK_SHADER_STAGE_COMPUTE_BIT,
resourceSlots.size(), resourceSlots.data(),
{ 0u, 0u, 0u, sizeof(VkExtent2D) }, code);
}
}

View file

@ -0,0 +1,40 @@
#pragma once
#include "d3d9_include.h"
#include "d3d9_format.h"
#include "../dxvk/dxvk_device.h"
#include "../dxvk/dxvk_context.h"
namespace dxvk {
class D3D9FormatHelper {
public:
D3D9FormatHelper(const Rc<DxvkDevice>& device);
void ConvertVideoFormat(
D3D9_VIDEO_FORMAT_INFO videoFormat,
const Rc<DxvkImage>& dstImage,
VkImageSubresourceLayers dstSubresource,
const Rc<DxvkBuffer>& srcBuffer);
private:
enum BindingIds : uint32_t {
Image = 0,
Buffer = 1,
};
void InitShaders();
Rc<DxvkShader> InitShader(SpirvCodeBuffer code);
Rc<DxvkDevice> m_device;
Rc<DxvkContext> m_context;
std::array<Rc<DxvkShader>, D3D9VideoFormat_Count> m_shaders;
};
}

36
src/d3d9/d3d9_hud.cpp Normal file
View file

@ -0,0 +1,36 @@
#include "d3d9_hud.h"
namespace dxvk::hud {
HudSamplerCount::HudSamplerCount(D3D9DeviceEx* device)
: m_device (device)
, m_samplerCount ("0"){
}
void HudSamplerCount::update(dxvk::high_resolution_clock::time_point time) {
m_samplerCount = str::format(m_device->GetSamplerCount());
}
HudPos HudSamplerCount::render(
HudRenderer& renderer,
HudPos position) {
position.y += 16.0f;
renderer.drawText(16.0f,
{ position.x, position.y },
{ 0.0f, 1.0f, 0.75f, 1.0f },
"Samplers:");
renderer.drawText(16.0f,
{ position.x + 120.0f, position.y },
{ 1.0f, 1.0f, 1.0f, 1.0f },
m_samplerCount);
position.y += 8.0f;
return position;
}
}

31
src/d3d9/d3d9_hud.h Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#include "d3d9_device.h"
#include "../dxvk/hud/dxvk_hud_item.h"
namespace dxvk::hud {
/**
* \brief HUD item to display DXVK version
*/
class HudSamplerCount : public HudItem {
public:
HudSamplerCount(D3D9DeviceEx* device);
void update(dxvk::high_resolution_clock::time_point time);
HudPos render(
HudRenderer& renderer,
HudPos position);
private:
D3D9DeviceEx* m_device;
std::string m_samplerCount;
};
}

95
src/d3d9/d3d9_include.h Normal file
View file

@ -0,0 +1,95 @@
#pragma once
#ifndef _MSC_VER
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0A00
#endif
#include <stdint.h>
#include <d3d9.h>
//for some reason we need to specify __declspec(dllexport) for MinGW
#if defined(__WINE__)
#define DLLEXPORT __attribute__((visibility("default")))
#elif defined(_MSC_VER)
#define DLLEXPORT
#else
#define DLLEXPORT __declspec(dllexport)
#endif
#include "../util/com/com_guid.h"
#include "../util/com/com_object.h"
#include "../util/com/com_pointer.h"
#include "../util/log/log.h"
#include "../util/log/log_debug.h"
#include "../util/rc/util_rc.h"
#include "../util/rc/util_rc_ptr.h"
#include "../util/util_env.h"
#include "../util/util_enum.h"
#include "../util/util_error.h"
#include "../util/util_flags.h"
#include "../util/util_likely.h"
#include "../util/util_math.h"
#include "../util/util_string.h"
#include "../util/util_misc.h"
// Missed definitions in Wine/MinGW.
#ifndef D3DPRESENT_BACK_BUFFERS_MAX_EX
#define D3DPRESENT_BACK_BUFFERS_MAX_EX 30
#endif
#ifndef D3DSI_OPCODE_MASK
#define D3DSI_OPCODE_MASK 0x0000FFFF
#endif
#ifndef D3DSP_TEXTURETYPE_MASK
#define D3DSP_TEXTURETYPE_MASK 0x78000000
#endif
#ifndef D3DUSAGE_AUTOGENMIPMAP
#define D3DUSAGE_AUTOGENMIPMAP 0x00000400L
#endif
#ifndef D3DSP_DCL_USAGE_MASK
#define D3DSP_DCL_USAGE_MASK 0x0000000f
#endif
#ifndef D3DSP_OPCODESPECIFICCONTROL_MASK
#define D3DSP_OPCODESPECIFICCONTROL_MASK 0x00ff0000
#endif
#ifndef D3DSP_OPCODESPECIFICCONTROL_SHIFT
#define D3DSP_OPCODESPECIFICCONTROL_SHIFT 16
#endif
#ifndef D3DCURSOR_IMMEDIATE_UPDATE
#define D3DCURSOR_IMMEDIATE_UPDATE 0x00000001L
#endif
#ifndef D3DPRESENT_FORCEIMMEDIATE
#define D3DPRESENT_FORCEIMMEDIATE 0x00000100L
#endif
// MinGW headers are broken. Who'dve guessed?
#ifndef _MSC_VER
typedef struct _D3DDEVINFO_RESOURCEMANAGER
{
char dummy;
} D3DDEVINFO_RESOURCEMANAGER, * LPD3DDEVINFO_RESOURCEMANAGER;
#ifndef __WINE__
extern "C" WINUSERAPI WINBOOL WINAPI SetProcessDPIAware(VOID);
#endif
#endif
// This is the managed pool on D3D9Ex, it's just hidden!
#define D3DPOOL_MANAGED_EX D3DPOOL(6)
using D3D9VertexElements = std::vector<D3DVERTEXELEMENT9>;

View file

@ -0,0 +1,165 @@
#include <cstring>
#include "d3d9_initializer.h"
namespace dxvk {
D3D9Initializer::D3D9Initializer(
const Rc<DxvkDevice>& Device)
: m_device(Device), m_context(m_device->createContext()) {
m_context->beginRecording(
m_device->createCommandList());
}
D3D9Initializer::~D3D9Initializer() {
}
void D3D9Initializer::Flush() {
std::lock_guard<std::mutex> lock(m_mutex);
if (m_transferCommands != 0)
FlushInternal();
}
void D3D9Initializer::InitBuffer(
D3D9CommonBuffer* pBuffer) {
VkMemoryPropertyFlags memFlags = pBuffer->GetBuffer<D3D9_COMMON_BUFFER_TYPE_REAL>()->memFlags();
(memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
? InitHostVisibleBuffer(pBuffer->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_REAL>())
: InitDeviceLocalBuffer(pBuffer->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_REAL>());
if (pBuffer->GetMapMode() == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER)
InitHostVisibleBuffer(pBuffer->GetBufferSlice<D3D9_COMMON_BUFFER_TYPE_STAGING>());
}
void D3D9Initializer::InitTexture(
D3D9CommonTexture* pTexture,
void* pInitialData) {
if (pTexture->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_NONE)
return;
(pTexture->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED)
? InitDeviceLocalTexture(pTexture)
: InitHostVisibleTexture(pTexture, pInitialData);
}
void D3D9Initializer::InitDeviceLocalBuffer(
DxvkBufferSlice Slice) {
std::lock_guard<std::mutex> lock(m_mutex);
m_transferCommands += 1;
m_context->clearBuffer(
Slice.buffer(),
Slice.offset(),
Slice.length(),
0u);
FlushImplicit();
}
void D3D9Initializer::InitHostVisibleBuffer(
DxvkBufferSlice Slice) {
// If the buffer is mapped, we can write data directly
// to the mapped memory region instead of doing it on
// the GPU. Same goes for zero-initialization.
std::memset(
Slice.mapPtr(0), 0,
Slice.length());
}
void D3D9Initializer::InitDeviceLocalTexture(
D3D9CommonTexture* pTexture) {
std::lock_guard<std::mutex> lock(m_mutex);
auto InitImage = [&](Rc<DxvkImage> image) {
if (image == nullptr)
return;
auto formatInfo = imageFormatInfo(image->info().format);
m_transferCommands += 1;
// While the Microsoft docs state that resource contents are
// undefined if no initial data is provided, some applications
// expect a resource to be pre-cleared. We can only do that
// for non-compressed images, but that should be fine.
VkImageSubresourceRange subresources;
subresources.aspectMask = formatInfo->aspectMask;
subresources.baseMipLevel = 0;
subresources.levelCount = image->info().mipLevels;
subresources.baseArrayLayer = 0;
subresources.layerCount = image->info().numLayers;
if (formatInfo->flags.test(DxvkFormatFlag::BlockCompressed)) {
m_context->clearCompressedColorImage(image, subresources);
} else {
if (subresources.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) {
VkClearColorValue value = { };
m_context->clearColorImage(
image, value, subresources);
} else {
VkClearDepthStencilValue value;
value.depth = 0.0f;
value.stencil = 0;
m_context->clearDepthStencilImage(
image, value, subresources);
}
}
};
InitImage(pTexture->GetImage());
FlushImplicit();
}
void D3D9Initializer::InitHostVisibleTexture(
D3D9CommonTexture* pTexture,
void* pInitialData) {
// If the buffer is mapped, we can write data directly
// to the mapped memory region instead of doing it on
// the GPU. Same goes for zero-initialization.
for (uint32_t i = 0; i < pTexture->CountSubresources(); i++) {
DxvkBufferSliceHandle mapSlice = pTexture->GetBuffer(i)->getSliceHandle();
if (pInitialData != nullptr) {
std::memcpy(
mapSlice.mapPtr,
pInitialData,
mapSlice.length);
} else {
std::memset(
mapSlice.mapPtr, 0,
mapSlice.length);
}
}
}
void D3D9Initializer::FlushImplicit() {
if (m_transferCommands > MaxTransferCommands
|| m_transferMemory > MaxTransferMemory)
FlushInternal();
}
void D3D9Initializer::FlushInternal() {
m_context->flushCommandList();
m_transferCommands = 0;
m_transferMemory = 0;
}
}

View file

@ -0,0 +1,62 @@
#pragma once
#include "d3d9_common_buffer.h"
#include "d3d9_common_texture.h"
namespace dxvk {
/**
* \brief Resource initialization context
*
* Manages a context which is used for resource
* initialization. This includes
* zero-initialization for buffers and images.
*/
class D3D9Initializer {
constexpr static size_t MaxTransferMemory = 32 * 1024 * 1024;
constexpr static size_t MaxTransferCommands = 512;
public:
D3D9Initializer(
const Rc<DxvkDevice>& Device);
~D3D9Initializer();
void Flush();
void InitBuffer(
D3D9CommonBuffer* pBuffer);
void InitTexture(
D3D9CommonTexture* pTexture,
void* pInitialData = nullptr);
private:
std::mutex m_mutex;
Rc<DxvkDevice> m_device;
Rc<DxvkContext> m_context;
size_t m_transferCommands = 0;
size_t m_transferMemory = 0;
void InitDeviceLocalBuffer(
DxvkBufferSlice Slice);
void InitHostVisibleBuffer(
DxvkBufferSlice Slice);
void InitDeviceLocalTexture(
D3D9CommonTexture* pTexture);
void InitHostVisibleTexture(
D3D9CommonTexture* pTexture,
void* pInitialData);
void FlushImplicit();
void FlushInternal();
};
}

316
src/d3d9/d3d9_interface.cpp Normal file
View file

@ -0,0 +1,316 @@
#include "d3d9_interface.h"
#include "d3d9_monitor.h"
#include "d3d9_caps.h"
#include "d3d9_device.h"
#include <algorithm>
namespace dxvk {
D3D9InterfaceEx::D3D9InterfaceEx(bool bExtended)
: m_instance ( new DxvkInstance() )
, m_extended ( bExtended )
, m_d3d9Options ( nullptr, m_instance->config() ) {
m_adapters.reserve(m_instance->adapterCount());
for (uint32_t i = 0; i < m_instance->adapterCount(); i++)
m_adapters.emplace_back(this, m_instance->enumAdapters(i), i);
if (m_d3d9Options.dpiAware) {
Logger::info("Process set as DPI aware");
SetProcessDPIAware();
}
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::QueryInterface(REFIID riid, void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3D9)
|| (m_extended && riid == __uuidof(IDirect3D9Ex))) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9InterfaceEx::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::RegisterSoftwareDevice(void* pInitializeFunction) {
Logger::warn("D3D9InterfaceEx::RegisterSoftwareDevice: Stub");
return D3D_OK;
}
UINT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterCount() {
return UINT(m_adapters.size());
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterIdentifier(
UINT Adapter,
DWORD Flags,
D3DADAPTER_IDENTIFIER9* pIdentifier) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->GetAdapterIdentifier(Flags, pIdentifier);
return D3DERR_INVALIDCALL;
}
UINT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterModeCount(UINT Adapter, D3DFORMAT Format) {
D3DDISPLAYMODEFILTER filter;
filter.Size = sizeof(D3DDISPLAYMODEFILTER);
filter.Format = Format;
filter.ScanLineOrdering = D3DSCANLINEORDERING_PROGRESSIVE;
return this->GetAdapterModeCountEx(Adapter, &filter);
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) {
constexpr D3DFORMAT format = D3DFMT_X8R8G8B8;
const UINT mode = GetAdapterModeCount(Adapter, format) - 1;
return this->EnumAdapterModes(Adapter, format, mode, pMode);
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceType(
UINT Adapter,
D3DDEVTYPE DevType,
D3DFORMAT AdapterFormat,
D3DFORMAT BackBufferFormat,
BOOL bWindowed) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->CheckDeviceType(
DevType, EnumerateFormat(AdapterFormat),
EnumerateFormat(BackBufferFormat), bWindowed);
return D3DERR_INVALIDCALL;
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceFormat(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT AdapterFormat,
DWORD Usage,
D3DRESOURCETYPE RType,
D3DFORMAT CheckFormat) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->CheckDeviceFormat(
DeviceType, EnumerateFormat(AdapterFormat),
Usage, RType,
EnumerateFormat(CheckFormat));
return D3DERR_INVALIDCALL;
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceMultiSampleType(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT SurfaceFormat,
BOOL Windowed,
D3DMULTISAMPLE_TYPE MultiSampleType,
DWORD* pQualityLevels) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->CheckDeviceMultiSampleType(
DeviceType, EnumerateFormat(SurfaceFormat),
Windowed, MultiSampleType,
pQualityLevels);
return D3DERR_INVALIDCALL;
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDepthStencilMatch(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT AdapterFormat,
D3DFORMAT RenderTargetFormat,
D3DFORMAT DepthStencilFormat) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->CheckDepthStencilMatch(
DeviceType, EnumerateFormat(AdapterFormat),
EnumerateFormat(RenderTargetFormat),
EnumerateFormat(DepthStencilFormat));
return D3DERR_INVALIDCALL;
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CheckDeviceFormatConversion(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT SourceFormat,
D3DFORMAT TargetFormat) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->CheckDeviceFormatConversion(
DeviceType, EnumerateFormat(SourceFormat),
EnumerateFormat(TargetFormat));
return D3DERR_INVALIDCALL;
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetDeviceCaps(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DCAPS9* pCaps) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->GetDeviceCaps(
DeviceType, pCaps);
return D3DERR_INVALIDCALL;
}
HMONITOR STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterMonitor(UINT Adapter) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->GetMonitor();
return nullptr;
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CreateDevice(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS* pPresentationParameters,
IDirect3DDevice9** ppReturnedDeviceInterface) {
return this->CreateDeviceEx(
Adapter,
DeviceType,
hFocusWindow,
BehaviorFlags,
pPresentationParameters,
nullptr, // <-- pFullscreenDisplayMode
reinterpret_cast<IDirect3DDevice9Ex**>(ppReturnedDeviceInterface));
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::EnumAdapterModes(
UINT Adapter,
D3DFORMAT Format,
UINT Mode,
D3DDISPLAYMODE* pMode) {
if (pMode == nullptr)
return D3DERR_INVALIDCALL;
D3DDISPLAYMODEFILTER filter;
filter.Format = Format;
filter.ScanLineOrdering = D3DSCANLINEORDERING_PROGRESSIVE;
filter.Size = sizeof(D3DDISPLAYMODEFILTER);
D3DDISPLAYMODEEX modeEx;
HRESULT hr = this->EnumAdapterModesEx(Adapter, &filter, Mode, &modeEx);
if (FAILED(hr))
return hr;
pMode->Width = modeEx.Width;
pMode->Height = modeEx.Height;
pMode->RefreshRate = modeEx.RefreshRate;
pMode->Format = modeEx.Format;
return D3D_OK;
}
// Ex Methods
UINT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterModeCountEx(UINT Adapter, CONST D3DDISPLAYMODEFILTER* pFilter) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->GetAdapterModeCountEx(pFilter);
return 0;
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::EnumAdapterModesEx(
UINT Adapter,
const D3DDISPLAYMODEFILTER* pFilter,
UINT Mode,
D3DDISPLAYMODEEX* pMode) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->EnumAdapterModesEx(pFilter, Mode, pMode);
return D3DERR_INVALIDCALL;
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterDisplayModeEx(
UINT Adapter,
D3DDISPLAYMODEEX* pMode,
D3DDISPLAYROTATION* pRotation) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->GetAdapterDisplayModeEx(pMode, pRotation);
return D3DERR_INVALIDCALL;
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::CreateDeviceEx(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS* pPresentationParameters,
D3DDISPLAYMODEEX* pFullscreenDisplayMode,
IDirect3DDevice9Ex** ppReturnedDeviceInterface) {
InitReturnPtr(ppReturnedDeviceInterface);
if (ppReturnedDeviceInterface == nullptr
|| pPresentationParameters == nullptr)
return D3DERR_INVALIDCALL;
auto* adapter = GetAdapter(Adapter);
if (adapter == nullptr)
return D3DERR_INVALIDCALL;
auto dxvkAdapter = adapter->GetDXVKAdapter();
std::string clientApi = str::format("D3D9", m_extended ? "Ex" : "");
try {
auto dxvkDevice = dxvkAdapter->createDevice(m_instance, clientApi, D3D9DeviceEx::GetDeviceFeatures(dxvkAdapter));
*ppReturnedDeviceInterface = ref(new D3D9DeviceEx(
this,
adapter,
DeviceType,
hFocusWindow,
BehaviorFlags,
pPresentationParameters,
pFullscreenDisplayMode,
dxvkDevice));
}
catch (const DxvkError& e) {
Logger::err(e.message());
return D3DERR_NOTAVAILABLE;
}
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE D3D9InterfaceEx::GetAdapterLUID(UINT Adapter, LUID* pLUID) {
if (auto* adapter = GetAdapter(Adapter))
return adapter->GetAdapterLUID(pLUID);
return D3DERR_INVALIDCALL;
}
}

148
src/d3d9/d3d9_interface.h Normal file
View file

@ -0,0 +1,148 @@
#pragma once
#include "d3d9_adapter.h"
#include "../dxvk/dxvk_instance.h"
namespace dxvk {
/**
* \brief D3D9 interface implementation
*
* Implements the IDirect3DDevice9Ex interfaces
* which provides the way to get adapters and create other objects such as \ref IDirect3DDevice9Ex.
* similar to \ref DxgiFactory but for D3D9.
*/
class D3D9InterfaceEx final : public ComObjectClamp<IDirect3D9Ex> {
public:
D3D9InterfaceEx(bool bExtended);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction);
UINT STDMETHODCALLTYPE GetAdapterCount();
HRESULT STDMETHODCALLTYPE GetAdapterIdentifier(
UINT Adapter,
DWORD Flags,
D3DADAPTER_IDENTIFIER9* pIdentifier);
UINT STDMETHODCALLTYPE GetAdapterModeCount(UINT Adapter, D3DFORMAT Format);
HRESULT STDMETHODCALLTYPE GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode);
HRESULT STDMETHODCALLTYPE CheckDeviceType(
UINT Adapter,
D3DDEVTYPE DevType,
D3DFORMAT AdapterFormat,
D3DFORMAT BackBufferFormat,
BOOL bWindowed);
HRESULT STDMETHODCALLTYPE CheckDeviceFormat(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT AdapterFormat,
DWORD Usage,
D3DRESOURCETYPE RType,
D3DFORMAT CheckFormat);
HRESULT STDMETHODCALLTYPE CheckDeviceMultiSampleType(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT SurfaceFormat,
BOOL Windowed,
D3DMULTISAMPLE_TYPE MultiSampleType,
DWORD* pQualityLevels);
HRESULT STDMETHODCALLTYPE CheckDepthStencilMatch(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT AdapterFormat,
D3DFORMAT RenderTargetFormat,
D3DFORMAT DepthStencilFormat);
HRESULT STDMETHODCALLTYPE CheckDeviceFormatConversion(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT SourceFormat,
D3DFORMAT TargetFormat);
HRESULT STDMETHODCALLTYPE GetDeviceCaps(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DCAPS9* pCaps);
HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter);
HRESULT STDMETHODCALLTYPE CreateDevice(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS* pPresentationParameters,
IDirect3DDevice9** ppReturnedDeviceInterface);
HRESULT STDMETHODCALLTYPE EnumAdapterModes(
UINT Adapter,
D3DFORMAT Format,
UINT Mode,
D3DDISPLAYMODE* pMode);
// Ex Methods
UINT STDMETHODCALLTYPE GetAdapterModeCountEx(UINT Adapter, CONST D3DDISPLAYMODEFILTER* pFilter);
HRESULT STDMETHODCALLTYPE EnumAdapterModesEx(
UINT Adapter,
const D3DDISPLAYMODEFILTER* pFilter,
UINT Mode,
D3DDISPLAYMODEEX* pMode);
HRESULT STDMETHODCALLTYPE GetAdapterDisplayModeEx(
UINT Adapter,
D3DDISPLAYMODEEX* pMode,
D3DDISPLAYROTATION* pRotation);
HRESULT STDMETHODCALLTYPE CreateDeviceEx(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS* pPresentationParameters,
D3DDISPLAYMODEEX* pFullscreenDisplayMode,
IDirect3DDevice9Ex** ppReturnedDeviceInterface);
HRESULT STDMETHODCALLTYPE GetAdapterLUID(UINT Adapter, LUID* pLUID);
const D3D9Options& GetOptions() { return m_d3d9Options; }
D3D9Adapter* GetAdapter(UINT Ordinal) {
return Ordinal < m_adapters.size()
? &m_adapters[Ordinal]
: nullptr;
}
bool IsExtended() { return m_extended; }
Rc<DxvkInstance> GetInstance() { return m_instance; }
private:
void CacheModes(D3D9Format Format);
static const char* GetDriverDllName(DxvkGpuVendor vendor);
Rc<DxvkInstance> m_instance;
bool m_extended;
D3D9Options m_d3d9Options;
std::vector<D3D9Adapter> m_adapters;
};
}

86
src/d3d9/d3d9_main.cpp Normal file
View file

@ -0,0 +1,86 @@
#include "../dxvk/dxvk_instance.h"
#include "d3d9_interface.h"
#include "d3d9_shader_validator.h"
class D3DFE_PROCESSVERTICES;
using PSGPERRORID = UINT;
namespace dxvk {
Logger Logger::s_instance("d3d9.log");
HRESULT CreateD3D9(
bool Extended,
IDirect3D9Ex** ppDirect3D9Ex) {
if (!ppDirect3D9Ex)
return D3DERR_INVALIDCALL;
*ppDirect3D9Ex = ref(new D3D9InterfaceEx( Extended ));
return D3D_OK;
}
}
extern "C" {
DLLEXPORT IDirect3D9* __stdcall Direct3DCreate9(UINT nSDKVersion) {
IDirect3D9Ex* pDirect3D = nullptr;
dxvk::CreateD3D9(false, &pDirect3D);
return pDirect3D;
}
DLLEXPORT HRESULT __stdcall Direct3DCreate9Ex(UINT nSDKVersion, IDirect3D9Ex** ppDirect3D9Ex) {
return dxvk::CreateD3D9(true, ppDirect3D9Ex);
}
DLLEXPORT int __stdcall D3DPERF_BeginEvent(D3DCOLOR col, LPCWSTR wszName) {
return 0;
}
DLLEXPORT int __stdcall D3DPERF_EndEvent(void) {
return 0;
}
DLLEXPORT void __stdcall D3DPERF_SetMarker(D3DCOLOR col, LPCWSTR wszName) {
}
DLLEXPORT void __stdcall D3DPERF_SetRegion(D3DCOLOR col, LPCWSTR wszName) {
}
DLLEXPORT BOOL __stdcall D3DPERF_QueryRepeatFrame(void) {
return FALSE;
}
DLLEXPORT void __stdcall D3DPERF_SetOptions(DWORD dwOptions) {
}
DLLEXPORT DWORD __stdcall D3DPERF_GetStatus(void) {
return 0;
}
DLLEXPORT void __stdcall DebugSetMute(void) {
}
DLLEXPORT int __stdcall DebugSetLevel(void) {
return 0;
}
// Processor Specific Geometry Pipeline
// for P3 SIMD/AMD 3DNow.
DLLEXPORT void __stdcall PSGPError(D3DFE_PROCESSVERTICES* a, PSGPERRORID b, UINT c) {
}
DLLEXPORT void __stdcall PSGPSampleTexture(D3DFE_PROCESSVERTICES* a, UINT b, float(*const c)[4], UINT d, float(*const e)[4]) {
}
DLLEXPORT dxvk::D3D9ShaderValidator* __stdcall Direct3DShaderValidatorCreate9(void) {
return ref(new dxvk::D3D9ShaderValidator());
}
DLLEXPORT int __stdcall Direct3D9EnableMaximizedWindowedModeShim(UINT a) {
return 0;
}
}

175
src/d3d9/d3d9_monitor.cpp Normal file
View file

@ -0,0 +1,175 @@
#include "d3d9_monitor.h"
#include "d3d9_format.h"
namespace dxvk {
uint32_t GetMonitorFormatBpp(D3D9Format Format) {
switch (Format) {
case D3D9Format::A8R8G8B8:
case D3D9Format::X8R8G8B8: // This is still 32 bit even though the alpha is unspecified.
case D3D9Format::A2R10G10B10:
return 32;
case D3D9Format::A1R5G5B5:
case D3D9Format::X1R5G5B5:
case D3D9Format::R5G6B5:
return 16;
default:
Logger::warn(str::format(
"GetMonitorFormatBpp: Unknown format: ",
Format));
return 32;
}
}
bool IsSupportedAdapterFormat(
D3D9Format Format) {
return Format == D3D9Format::A2R10G10B10
|| Format == D3D9Format::X8R8G8B8
|| Format == D3D9Format::A8R8G8B8
|| Format == D3D9Format::X1R5G5B5
|| Format == D3D9Format::A1R5G5B5
|| Format == D3D9Format::R5G6B5;
}
bool IsSupportedDisplayFormat(
D3D9Format Format,
BOOL Windowed) {
return (Format == D3D9Format::A2R10G10B10 && !Windowed)
|| Format == D3D9Format::X8R8G8B8
|| Format == D3D9Format::X1R5G5B5
|| Format == D3D9Format::R5G6B5;
}
bool IsSupportedBackBufferFormat(
D3D9Format AdapterFormat,
D3D9Format BackBufferFormat,
BOOL Windowed) {
if (!IsSupportedAdapterFormat(AdapterFormat))
return false;
if (AdapterFormat == D3D9Format::A2R10G10B10 && Windowed)
return false;
return AdapterFormat == BackBufferFormat
|| (AdapterFormat == D3D9Format::X8R8G8B8 && BackBufferFormat == D3D9Format::A8R8G8B8)
|| (AdapterFormat == D3D9Format::X1R5G5B5 && BackBufferFormat == D3D9Format::A1R5G5B5);
}
bool IsSupportedBackBufferFormat(
D3D9Format BackBufferFormat,
BOOL Windowed) {
return (BackBufferFormat == D3D9Format::A2R10G10B10 && !Windowed)
|| BackBufferFormat == D3D9Format::A8R8G8B8
|| BackBufferFormat == D3D9Format::X8R8G8B8
|| BackBufferFormat == D3D9Format::A1R5G5B5
|| BackBufferFormat == D3D9Format::X1R5G5B5
|| BackBufferFormat == D3D9Format::R5G6B5;
}
HMONITOR GetDefaultMonitor() {
return ::MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
}
HRESULT SetMonitorDisplayMode(
HMONITOR hMonitor,
const D3DDISPLAYMODEEX* pMode) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("D3D9: Failed to query monitor info");
return E_FAIL;
}
DEVMODEW devMode = { };
devMode.dmSize = sizeof(devMode);
devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
devMode.dmPelsWidth = pMode->Width;
devMode.dmPelsHeight = pMode->Height;
devMode.dmBitsPerPel = GetMonitorFormatBpp(EnumerateFormat(pMode->Format));
if (pMode->RefreshRate != 0) {
devMode.dmFields |= DM_DISPLAYFREQUENCY;
devMode.dmDisplayFrequency = pMode->RefreshRate;
}
Logger::info(str::format("D3D9: Setting display mode: ",
devMode.dmPelsWidth, "x", devMode.dmPelsHeight, "@",
devMode.dmDisplayFrequency));
LONG status = ::ChangeDisplaySettingsExW(
monInfo.szDevice, &devMode, nullptr, CDS_FULLSCREEN, nullptr);
if (status != DISP_CHANGE_SUCCESSFUL) {
// Try again but without setting the frequency.
devMode.dmFields &= ~DM_DISPLAYFREQUENCY;
devMode.dmDisplayFrequency = 0;
status = ::ChangeDisplaySettingsExW(
monInfo.szDevice, &devMode, nullptr, CDS_FULLSCREEN, nullptr);
}
return status == DISP_CHANGE_SUCCESSFUL ? D3D_OK : D3DERR_NOTAVAILABLE;
}
void GetWindowClientSize(
HWND hWnd,
UINT* pWidth,
UINT* pHeight) {
RECT rect = { };
::GetClientRect(hWnd, &rect);
if (pWidth)
*pWidth = rect.right - rect.left;
if (pHeight)
*pHeight = rect.bottom - rect.top;
}
void GetMonitorClientSize(
HMONITOR hMonitor,
UINT* pWidth,
UINT* pHeight) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("D3D9: Failed to query monitor info");
return;
}
auto rect = monInfo.rcMonitor;
if (pWidth)
*pWidth = rect.right - rect.left;
if (pHeight)
*pHeight = rect.bottom - rect.top;
}
void GetMonitorRect(
HMONITOR hMonitor,
RECT* pRect) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(hMonitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("D3D9: Failed to query monitor info");
return;
}
*pRect = monInfo.rcMonitor;
}
}

88
src/d3d9/d3d9_monitor.h Normal file
View file

@ -0,0 +1,88 @@
#pragma once
#include "d3d9_include.h"
#include "d3d9_format.h"
namespace dxvk {
/**
* \brief Queries bits per pixel for a format
*
* The format must be a valid swap chain format.
* \param [in] Format The D3D9 format to query
* \returns Bits per pixel for this format
*/
uint32_t GetMonitorFormatBpp(
D3D9Format Format);
/**
* \brief Returns if a format is supported for a backbuffer/swapchain.
*
* \param [in] Format The D3D9 format to query
* \returns If it is supported as a swapchain/backbuffer format.
*/
bool IsSupportedAdapterFormat(
D3D9Format Format);
bool IsSupportedDisplayFormat(
D3D9Format Format,
BOOL Windowed);
bool IsSupportedBackBufferFormat(
D3D9Format AdapterFormat,
D3D9Format BackBufferFormat,
BOOL Windowed);
bool IsSupportedBackBufferFormat(
D3D9Format BackBufferFormat,
BOOL Windowed);
HMONITOR GetDefaultMonitor();
/**
* \brief Sets monitor display mode
*
* \param [in] hMonitor Monitor handle
* \param [in] pMode Display mode properties
* \returns S_OK on success
*/
HRESULT SetMonitorDisplayMode(
HMONITOR hMonitor,
const D3DDISPLAYMODEEX* pMode);
/**
* \brief Queries window client size
*
* \param [in] hWnd Window to query
* \param [out] pWidth Client width
* \param [out] pHeight Client height
*/
void GetWindowClientSize(
HWND hWnd,
UINT* pWidth,
UINT* pHeight);
/**
* \brief Queries monitor size
*
* \param [in] hMonitor Monitor to query
* \param [out] pWidth Client width
* \param [out] pHeight Client height
*/
void GetMonitorClientSize(
HMONITOR hMonitor,
UINT* pWidth,
UINT* pHeight);
/**
* \brief Queries monitor rect
*
* \param [in] hMonitor Monitor to query
* \param [out] pRect The rect to return
*/
void GetMonitorRect(
HMONITOR hMonitor,
RECT* pRect);
}

View file

@ -0,0 +1,41 @@
#include "d3d9_device.h"
namespace dxvk {
void D3D9DeviceMutex::lock() {
while (!try_lock())
dxvk::this_thread::yield();
}
void D3D9DeviceMutex::unlock() {
if (likely(m_counter == 0))
m_owner.store(0, std::memory_order_release);
else
m_counter -= 1;
}
bool D3D9DeviceMutex::try_lock() {
uint32_t threadId = GetCurrentThreadId();
uint32_t expected = 0;
bool status = m_owner.compare_exchange_weak(
expected, threadId, std::memory_order_acquire);
if (status)
return true;
if (expected != threadId)
return false;
m_counter += 1;
return true;
}
D3D9Multithread::D3D9Multithread(
BOOL Protected)
: m_protected( Protected ) { }
}

101
src/d3d9/d3d9_multithread.h Normal file
View file

@ -0,0 +1,101 @@
#pragma once
#include "d3d9_include.h"
namespace dxvk {
/**
* \brief Device mutex
*
* Effectively implements a recursive spinlock
* which is used to lock the D3D9 device.
*/
class D3D9DeviceMutex {
public:
void lock();
void unlock();
bool try_lock();
private:
std::atomic<uint32_t> m_owner = { 0u };
uint32_t m_counter = { 0u };
};
/**
* \brief Device lock
*
* Lightweight RAII wrapper that implements
* a subset of the functionality provided by
* \c std::unique_lock, with the goal of being
* cheaper to construct and destroy.
*/
class D3D9DeviceLock {
public:
D3D9DeviceLock()
: m_mutex(nullptr) { }
D3D9DeviceLock(D3D9DeviceMutex& mutex)
: m_mutex(&mutex) {
mutex.lock();
}
D3D9DeviceLock(D3D9DeviceLock&& other)
: m_mutex(other.m_mutex) {
other.m_mutex = nullptr;
}
D3D9DeviceLock& operator = (D3D9DeviceLock&& other) {
if (m_mutex)
m_mutex->unlock();
m_mutex = other.m_mutex;
other.m_mutex = nullptr;
return *this;
}
~D3D9DeviceLock() {
if (m_mutex != nullptr)
m_mutex->unlock();
}
private:
D3D9DeviceMutex* m_mutex;
};
/**
* \brief D3D9 context lock
*/
class D3D9Multithread {
public:
D3D9Multithread(
BOOL Protected);
D3D9DeviceLock AcquireLock() {
return m_protected
? D3D9DeviceLock(m_mutex)
: D3D9DeviceLock();
}
private:
BOOL m_protected;
D3D9DeviceMutex m_mutex;
};
}

230
src/d3d9/d3d9_names.cpp Normal file
View file

@ -0,0 +1,230 @@
#include "d3d9_format.h"
namespace dxvk {
std::ostream& operator << (std::ostream& os, D3D9Format e) {
switch (e) {
ENUM_NAME(D3D9Format::Unknown);
ENUM_NAME(D3D9Format::R8G8B8);
ENUM_NAME(D3D9Format::A8R8G8B8);
ENUM_NAME(D3D9Format::X8R8G8B8);
ENUM_NAME(D3D9Format::R5G6B5);
ENUM_NAME(D3D9Format::X1R5G5B5);
ENUM_NAME(D3D9Format::A1R5G5B5);
ENUM_NAME(D3D9Format::A4R4G4B4);
ENUM_NAME(D3D9Format::R3G3B2);
ENUM_NAME(D3D9Format::A8);
ENUM_NAME(D3D9Format::A8R3G3B2);
ENUM_NAME(D3D9Format::X4R4G4B4);
ENUM_NAME(D3D9Format::A2B10G10R10);
ENUM_NAME(D3D9Format::A8B8G8R8);
ENUM_NAME(D3D9Format::X8B8G8R8);
ENUM_NAME(D3D9Format::G16R16);
ENUM_NAME(D3D9Format::A2R10G10B10);
ENUM_NAME(D3D9Format::A16B16G16R16);
ENUM_NAME(D3D9Format::A8P8);
ENUM_NAME(D3D9Format::P8);
ENUM_NAME(D3D9Format::L8);
ENUM_NAME(D3D9Format::A8L8);
ENUM_NAME(D3D9Format::A4L4);
ENUM_NAME(D3D9Format::V8U8);
ENUM_NAME(D3D9Format::L6V5U5);
ENUM_NAME(D3D9Format::X8L8V8U8);
ENUM_NAME(D3D9Format::Q8W8V8U8);
ENUM_NAME(D3D9Format::V16U16);
ENUM_NAME(D3D9Format::A2W10V10U10);
ENUM_NAME(D3D9Format::UYVY);
ENUM_NAME(D3D9Format::R8G8_B8G8);
ENUM_NAME(D3D9Format::YUY2);
ENUM_NAME(D3D9Format::G8R8_G8B8);
ENUM_NAME(D3D9Format::DXT1);
ENUM_NAME(D3D9Format::DXT2);
ENUM_NAME(D3D9Format::DXT3);
ENUM_NAME(D3D9Format::DXT4);
ENUM_NAME(D3D9Format::DXT5);
ENUM_NAME(D3D9Format::D16_LOCKABLE);
ENUM_NAME(D3D9Format::D32);
ENUM_NAME(D3D9Format::D15S1);
ENUM_NAME(D3D9Format::D24S8);
ENUM_NAME(D3D9Format::D24X8);
ENUM_NAME(D3D9Format::D24X4S4);
ENUM_NAME(D3D9Format::D16);
ENUM_NAME(D3D9Format::D32F_LOCKABLE);
ENUM_NAME(D3D9Format::D24FS8);
ENUM_NAME(D3D9Format::D32_LOCKABLE);
ENUM_NAME(D3D9Format::S8_LOCKABLE);
ENUM_NAME(D3D9Format::L16);
ENUM_NAME(D3D9Format::VERTEXDATA);
ENUM_NAME(D3D9Format::INDEX16);
ENUM_NAME(D3D9Format::INDEX32);
ENUM_NAME(D3D9Format::Q16W16V16U16);
ENUM_NAME(D3D9Format::MULTI2_ARGB8);
ENUM_NAME(D3D9Format::R16F);
ENUM_NAME(D3D9Format::G16R16F);
ENUM_NAME(D3D9Format::A16B16G16R16F);
ENUM_NAME(D3D9Format::R32F);
ENUM_NAME(D3D9Format::G32R32F);
ENUM_NAME(D3D9Format::A32B32G32R32F);
ENUM_NAME(D3D9Format::CxV8U8);
ENUM_NAME(D3D9Format::A1);
ENUM_NAME(D3D9Format::A2B10G10R10_XR_BIAS);
ENUM_NAME(D3D9Format::BINARYBUFFER);
// Driver Hacks / Unofficial Formats
ENUM_NAME(D3D9Format::ATI1);
ENUM_NAME(D3D9Format::ATI2);
ENUM_NAME(D3D9Format::INST);
ENUM_NAME(D3D9Format::DF24);
ENUM_NAME(D3D9Format::DF16);
ENUM_NAME(D3D9Format::NULL_FORMAT);
ENUM_NAME(D3D9Format::GET4);
ENUM_NAME(D3D9Format::GET1);
ENUM_NAME(D3D9Format::NVDB);
ENUM_NAME(D3D9Format::A2M1);
ENUM_NAME(D3D9Format::A2M0);
ENUM_NAME(D3D9Format::ATOC);
ENUM_NAME(D3D9Format::INTZ);
ENUM_NAME(D3D9Format::RAWZ);
ENUM_NAME(D3D9Format::RESZ);
ENUM_NAME(D3D9Format::NV11);
ENUM_NAME(D3D9Format::NV12);
ENUM_NAME(D3D9Format::P010);
ENUM_NAME(D3D9Format::P016);
ENUM_NAME(D3D9Format::Y210);
ENUM_NAME(D3D9Format::Y216);
ENUM_NAME(D3D9Format::Y410);
ENUM_NAME(D3D9Format::AYUV);
ENUM_NAME(D3D9Format::YV12);
ENUM_NAME(D3D9Format::OPAQUE_420);
ENUM_NAME(D3D9Format::AI44);
ENUM_NAME(D3D9Format::IA44);
ENUM_NAME(D3D9Format::R2VB);
ENUM_NAME(D3D9Format::COPM);
ENUM_NAME(D3D9Format::SSAA);
ENUM_NAME(D3D9Format::AL16);
ENUM_NAME(D3D9Format::R16);
ENUM_NAME(D3D9Format::EXT1);
ENUM_NAME(D3D9Format::FXT1);
ENUM_NAME(D3D9Format::GXT1);
ENUM_NAME(D3D9Format::HXT1);
ENUM_DEFAULT(e);
}
}
std::ostream& operator << (std::ostream& os, D3DRENDERSTATETYPE e) {
switch (e) {
ENUM_NAME(D3DRS_ZENABLE);
ENUM_NAME(D3DRS_FILLMODE);
ENUM_NAME(D3DRS_SHADEMODE);
ENUM_NAME(D3DRS_ZWRITEENABLE);
ENUM_NAME(D3DRS_ALPHATESTENABLE);
ENUM_NAME(D3DRS_LASTPIXEL);
ENUM_NAME(D3DRS_SRCBLEND);
ENUM_NAME(D3DRS_DESTBLEND);
ENUM_NAME(D3DRS_CULLMODE);
ENUM_NAME(D3DRS_ZFUNC);
ENUM_NAME(D3DRS_ALPHAREF);
ENUM_NAME(D3DRS_ALPHAFUNC);
ENUM_NAME(D3DRS_DITHERENABLE);
ENUM_NAME(D3DRS_ALPHABLENDENABLE);
ENUM_NAME(D3DRS_FOGENABLE);
ENUM_NAME(D3DRS_SPECULARENABLE);
ENUM_NAME(D3DRS_FOGCOLOR);
ENUM_NAME(D3DRS_FOGTABLEMODE);
ENUM_NAME(D3DRS_FOGSTART);
ENUM_NAME(D3DRS_FOGEND);
ENUM_NAME(D3DRS_FOGDENSITY);
ENUM_NAME(D3DRS_RANGEFOGENABLE);
ENUM_NAME(D3DRS_STENCILENABLE);
ENUM_NAME(D3DRS_STENCILFAIL);
ENUM_NAME(D3DRS_STENCILZFAIL);
ENUM_NAME(D3DRS_STENCILPASS);
ENUM_NAME(D3DRS_STENCILFUNC);
ENUM_NAME(D3DRS_STENCILREF);
ENUM_NAME(D3DRS_STENCILMASK);
ENUM_NAME(D3DRS_STENCILWRITEMASK);
ENUM_NAME(D3DRS_TEXTUREFACTOR);
ENUM_NAME(D3DRS_WRAP0);
ENUM_NAME(D3DRS_WRAP1);
ENUM_NAME(D3DRS_WRAP2);
ENUM_NAME(D3DRS_WRAP3);
ENUM_NAME(D3DRS_WRAP4);
ENUM_NAME(D3DRS_WRAP5);
ENUM_NAME(D3DRS_WRAP6);
ENUM_NAME(D3DRS_WRAP7);
ENUM_NAME(D3DRS_CLIPPING);
ENUM_NAME(D3DRS_LIGHTING);
ENUM_NAME(D3DRS_AMBIENT);
ENUM_NAME(D3DRS_FOGVERTEXMODE);
ENUM_NAME(D3DRS_COLORVERTEX);
ENUM_NAME(D3DRS_LOCALVIEWER);
ENUM_NAME(D3DRS_NORMALIZENORMALS);
ENUM_NAME(D3DRS_DIFFUSEMATERIALSOURCE);
ENUM_NAME(D3DRS_SPECULARMATERIALSOURCE);
ENUM_NAME(D3DRS_AMBIENTMATERIALSOURCE);
ENUM_NAME(D3DRS_EMISSIVEMATERIALSOURCE);
ENUM_NAME(D3DRS_VERTEXBLEND);
ENUM_NAME(D3DRS_CLIPPLANEENABLE);
ENUM_NAME(D3DRS_POINTSIZE);
ENUM_NAME(D3DRS_POINTSIZE_MIN);
ENUM_NAME(D3DRS_POINTSPRITEENABLE);
ENUM_NAME(D3DRS_POINTSCALEENABLE);
ENUM_NAME(D3DRS_POINTSCALE_A);
ENUM_NAME(D3DRS_POINTSCALE_B);
ENUM_NAME(D3DRS_POINTSCALE_C);
ENUM_NAME(D3DRS_MULTISAMPLEANTIALIAS);
ENUM_NAME(D3DRS_MULTISAMPLEMASK);
ENUM_NAME(D3DRS_PATCHEDGESTYLE);
ENUM_NAME(D3DRS_DEBUGMONITORTOKEN);
ENUM_NAME(D3DRS_POINTSIZE_MAX);
ENUM_NAME(D3DRS_INDEXEDVERTEXBLENDENABLE);
ENUM_NAME(D3DRS_COLORWRITEENABLE);
ENUM_NAME(D3DRS_TWEENFACTOR);
ENUM_NAME(D3DRS_BLENDOP);
ENUM_NAME(D3DRS_POSITIONDEGREE);
ENUM_NAME(D3DRS_NORMALDEGREE);
ENUM_NAME(D3DRS_SCISSORTESTENABLE);
ENUM_NAME(D3DRS_SLOPESCALEDEPTHBIAS);
ENUM_NAME(D3DRS_ANTIALIASEDLINEENABLE);
ENUM_NAME(D3DRS_MINTESSELLATIONLEVEL);
ENUM_NAME(D3DRS_MAXTESSELLATIONLEVEL);
ENUM_NAME(D3DRS_ADAPTIVETESS_X);
ENUM_NAME(D3DRS_ADAPTIVETESS_Y);
ENUM_NAME(D3DRS_ADAPTIVETESS_Z);
ENUM_NAME(D3DRS_ADAPTIVETESS_W);
ENUM_NAME(D3DRS_ENABLEADAPTIVETESSELLATION);
ENUM_NAME(D3DRS_TWOSIDEDSTENCILMODE);
ENUM_NAME(D3DRS_CCW_STENCILFAIL);
ENUM_NAME(D3DRS_CCW_STENCILZFAIL);
ENUM_NAME(D3DRS_CCW_STENCILPASS);
ENUM_NAME(D3DRS_CCW_STENCILFUNC);
ENUM_NAME(D3DRS_COLORWRITEENABLE1);
ENUM_NAME(D3DRS_COLORWRITEENABLE2);
ENUM_NAME(D3DRS_COLORWRITEENABLE3);
ENUM_NAME(D3DRS_BLENDFACTOR);
ENUM_NAME(D3DRS_SRGBWRITEENABLE);
ENUM_NAME(D3DRS_DEPTHBIAS);
ENUM_NAME(D3DRS_WRAP8);
ENUM_NAME(D3DRS_WRAP9);
ENUM_NAME(D3DRS_WRAP10);
ENUM_NAME(D3DRS_WRAP11);
ENUM_NAME(D3DRS_WRAP12);
ENUM_NAME(D3DRS_WRAP13);
ENUM_NAME(D3DRS_WRAP14);
ENUM_NAME(D3DRS_WRAP15);
ENUM_NAME(D3DRS_SEPARATEALPHABLENDENABLE);
ENUM_NAME(D3DRS_SRCBLENDALPHA);
ENUM_NAME(D3DRS_DESTBLENDALPHA);
ENUM_NAME(D3DRS_BLENDOPALPHA);
ENUM_DEFAULT(e);
}
}
}

7
src/d3d9/d3d9_names.h Normal file
View file

@ -0,0 +1,7 @@
#include "d3d9_include.h"
namespace dxvk {
std::ostream& operator << (std::ostream& os, D3DRENDERSTATETYPE e);
}

76
src/d3d9/d3d9_options.cpp Normal file
View file

@ -0,0 +1,76 @@
#include "d3d9_options.h"
#include "d3d9_caps.h"
namespace dxvk {
static int32_t parsePciId(const std::string& str) {
if (str.size() != 4)
return -1;
int32_t id = 0;
for (size_t i = 0; i < str.size(); i++) {
id *= 16;
if (str[i] >= '0' && str[i] <= '9')
id += str[i] - '0';
else if (str[i] >= 'A' && str[i] <= 'F')
id += str[i] - 'A' + 10;
else if (str[i] >= 'a' && str[i] <= 'f')
id += str[i] - 'a' + 10;
else
return -1;
}
return id;
}
D3D9Options::D3D9Options(const Rc<DxvkDevice>& device, const Config& config) {
const Rc<DxvkAdapter> adapter = device != nullptr ? device->adapter() : nullptr;
// Fetch these as a string representing a hexadecimal number and parse it.
this->customVendorId = parsePciId(config.getOption<std::string>("d3d9.customVendorId"));
this->customDeviceId = parsePciId(config.getOption<std::string>("d3d9.customDeviceId"));
this->customDeviceDesc = config.getOption<std::string>("d3d9.customDeviceDesc");
const int32_t vendorId = this->customDeviceId != -1 ? this->customDeviceId : (adapter != nullptr ? adapter->deviceProperties().vendorID : 0);
this->maxFrameLatency = config.getOption<int32_t> ("d3d9.maxFrameLatency", 0);
this->presentInterval = config.getOption<int32_t> ("d3d9.presentInterval", -1);
this->shaderModel = config.getOption<int32_t> ("d3d9.shaderModel", 3);
this->evictManagedOnUnlock = config.getOption<bool> ("d3d9.evictManagedOnUnlock", false);
this->dpiAware = config.getOption<bool> ("d3d9.dpiAware", true);
this->allowLockFlagReadonly = config.getOption<bool> ("d3d9.allowLockFlagReadonly", true);
this->strictConstantCopies = config.getOption<bool> ("d3d9.strictConstantCopies", false);
this->strictPow = config.getOption<bool> ("d3d9.strictPow", true);
this->lenientClear = config.getOption<bool> ("d3d9.lenientClear", false);
this->numBackBuffers = config.getOption<int32_t> ("d3d9.numBackBuffers", 0);
this->deferSurfaceCreation = config.getOption<bool> ("d3d9.deferSurfaceCreation", false);
this->samplerAnisotropy = config.getOption<int32_t> ("d3d9.samplerAnisotropy", -1);
this->maxAvailableMemory = config.getOption<int32_t> ("d3d9.maxAvailableMemory", 4096);
this->supportDFFormats = config.getOption<bool> ("d3d9.supportDFFormats", true);
this->supportX4R4G4B4 = config.getOption<bool> ("d3d9.supportX4R4G4B4", true);
this->supportD32 = config.getOption<bool> ("d3d9.supportD32", true);
this->swvpFloatCount = config.getOption<int32_t> ("d3d9.swvpFloatCount", caps::MaxFloatConstantsSoftware);
this->swvpIntCount = config.getOption<int32_t> ("d3d9.swvpIntCount", caps::MaxOtherConstantsSoftware);
this->swvpBoolCount = config.getOption<int32_t> ("d3d9.swvpBoolCount", caps::MaxOtherConstantsSoftware);
this->disableA8RT = config.getOption<bool> ("d3d9.disableA8RT", false);
this->invariantPosition = config.getOption<bool> ("d3d9.invariantPosition", false);
this->memoryTrackTest = config.getOption<bool> ("d3d9.memoryTrackTest", false);
this->supportVCache = config.getOption<bool> ("d3d9.supportVCache", vendorId == 0x10de);
this->enableDialogMode = config.getOption<bool> ("d3d9.enableDialogMode", false);
this->forceAspectRatio = config.getOption<std::string>("d3d9.forceAspectRatio", "");
// If we are not Nvidia, enable general hazards.
this->generalHazards = adapter == nullptr || !adapter->matchesDriver(DxvkGpuVendor::Nvidia, VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR, 0, 0);
applyTristate(this->generalHazards, config.getOption<Tristate>("d3d9.generalHazards", Tristate::Auto));
this->d3d9FloatEmulation = true; // <-- Future Extension?
applyTristate(this->d3d9FloatEmulation, config.getOption<Tristate>("d3d9.floatEmulation", Tristate::Auto));
}
}

117
src/d3d9/d3d9_options.h Normal file
View file

@ -0,0 +1,117 @@
#pragma once
#include "../util/config/config.h"
#include "../dxvk/dxvk_device.h"
#include "d3d9_include.h"
namespace dxvk {
struct D3D9Options {
D3D9Options(const Rc<DxvkDevice>& device, const Config& config);
/// Override PCI vendor and device IDs reported to the
/// application. This may make apps think they are running
/// on a different GPU than they do and behave differently.
int32_t customVendorId;
int32_t customDeviceId;
std::string customDeviceDesc;
/// Present interval. Overrides the value
/// in D3DPRESENT_PARAMS used in swapchain present.
int32_t presentInterval;
/// Override maximum frame latency if the app specifies
/// a higher value. May help with frame timing issues.
int32_t maxFrameLatency;
/// Set the max shader model the device can support in the caps.
int32_t shaderModel;
/// Whether or not managed resources should stay in memory until unlock, or until manually evicted.
bool evictManagedOnUnlock;
/// Whether or not to set the process as DPI aware in Windows when the API interface is created.
bool dpiAware;
/// Handle D3DLOCK_READONLY properly.
///
/// Risen 1 writes to buffers mapped with readonly.
bool allowLockFlagReadonly;
/// True: Copy our constant set into UBO if we are relative indexing ever.
/// False: Copy our constant set into UBO if we are relative indexing at the start of a defined constant
/// Why?: In theory, FXC should never generate code where this would be an issue.
bool strictConstantCopies;
/// Whether or not we should care about pow(0, 0) = 1
bool strictPow;
/// Whether or not to do a fast path clear if we're close enough to the whole render target.
bool lenientClear;
/// Back buffer count for the Vulkan swap chain.
/// Overrides buffer count in present parameters.
int32_t numBackBuffers;
/// Defer surface creation
bool deferSurfaceCreation;
/// Whether to transition to general
/// for rendering hazards
bool generalHazards;
/// Anisotropic filter override
///
/// Enforces anisotropic filtering with the
/// given anisotropy value for all samplers.
int32_t samplerAnisotropy;
/// Max available memory override
///
/// Changes the max initial value used in
/// tracking and GetAvailableTextureMem
uint32_t maxAvailableMemory;
/// D3D9 Floating Point Emulation (anything * 0 = 0)
bool d3d9FloatEmulation;
/// Support the DF16 & DF24 texture format
bool supportDFFormats;
/// Support X4R4G4B4
bool supportX4R4G4B4;
/// Support D32
bool supportD32;
/// SWVP Constant Limits
int32_t swvpFloatCount;
int32_t swvpIntCount;
int32_t swvpBoolCount;
/// Disable D3DFMT_A8 for render targets.
/// Specifically to work around a game
/// bug in The Sims 2 that happens on native too!
bool disableA8RT;
/// Work around a NV driver quirk
/// Fixes flickering/z-fighting in some games.
bool invariantPosition;
/// Whether or not to respect memory tracking for
/// failing resource allocation.
bool memoryTrackTest;
/// Support VCACHE query
bool supportVCache;
/// Forced aspect ratio, disable other modes
std::string forceAspectRatio;
/// Enable dialog mode (ie. no exclusive fullscreen)
bool enableDialogMode;
};
}

312
src/d3d9/d3d9_query.cpp Normal file
View file

@ -0,0 +1,312 @@
#include "d3d9_query.h"
#include "d3d9_device.h"
namespace dxvk {
D3D9Query::D3D9Query(
D3D9DeviceEx* pDevice,
D3DQUERYTYPE QueryType)
: D3D9DeviceChild<IDirect3DQuery9>(pDevice)
, m_queryType (QueryType)
, m_state (D3D9_VK_QUERY_INITIAL) {
Rc<DxvkDevice> dxvkDevice = m_parent->GetDXVKDevice();
switch (m_queryType) {
case D3DQUERYTYPE_VCACHE:
if (!pDevice->GetOptions()->supportVCache)
throw DxvkError(str::format("D3D9Query: Unsupported query type ", m_queryType, " (from d3d9.supportVCache)"));
break;
case D3DQUERYTYPE_EVENT:
m_event[0] = dxvkDevice->createGpuEvent();
break;
case D3DQUERYTYPE_OCCLUSION:
m_query[0] = dxvkDevice->createGpuQuery(
VK_QUERY_TYPE_OCCLUSION,
VK_QUERY_CONTROL_PRECISE_BIT, 0);
break;
case D3DQUERYTYPE_TIMESTAMP:
m_query[0] = dxvkDevice->createGpuQuery(
VK_QUERY_TYPE_TIMESTAMP, 0, 0);
break;
case D3DQUERYTYPE_TIMESTAMPDISJOINT:
for (uint32_t i = 0; i < 2; i++) {
m_query[i] = dxvkDevice->createGpuQuery(
VK_QUERY_TYPE_TIMESTAMP, 0, 0);
}
break;
case D3DQUERYTYPE_TIMESTAMPFREQ:
break;
case D3DQUERYTYPE_VERTEXSTATS:
m_query[0] = dxvkDevice->createGpuQuery(
VK_QUERY_TYPE_PIPELINE_STATISTICS, 0, 0);
break;
default:
throw DxvkError(str::format("D3D9Query: Unsupported query type ", m_queryType));
}
}
HRESULT STDMETHODCALLTYPE D3D9Query::QueryInterface(REFIID riid, void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3DQuery9)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9Query::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
D3DQUERYTYPE STDMETHODCALLTYPE D3D9Query::GetType() {
return m_queryType;
}
DWORD STDMETHODCALLTYPE D3D9Query::GetDataSize() {
switch (m_queryType) {
case D3DQUERYTYPE_VCACHE: return sizeof(D3DDEVINFO_VCACHE);
case D3DQUERYTYPE_RESOURCEMANAGER: return sizeof(D3DDEVINFO_RESOURCEMANAGER);
case D3DQUERYTYPE_VERTEXSTATS: return sizeof(D3DDEVINFO_D3DVERTEXSTATS);
case D3DQUERYTYPE_EVENT: return sizeof(BOOL);
case D3DQUERYTYPE_OCCLUSION: return sizeof(DWORD);
case D3DQUERYTYPE_TIMESTAMP: return sizeof(UINT64);
case D3DQUERYTYPE_TIMESTAMPDISJOINT: return sizeof(BOOL);
case D3DQUERYTYPE_TIMESTAMPFREQ: return sizeof(UINT64);
case D3DQUERYTYPE_PIPELINETIMINGS: return sizeof(D3DDEVINFO_D3D9PIPELINETIMINGS);
case D3DQUERYTYPE_INTERFACETIMINGS: return sizeof(D3DDEVINFO_D3D9INTERFACETIMINGS);
case D3DQUERYTYPE_VERTEXTIMINGS: return sizeof(D3DDEVINFO_D3D9STAGETIMINGS);
case D3DQUERYTYPE_PIXELTIMINGS: return sizeof(D3DDEVINFO_D3D9PIPELINETIMINGS);
case D3DQUERYTYPE_BANDWIDTHTIMINGS: return sizeof(D3DDEVINFO_D3D9BANDWIDTHTIMINGS);
case D3DQUERYTYPE_CACHEUTILIZATION: return sizeof(D3DDEVINFO_D3D9CACHEUTILIZATION);
default: return 0;
}
}
HRESULT STDMETHODCALLTYPE D3D9Query::Issue(DWORD dwIssueFlags) {
// Note: No need to submit to CS if we don't do anything!
if (dwIssueFlags == D3DISSUE_BEGIN) {
if (QueryBeginnable(m_queryType)) {
if (m_state == D3D9_VK_QUERY_BEGUN && QueryEndable(m_queryType))
m_parent->End(this);
m_parent->Begin(this);
m_state = D3D9_VK_QUERY_BEGUN;
}
}
else {
if (QueryEndable(m_queryType)) {
if (m_state != D3D9_VK_QUERY_BEGUN && QueryBeginnable(m_queryType))
m_parent->Begin(this);
m_resetCtr.fetch_add(1, std::memory_order_acquire);
m_parent->End(this);
}
m_state = D3D9_VK_QUERY_ENDED;
}
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE D3D9Query::GetData(void* pData, DWORD dwSize, DWORD dwGetDataFlags) {
HRESULT hr = this->GetQueryData(pData, dwSize);
bool flush = dwGetDataFlags & D3DGETDATA_FLUSH;
// If we get S_FALSE and it's not from the fact
// they didn't call end, do some flushy stuff...
if (flush && hr == S_FALSE && m_state != D3D9_VK_QUERY_BEGUN) {
this->NotifyStall();
m_parent->FlushImplicit(FALSE);
}
return hr;
}
HRESULT D3D9Query::GetQueryData(void* pData, DWORD dwSize) {
// Let the game know that calling end might be a good idea...
if (m_state == D3D9_VK_QUERY_BEGUN)
return S_FALSE;
if (unlikely(!pData && dwSize))
return D3DERR_INVALIDCALL;
// The game forgot to even issue the query!
// Let's do it for them...
// This will issue both the begin, and the end.
if (m_state == D3D9_VK_QUERY_INITIAL)
this->Issue(D3DISSUE_END);
if (m_resetCtr != 0u)
return S_FALSE;
if (m_queryType == D3DQUERYTYPE_EVENT) {
DxvkGpuEventStatus status = m_event[0]->test();
if (status == DxvkGpuEventStatus::Invalid)
return D3DERR_INVALIDCALL;
bool signaled = status == DxvkGpuEventStatus::Signaled;
if (pData != nullptr)
*static_cast<BOOL*>(pData) = signaled;
return signaled ? D3D_OK : S_FALSE;
}
else {
std::array<DxvkQueryData, MaxGpuQueries> queryData = { };
for (uint32_t i = 0; i < MaxGpuQueries && m_query[i] != nullptr; i++) {
DxvkGpuQueryStatus status = m_query[i]->getData(queryData[i]);
if (status == DxvkGpuQueryStatus::Invalid
|| status == DxvkGpuQueryStatus::Failed)
return D3DERR_INVALIDCALL;
if (status == DxvkGpuQueryStatus::Pending)
return S_FALSE;
}
if (pData == nullptr)
return D3D_OK;
auto* data = static_cast<D3D9_QUERY_DATA*>(pData);
switch (m_queryType) {
case D3DQUERYTYPE_VCACHE:
// Don't know what the hell any of this means.
// Nor do I care. This just makes games work.
data->VCache.Pattern = MAKEFOURCC('H', 'C', 'A', 'C');
data->VCache.OptMethod = 1;
data->VCache.CacheSize = 24;
data->VCache.MagicNumber = 20;
return D3D_OK;
case D3DQUERYTYPE_OCCLUSION:
data->Occlusion = DWORD(queryData[0].occlusion.samplesPassed);
return D3D_OK;
case D3DQUERYTYPE_TIMESTAMP:
data->Timestamp = queryData[0].timestamp.time;
return D3D_OK;
case D3DQUERYTYPE_TIMESTAMPDISJOINT:
data->TimestampDisjoint = queryData[0].timestamp.time < queryData[1].timestamp.time;
return D3D_OK;
case D3DQUERYTYPE_TIMESTAMPFREQ:
data->TimestampFreq = GetTimestampQueryFrequency();
return D3D_OK;
case D3DQUERYTYPE_VERTEXSTATS:
data->VertexStats.NumRenderedTriangles = queryData[0].statistic.iaPrimitives;
data->VertexStats.NumExtraClippingTriangles = queryData[0].statistic.clipPrimitives;
return D3D_OK;
default:
return D3D_OK;
}
}
}
UINT64 D3D9Query::GetTimestampQueryFrequency() const {
Rc<DxvkDevice> device = m_parent->GetDXVKDevice();
Rc<DxvkAdapter> adapter = device->adapter();
VkPhysicalDeviceLimits limits = adapter->deviceProperties().limits;
return uint64_t(1'000'000'000.0f / limits.timestampPeriod);
}
void D3D9Query::Begin(DxvkContext* ctx) {
switch (m_queryType) {
case D3DQUERYTYPE_OCCLUSION:
case D3DQUERYTYPE_VERTEXSTATS:
ctx->beginQuery(m_query[0]);
break;
case D3DQUERYTYPE_TIMESTAMPDISJOINT:
ctx->writeTimestamp(m_query[1]);
break;
default: break;
}
}
void D3D9Query::End(DxvkContext* ctx) {
switch (m_queryType) {
case D3DQUERYTYPE_TIMESTAMP:
case D3DQUERYTYPE_TIMESTAMPDISJOINT:
ctx->writeTimestamp(m_query[0]);
break;
case D3DQUERYTYPE_VERTEXSTATS:
case D3DQUERYTYPE_OCCLUSION:
ctx->endQuery(m_query[0]);
break;
case D3DQUERYTYPE_EVENT:
ctx->signalGpuEvent(m_event[0]);
break;
default: break;
}
m_resetCtr.fetch_sub(1, std::memory_order_release);
}
bool D3D9Query::QueryBeginnable(D3DQUERYTYPE QueryType) {
return QueryType == D3DQUERYTYPE_OCCLUSION
|| QueryType == D3DQUERYTYPE_VERTEXSTATS
|| QueryType == D3DQUERYTYPE_TIMESTAMPDISJOINT;
}
bool D3D9Query::QueryEndable(D3DQUERYTYPE QueryType) {
return QueryBeginnable(QueryType)
|| QueryType == D3DQUERYTYPE_TIMESTAMP
|| QueryType == D3DQUERYTYPE_EVENT;
}
HRESULT D3D9Query::QuerySupported(D3DQUERYTYPE QueryType) {
switch (QueryType) {
case D3DQUERYTYPE_VCACHE:
case D3DQUERYTYPE_EVENT:
case D3DQUERYTYPE_OCCLUSION:
case D3DQUERYTYPE_TIMESTAMP:
case D3DQUERYTYPE_TIMESTAMPDISJOINT:
case D3DQUERYTYPE_TIMESTAMPFREQ:
case D3DQUERYTYPE_VERTEXSTATS:
return D3D_OK;
default:
return D3DERR_NOTAVAILABLE;
}
}
}

88
src/d3d9/d3d9_query.h Normal file
View file

@ -0,0 +1,88 @@
#pragma once
#include "d3d9_device_child.h"
#include "../dxvk/dxvk_context.h"
namespace dxvk {
enum D3D9_VK_QUERY_STATE : uint32_t {
D3D9_VK_QUERY_INITIAL,
D3D9_VK_QUERY_BEGUN,
D3D9_VK_QUERY_ENDED,
};
union D3D9_QUERY_DATA {
D3DDEVINFO_VCACHE VCache;
DWORD Occlusion;
UINT64 Timestamp;
BOOL TimestampDisjoint;
UINT64 TimestampFreq;
D3DDEVINFO_D3DVERTEXSTATS VertexStats;
};
class D3D9Query : public D3D9DeviceChild<IDirect3DQuery9> {
constexpr static uint32_t MaxGpuQueries = 2;
constexpr static uint32_t MaxGpuEvents = 1;
public:
D3D9Query(
D3D9DeviceEx* pDevice,
D3DQUERYTYPE QueryType);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
D3DQUERYTYPE STDMETHODCALLTYPE GetType() final;
DWORD STDMETHODCALLTYPE GetDataSize() final;
HRESULT STDMETHODCALLTYPE Issue(DWORD dwIssueFlags) final;
HRESULT STDMETHODCALLTYPE GetData(void* pData, DWORD dwSize, DWORD dwGetDataFlags) final;
HRESULT GetQueryData(void* pData, DWORD dwSize);
void Begin(DxvkContext* ctx);
void End(DxvkContext* ctx);
static bool QueryBeginnable(D3DQUERYTYPE QueryType);
static bool QueryEndable(D3DQUERYTYPE QueryType);
static HRESULT QuerySupported(D3DQUERYTYPE QueryType);
bool IsEvent() const {
return m_queryType == D3DQUERYTYPE_EVENT;
}
bool IsStalling() const {
return m_stallFlag;
}
void NotifyEnd() {
m_stallMask <<= 1;
}
void NotifyStall() {
m_stallMask |= 1;
m_stallFlag |= bit::popcnt(m_stallMask) >= 16;
}
private:
D3DQUERYTYPE m_queryType;
D3D9_VK_QUERY_STATE m_state;
std::array<Rc<DxvkGpuQuery>, MaxGpuQueries> m_query;
std::array<Rc<DxvkGpuEvent>, MaxGpuEvents> m_event;
uint32_t m_stallMask = 0;
bool m_stallFlag = false;
std::atomic<uint32_t> m_resetCtr = { 0u };
UINT64 GetTimestampQueryFrequency() const;
};
}

87
src/d3d9/d3d9_resource.h Normal file
View file

@ -0,0 +1,87 @@
#pragma once
#include "d3d9_device_child.h"
#include "../util/com/com_private_data.h"
namespace dxvk {
template <typename... Type>
class D3D9Resource : public D3D9DeviceChild<Type...> {
public:
D3D9Resource(D3D9DeviceEx* pDevice)
: D3D9DeviceChild<Type...>(pDevice)
, m_priority ( 0 ) { }
HRESULT STDMETHODCALLTYPE SetPrivateData(
REFGUID refguid,
const void* pData,
DWORD SizeOfData,
DWORD Flags) final {
HRESULT hr;
if (Flags & D3DSPD_IUNKNOWN) {
IUnknown* unknown =
const_cast<IUnknown*>(
reinterpret_cast<const IUnknown*>(pData));
hr = m_privateData.setInterface(
refguid, unknown);
}
else
hr = m_privateData.setData(
refguid, SizeOfData, pData);
if (FAILED(hr))
return D3DERR_INVALIDCALL;
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE GetPrivateData(
REFGUID refguid,
void* pData,
DWORD* pSizeOfData) final {
HRESULT hr = m_privateData.getData(
refguid, reinterpret_cast<UINT*>(pSizeOfData), pData);
if (FAILED(hr))
return D3DERR_INVALIDCALL;
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE FreePrivateData(REFGUID refguid) final {
HRESULT hr = m_privateData.setData(refguid, 0, nullptr);
if (FAILED(hr))
return D3DERR_INVALIDCALL;
return D3D_OK;
}
DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) {
DWORD oldPriority = m_priority;
m_priority = PriorityNew;
return oldPriority;
}
DWORD STDMETHODCALLTYPE GetPriority() {
return m_priority;
}
void STDMETHODCALLTYPE PreLoad() {
}
protected:
DWORD m_priority;
private:
ComPrivateData m_privateData;
};
}

47
src/d3d9/d3d9_sampler.cpp Normal file
View file

@ -0,0 +1,47 @@
#include "d3d9_sampler.h"
namespace dxvk {
size_t D3D9SamplerKeyHash::operator () (const D3D9SamplerKey& key) const {
DxvkHashState state;
std::hash<DWORD> dhash;
std::hash<D3DTEXTUREADDRESS> tahash;
std::hash<D3DTEXTUREFILTERTYPE> tfhash;
std::hash<float> fhash;
state.add(tahash(key.AddressU));
state.add(tahash(key.AddressV));
state.add(tahash(key.AddressW));
state.add(tfhash(key.MagFilter));
state.add(tfhash(key.MinFilter));
state.add(tfhash(key.MipFilter));
state.add(dhash (key.MaxAnisotropy));
state.add(fhash (key.MipmapLodBias));
state.add(dhash (key.MaxMipLevel));
state.add(fhash (key.BorderColor[0]));
state.add(fhash (key.BorderColor[1]));
state.add(fhash (key.BorderColor[2]));
state.add(fhash (key.BorderColor[3]));
return state;
}
bool D3D9SamplerKeyEq::operator () (const D3D9SamplerKey& a, const D3D9SamplerKey& b) const {
return a.AddressU == b.AddressU
&& a.AddressV == b.AddressV
&& a.AddressW == b.AddressW
&& a.MagFilter == b.MagFilter
&& a.MinFilter == b.MinFilter
&& a.MipFilter == b.MipFilter
&& a.MaxAnisotropy == b.MaxAnisotropy
&& a.MipmapLodBias == b.MipmapLodBias
&& a.MaxMipLevel == b.MaxMipLevel
&& a.BorderColor[0] == b.BorderColor[0]
&& a.BorderColor[1] == b.BorderColor[1]
&& a.BorderColor[2] == b.BorderColor[2]
&& a.BorderColor[3] == b.BorderColor[3];
}
}

75
src/d3d9/d3d9_sampler.h Normal file
View file

@ -0,0 +1,75 @@
#pragma once
#include "d3d9_include.h"
#include "d3d9_util.h"
#include "../dxvk/dxvk_hash.h"
#include "../util/util_math.h"
namespace dxvk {
struct D3D9SamplerKey {
D3DTEXTUREADDRESS AddressU;
D3DTEXTUREADDRESS AddressV;
D3DTEXTUREADDRESS AddressW;
D3DTEXTUREFILTERTYPE MagFilter;
D3DTEXTUREFILTERTYPE MinFilter;
D3DTEXTUREFILTERTYPE MipFilter;
DWORD MaxAnisotropy;
float MipmapLodBias;
DWORD MaxMipLevel;
float BorderColor[4];
};
struct D3D9SamplerKeyHash {
size_t operator () (const D3D9SamplerKey& key) const;
};
struct D3D9SamplerKeyEq {
bool operator () (const D3D9SamplerKey& a, const D3D9SamplerKey& b) const;
};
inline void NormalizeSamplerKey(D3D9SamplerKey& key) {
key.AddressU = std::clamp(key.AddressU, D3DTADDRESS_WRAP, D3DTADDRESS_MIRRORONCE);
key.AddressV = std::clamp(key.AddressV, D3DTADDRESS_WRAP, D3DTADDRESS_MIRRORONCE);
key.AddressW = std::clamp(key.AddressW, D3DTADDRESS_WRAP, D3DTADDRESS_MIRRORONCE);
key.MagFilter = std::clamp(key.MagFilter, D3DTEXF_NONE, D3DTEXF_ANISOTROPIC);
key.MinFilter = std::clamp(key.MinFilter, D3DTEXF_NONE, D3DTEXF_ANISOTROPIC);
key.MipFilter = std::clamp(key.MipFilter, D3DTEXF_NONE, D3DTEXF_ANISOTROPIC);
key.MaxAnisotropy = std::clamp<DWORD>(key.MaxAnisotropy, 0, 16);
if (key.MipFilter == D3DTEXF_NONE) {
// May as well try and keep slots down.
key.MipmapLodBias = 0;
}
else {
// Games also pass NAN/INF here, this accounts for that.
if (unlikely(std::isnan(key.MipmapLodBias)))
key.MipmapLodBias = 0.0f;
// Clamp between -15.0f and 15.0f, matching mip limits of d3d9.
key.MipmapLodBias = std::clamp(key.MipmapLodBias, -15.0f, 15.0f);
// Round to the nearest .5
// Fixes sampler leaks in UE3 games w/ mip streaming
// eg. Borderlands 2
key.MipmapLodBias = std::round(key.MipmapLodBias * 2.0f) / 2.0f;
}
if (key.AddressU != D3DTADDRESS_BORDER
&& key.AddressV != D3DTADDRESS_BORDER
&& key.AddressW != D3DTADDRESS_BORDER) {
for (auto& val : key.BorderColor)
val = 0.0f;
}
else {
for (auto& val : key.BorderColor)
val = val >= 0.5f ? 1.0f : 0.0f;
}
}
}

143
src/d3d9/d3d9_shader.cpp Normal file
View file

@ -0,0 +1,143 @@
#include "d3d9_shader.h"
#include "d3d9_device.h"
#include "d3d9_util.h"
namespace dxvk {
D3D9CommonShader::D3D9CommonShader() {}
D3D9CommonShader::D3D9CommonShader(
D3D9DeviceEx* pDevice,
VkShaderStageFlagBits ShaderStage,
const Sha1Hash* pHash,
const DxsoModuleInfo* pDxsoModuleInfo,
const void* pShaderBytecode,
const DxsoAnalysisInfo& AnalysisInfo,
DxsoModule* pModule) {
const uint32_t bytecodeLength = AnalysisInfo.bytecodeByteLength;
m_bytecode.resize(bytecodeLength);
std::memcpy(m_bytecode.data(), pShaderBytecode, bytecodeLength);
DxvkShaderKey shaderKey = { ShaderStage, *pHash };
const std::string name = shaderKey.toString();
Logger::debug(str::format("Compiling shader ", name));
// If requested by the user, dump both the raw DXBC
// shader and the compiled SPIR-V module to a file.
const std::string dumpPath = env::getEnvVar("DXVK_SHADER_DUMP_PATH");
if (dumpPath.size() != 0) {
DxsoReader reader(
reinterpret_cast<const char*>(pShaderBytecode));
reader.store(std::ofstream(str::format(dumpPath, "/", name, ".dxso"),
std::ios_base::binary | std::ios_base::trunc), bytecodeLength);
char comment[2048];
Com<ID3DBlob> blob;
HRESULT hr = DisassembleShader(
pShaderBytecode,
TRUE,
comment,
&blob);
if (SUCCEEDED(hr)) {
std::ofstream disassembledOut(str::format(dumpPath, "/", name, ".dxso.dis"), std::ios_base::binary | std::ios_base::trunc);
disassembledOut.write(
reinterpret_cast<const char*>(blob->GetBufferPointer()),
blob->GetBufferSize());
}
}
// Decide whether we need to create a pass-through
// geometry shader for vertex shader stream output
const D3D9ConstantLayout& constantLayout = ShaderStage == VK_SHADER_STAGE_VERTEX_BIT
? pDevice->GetVertexConstantLayout()
: pDevice->GetPixelConstantLayout();
m_shaders = pModule->compile(*pDxsoModuleInfo, name, AnalysisInfo, constantLayout);
m_isgn = pModule->isgn();
m_usedSamplers = pModule->usedSamplers();
m_usedRTs = pModule->usedRTs();
m_info = pModule->info();
m_meta = pModule->meta();
m_constants = pModule->constants();
m_shaders[0]->setShaderKey(shaderKey);
if (m_shaders[1] != nullptr) {
// Lets lie about the shader key type for the state cache.
m_shaders[1]->setShaderKey({ VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, *pHash });
}
if (dumpPath.size() != 0) {
std::ofstream dumpStream(
str::format(dumpPath, "/", name, ".spv"),
std::ios_base::binary | std::ios_base::trunc);
m_shaders[0]->dump(dumpStream);
}
pDevice->GetDXVKDevice()->registerShader(m_shaders[0]);
if (m_shaders[1] != nullptr)
pDevice->GetDXVKDevice()->registerShader(m_shaders[1]);
}
D3D9CommonShader D3D9ShaderModuleSet::GetShaderModule(
D3D9DeviceEx* pDevice,
VkShaderStageFlagBits ShaderStage,
const DxsoModuleInfo* pDxbcModuleInfo,
const void* pShaderBytecode) {
DxsoReader reader(
reinterpret_cast<const char*>(pShaderBytecode));
DxsoModule module(reader);
if (module.info().majorVersion() > pDxbcModuleInfo->options.shaderModel)
throw DxvkError("GetShaderModule: Out of range of supported shader model");
if (module.info().shaderStage() != ShaderStage)
throw DxvkError("GetShaderModule: Bytecode does not match shader stage");
DxsoAnalysisInfo info = module.analyze();
Sha1Hash hash = Sha1Hash::compute(
pShaderBytecode, info.bytecodeByteLength);
DxvkShaderKey lookupKey = DxvkShaderKey(ShaderStage, hash);
// Use the shader's unique key for the lookup
{ std::unique_lock<std::mutex> lock(m_mutex);
auto entry = m_modules.find(lookupKey);
if (entry != m_modules.end())
return entry->second;
}
// This shader has not been compiled yet, so we have to create a
// new module. This takes a while, so we won't lock the structure.
D3D9CommonShader commonShader(
pDevice, ShaderStage, &hash,
pDxbcModuleInfo, pShaderBytecode,
info, &module);
// Insert the new module into the lookup table. If another thread
// has compiled the same shader in the meantime, we should return
// that object instead and discard the newly created module.
{ std::unique_lock<std::mutex> lock(m_mutex);
auto status = m_modules.insert({ lookupKey, commonShader });
if (!status.second)
return status.first->second;
}
return commonShader;
}
}

195
src/d3d9/d3d9_shader.h Normal file
View file

@ -0,0 +1,195 @@
#pragma once
#include "d3d9_resource.h"
#include "../dxso/dxso_module.h"
#include "d3d9_shader_permutations.h"
#include "d3d9_util.h"
#include <array>
namespace dxvk {
/**
* \brief Common shader object
*
* Stores the compiled SPIR-V shader and the SHA-1
* hash of the original DXBC shader, which can be
* used to identify the shader.
*/
class D3D9CommonShader {
public:
D3D9CommonShader();
D3D9CommonShader(
D3D9DeviceEx* pDevice,
VkShaderStageFlagBits ShaderStage,
const Sha1Hash* pHash,
const DxsoModuleInfo* pDxbcModuleInfo,
const void* pShaderBytecode,
const DxsoAnalysisInfo& AnalysisInfo,
DxsoModule* pModule);
Rc<DxvkShader> GetShader(D3D9ShaderPermutation Permutation) const {
return m_shaders[Permutation];
}
std::string GetName() const {
return m_shaders[D3D9ShaderPermutations::None]->debugName();
}
const std::vector<uint8_t>& GetBytecode() const {
return m_bytecode;
}
const DxsoIsgn& GetIsgn() const {
return m_isgn;
}
const DxsoShaderMetaInfo& GetMeta() const { return m_meta; }
const DxsoDefinedConstants& GetConstants() const { return m_constants; }
D3D9ShaderMasks GetShaderMask() const { return D3D9ShaderMasks{ m_usedSamplers, m_usedRTs }; }
const DxsoProgramInfo& GetInfo() const { return m_info; }
private:
DxsoIsgn m_isgn;
uint32_t m_usedSamplers;
uint32_t m_usedRTs;
DxsoProgramInfo m_info;
DxsoShaderMetaInfo m_meta;
DxsoDefinedConstants m_constants;
DxsoPermutations m_shaders;
std::vector<uint8_t> m_bytecode;
};
/**
* \brief Common shader interface
*
* Implements methods for all D3D11*Shader
* interfaces and stores the actual shader
* module object.
*/
template <typename Base>
class D3D9Shader : public D3D9DeviceChild<Base> {
public:
D3D9Shader(
D3D9DeviceEx* pDevice,
const D3D9CommonShader& CommonShader)
: D3D9DeviceChild<Base>( pDevice )
, m_shader ( CommonShader ) { }
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(Base)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9Shader::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE GetFunction(void* pOut, UINT* pSizeOfData) {
if (pSizeOfData == nullptr)
return D3DERR_INVALIDCALL;
const auto& bytecode = m_shader.GetBytecode();
if (pOut == nullptr) {
*pSizeOfData = bytecode.size();
return D3D_OK;
}
size_t copyAmount = std::min(size_t(*pSizeOfData), bytecode.size());
std::memcpy(pOut, bytecode.data(), copyAmount);
return D3D_OK;
}
const D3D9CommonShader* GetCommonShader() const {
return &m_shader;
}
private:
D3D9CommonShader m_shader;
};
// Needs their own classes and not usings for forward decl.
class D3D9VertexShader final : public D3D9Shader<IDirect3DVertexShader9> {
public:
D3D9VertexShader(
D3D9DeviceEx* pDevice,
const D3D9CommonShader& CommonShader)
: D3D9Shader<IDirect3DVertexShader9>( pDevice, CommonShader ) { }
};
class D3D9PixelShader final : public D3D9Shader<IDirect3DPixelShader9> {
public:
D3D9PixelShader(
D3D9DeviceEx* pDevice,
const D3D9CommonShader& CommonShader)
: D3D9Shader<IDirect3DPixelShader9>( pDevice, CommonShader ) { }
};
/**
* \brief Shader module set
*
* Some applications may compile the same shader multiple
* times, so we should cache the resulting shader modules
* and reuse them rather than creating new ones. This
* class is thread-safe.
*/
class D3D9ShaderModuleSet : public RcObject {
public:
D3D9CommonShader GetShaderModule(
D3D9DeviceEx* pDevice,
VkShaderStageFlagBits ShaderStage,
const DxsoModuleInfo* pDxbcModuleInfo,
const void* pShaderBytecode);
private:
std::mutex m_mutex;
std::unordered_map<
DxvkShaderKey,
D3D9CommonShader,
DxvkHash, DxvkEq> m_modules;
};
template<typename T>
const D3D9CommonShader* GetCommonShader(const T& pShader) {
return pShader != nullptr ? pShader->GetCommonShader() : nullptr;
}
}

View file

@ -0,0 +1,20 @@
#pragma once
#include "d3d9_include.h"
namespace dxvk {
class DxvkShader;
namespace D3D9ShaderPermutations {
enum D3D9ShaderPermutation {
None,
FlatShade,
Count
};
}
using D3D9ShaderPermutation = D3D9ShaderPermutations::D3D9ShaderPermutation;
using DxsoPermutations = std::array<Rc<DxvkShader>, D3D9ShaderPermutations::Count>;
}

View file

@ -0,0 +1,68 @@
#pragma once
#include "d3d9_include.h"
namespace dxvk {
class IDirect3DShaderValidator9 : public IUnknown {
public:
virtual HRESULT STDMETHODCALLTYPE Begin(
void* pCallback,
void* pUserParam,
DWORD Unknown) = 0;
virtual HRESULT STDMETHODCALLTYPE Instruction(
const char* pUnknown1,
UINT Unknown2,
const DWORD* pInstruction,
DWORD InstructionLength) = 0;
virtual HRESULT STDMETHODCALLTYPE End() = 0;
};
class D3D9ShaderValidator final : public ComObjectClamp<IDirect3DShaderValidator9> {
public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = ref(this);
return S_OK;
}
HRESULT STDMETHODCALLTYPE Begin(
void* pCallback,
void* pUserParam,
DWORD Unknown) {
Logger::debug("D3D9ShaderValidator::Begin: Stub");
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE Instruction(
const char* pUnknown1,
UINT Unknown2,
const DWORD* pInstruction,
DWORD InstructionLength) {
Logger::debug("D3D9ShaderValidator::Instruction: Stub");
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE End() {
Logger::debug("D3D9ShaderValidator::End: Stub");
return D3D_OK;
}
};
}

View file

@ -0,0 +1,19 @@
#pragma once
#include <cstdint>
namespace dxvk {
enum D3D9SpecConstantId : uint32_t {
AlphaTestEnable = 0,
AlphaCompareOp = 1,
SamplerType = 2,
FogEnabled = 3,
VertexFogMode = 4,
PixelFogMode = 5,
PointMode = 6,
ProjectionType = 7,
};
}

26
src/d3d9/d3d9_state.cpp Normal file
View file

@ -0,0 +1,26 @@
#include "d3d9_state.h"
#include "d3d9_texture.h"
namespace dxvk {
D3D9CapturableState::D3D9CapturableState() {
for (uint32_t i = 0; i < textures.size(); i++)
textures[i] = nullptr;
for (uint32_t i = 0; i < clipPlanes.size(); i++)
clipPlanes[i] = D3D9ClipPlane();
for (uint32_t i = 0; i < streamFreq.size(); i++)
streamFreq[i] = 1;
for (uint32_t i = 0; i < enabledLightIndices.size(); i++)
enabledLightIndices[i] = UINT32_MAX;
}
D3D9CapturableState::~D3D9CapturableState() {
for (uint32_t i = 0; i < textures.size(); i++)
TextureChangePrivate(textures[i], nullptr);
}
}

352
src/d3d9/d3d9_state.h Normal file
View file

@ -0,0 +1,352 @@
#pragma once
#include "d3d9_caps.h"
#include "d3d9_constant_set.h"
#include "../dxso/dxso_common.h"
#include "../util/util_matrix.h"
#include "d3d9_surface.h"
#include "d3d9_shader.h"
#include "d3d9_vertex_declaration.h"
#include "d3d9_buffer.h"
#include <array>
#include <bitset>
#include <optional>
namespace dxvk {
static constexpr uint32_t RenderStateCount = 256;
static constexpr uint32_t SamplerStateCount = D3DSAMP_DMAPOFFSET + 1;
static constexpr uint32_t SamplerCount = 21;
static constexpr uint32_t TextureStageStateCount = D3DTSS_CONSTANT + 1;
namespace hacks::PointSize {
static constexpr DWORD AlphaToCoverageDisabled = MAKEFOURCC('A', '2', 'M', '0');
static constexpr DWORD AlphaToCoverageEnabled = MAKEFOURCC('A', '2', 'M', '1');
}
struct D3D9ClipPlane {
float coeff[4];
};
struct D3D9RenderStateInfo {
std::array<float, 3> fogColor = { };
float fogScale = 0.0f;
float fogEnd = 1.0f;
float fogDensity = 1.0f;
float alphaRef = 0.0f;
float pointSize = 1.0f;
float pointSizeMin = 1.0f;
float pointSizeMax = 64.0f;
float pointScaleA = 1.0f;
float pointScaleB = 0.0f;
float pointScaleC = 0.0f;
};
enum class D3D9RenderStateItem {
FogColor = 0,
FogScale = 1,
FogEnd,
FogDensity,
AlphaRef,
PointSize,
PointSizeMin,
PointSizeMax,
PointScaleA,
PointScaleB,
PointScaleC,
Count
};
// This is needed in fixed function for POSITION_T support.
// These are constants we need to * and add to move
// Window Coords -> Real Coords w/ respect to the viewport.
struct D3D9ViewportInfo {
Vector4 inverseOffset;
Vector4 inverseExtent;
};
struct D3D9Light {
D3D9Light(const D3DLIGHT9& light, Matrix4 viewMtx) {
Diffuse = Vector4(light.Diffuse.r, light.Diffuse.g, light.Diffuse.b, light.Diffuse.a);
Specular = Vector4(light.Specular.r, light.Specular.g, light.Specular.b, light.Specular.a);
Ambient = Vector4(light.Ambient.r, light.Ambient.g, light.Ambient.b, light.Ambient.a);
Position = viewMtx * Vector4(light.Position.x, light.Position.y, light.Position.z, 1.0f);
Direction = Vector4(light.Direction.x, light.Direction.y, light.Direction.z, 0.0f);
Direction = normalize(viewMtx * Direction);
Type = light.Type;
Range = light.Range;
Falloff = light.Falloff;
Attenuation0 = light.Attenuation0;
Attenuation1 = light.Attenuation1;
Attenuation2 = light.Attenuation2;
Theta = cosf(light.Theta / 2.0f);
Phi = cosf(light.Phi / 2.0f);
}
Vector4 Diffuse;
Vector4 Specular;
Vector4 Ambient;
Vector4 Position;
Vector4 Direction;
D3DLIGHTTYPE Type;
float Range;
float Falloff;
float Attenuation0;
float Attenuation1;
float Attenuation2;
float Theta;
float Phi;
};
struct D3D9FixedFunctionVS {
Matrix4 WorldView;
Matrix4 NormalMatrix;
Matrix4 Projection;
std::array<Matrix4, 8> TexcoordMatrices;
D3D9ViewportInfo ViewportInfo;
Vector4 GlobalAmbient;
std::array<D3D9Light, caps::MaxEnabledLights> Lights;
D3DMATERIAL9 Material;
float TweenFactor;
};
struct D3D9FixedFunctionVertexBlendDataHW {
Matrix4 WorldView[8];
};
struct D3D9FixedFunctionVertexBlendDataSW {
Matrix4 WorldView[256];
};
struct D3D9FixedFunctionPS {
Vector4 textureFactor;
};
enum D3D9SharedPSStages {
D3D9SharedPSStages_Constant,
D3D9SharedPSStages_BumpEnvMat0,
D3D9SharedPSStages_BumpEnvMat1,
D3D9SharedPSStages_BumpEnvLScale,
D3D9SharedPSStages_BumpEnvLOffset,
D3D9SharedPSStages_Count,
};
struct D3D9SharedPS {
struct Stage {
float Constant[4];
float BumpEnvMat[2][2];
float BumpEnvLScale;
float BumpEnvLOffset;
float Padding[2];
} Stages[8];
};
struct D3D9VBO {
Com<D3D9VertexBuffer, false> vertexBuffer;
UINT offset = 0;
UINT stride = 0;
};
constexpr D3DLIGHT9 DefaultLight = {
D3DLIGHT_DIRECTIONAL, // Type
{1.0f, 1.0f, 1.0f, 1.0f}, // Diffuse
{0.0f, 0.0f, 0.0f, 0.0f}, // Specular
{0.0f, 0.0f, 0.0f, 0.0f}, // Ambient
{0.0f, 0.0f, 0.0f}, // Position
{0.0f, 0.0f, 0.0f}, // Direction
0.0f, // Range
0.0f, // Falloff
0.0f, 0.0f, 0.0f, // Attenuations [constant, linear, quadratic]
0.0f, // Theta
0.0f // Phi
};
struct D3D9CapturableState {
D3D9CapturableState();
~D3D9CapturableState();
Com<D3D9VertexDecl, false> vertexDecl;
Com<D3D9IndexBuffer, false> indices;
std::array<DWORD, RenderStateCount> renderStates = { 0 };
std::array<
std::array<DWORD, SamplerStateCount>,
SamplerCount> samplerStates;
std::array<D3D9VBO, caps::MaxStreams> vertexBuffers;
std::array<
IDirect3DBaseTexture9*,
SamplerCount> textures;
Com<D3D9VertexShader, false> vertexShader;
Com<D3D9PixelShader, false> pixelShader;
D3DVIEWPORT9 viewport;
RECT scissorRect;
std::array<
D3D9ClipPlane,
caps::MaxClipPlanes> clipPlanes;
std::array<
std::array<DWORD, TextureStageStateCount>,
caps::TextureStageCount> textureStages;
D3D9ShaderConstantsVSSoftware vsConsts;
D3D9ShaderConstantsPS psConsts;
std::array<UINT, caps::MaxStreams> streamFreq;
std::array<Matrix4, caps::MaxTransforms> transforms;
D3DMATERIAL9 material = D3DMATERIAL9();
std::vector<std::optional<D3DLIGHT9>> lights;
std::array<DWORD, caps::MaxEnabledLights> enabledLightIndices;
bool IsLightEnabled(DWORD Index) {
const auto& indices = enabledLightIndices;
return std::find(indices.begin(), indices.end(), Index) != indices.end();
}
};
template <
DxsoProgramType ProgramType,
D3D9ConstantType ConstantType,
typename T>
HRESULT UpdateStateConstants(
D3D9CapturableState* pState,
UINT StartRegister,
const T* pConstantData,
UINT Count,
bool FloatEmu) {
auto UpdateHelper = [&] (auto& set) {
if constexpr (ConstantType == D3D9ConstantType::Float) {
auto begin = reinterpret_cast<const Vector4*>(pConstantData);
auto end = begin + Count;
if (!FloatEmu)
std::copy(begin, end, &set.fConsts[StartRegister]);
else
std::transform(begin, end, &set.fConsts[StartRegister], replaceNaN);
}
else if constexpr (ConstantType == D3D9ConstantType::Int) {
auto begin = reinterpret_cast<const Vector4i*>(pConstantData);
auto end = begin + Count;
std::copy(begin, end, &set.iConsts[StartRegister]);
}
else {
for (uint32_t i = 0; i < Count; i++) {
const uint32_t constantIdx = StartRegister + i;
const uint32_t arrayIdx = constantIdx / 32;
const uint32_t bitIdx = constantIdx % 32;
const uint32_t bit = 1u << bitIdx;
set.bConsts[arrayIdx] &= ~bit;
if (pConstantData[i])
set.bConsts[arrayIdx] |= bit;
}
}
return D3D_OK;
};
return ProgramType == DxsoProgramTypes::VertexShader
? UpdateHelper(pState->vsConsts)
: UpdateHelper(pState->psConsts);
}
enum class D3D9CapturedStateFlag : uint32_t {
VertexDecl,
Indices,
RenderStates,
SamplerStates,
VertexBuffers,
Textures,
VertexShader,
PixelShader,
Viewport,
ScissorRect,
ClipPlanes,
VsConstants,
PsConstants,
StreamFreq,
Transforms,
TextureStages,
Material
};
using D3D9CapturedStateFlags = Flags<D3D9CapturedStateFlag>;
struct D3D9StateCaptures {
D3D9CapturedStateFlags flags;
std::bitset<RenderStateCount> renderStates;
std::bitset<SamplerCount> samplers;
std::array<
std::bitset<SamplerStateCount>,
SamplerCount> samplerStates;
std::bitset<caps::MaxStreams> vertexBuffers;
std::bitset<SamplerCount> textures;
std::bitset<caps::MaxClipPlanes> clipPlanes;
std::bitset<caps::MaxStreams> streamFreq;
std::bitset<caps::MaxTransforms> transforms;
std::bitset<caps::TextureStageCount> textureStages;
std::array<
std::bitset<D3DTSS_CONSTANT>,
caps::TextureStageCount> textureStageStates;
struct {
std::bitset<caps::MaxFloatConstantsSoftware> fConsts;
std::bitset<caps::MaxOtherConstantsSoftware> iConsts;
std::bitset<caps::MaxOtherConstantsSoftware> bConsts;
} vsConsts;
struct {
std::bitset<caps::MaxFloatConstantsPS> fConsts;
std::bitset<caps::MaxOtherConstants> iConsts;
std::bitset<caps::MaxOtherConstants> bConsts;
} psConsts;
};
struct Direct3DState9 : public D3D9CapturableState {
std::array<Com<D3D9Surface, false>, caps::MaxSimultaneousRenderTargets> renderTargets;
Com<D3D9Surface, false> depthStencil;
};
struct D3D9InputAssemblyState {
D3DPRIMITIVETYPE primitiveType = D3DPRIMITIVETYPE(0);
uint32_t streamsInstanced = 0;
uint32_t streamsUsed = 0;
};
}

View file

@ -0,0 +1,519 @@
#include "d3d9_stateblock.h"
#include "d3d9_device.h"
#include "d3d9_vertex_declaration.h"
#include "d3d9_buffer.h"
#include "d3d9_shader.h"
#include "d3d9_texture.h"
#include "d3d9_util.h"
namespace dxvk {
D3D9StateBlock::D3D9StateBlock(D3D9DeviceEx* pDevice, D3D9StateBlockType Type)
: D3D9StateBlockBase(pDevice)
, m_deviceState (pDevice->GetRawState()) {
CaptureType(Type);
}
HRESULT STDMETHODCALLTYPE D3D9StateBlock::QueryInterface(
REFIID riid,
void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3DStateBlock9)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9StateBlock::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE D3D9StateBlock::Capture() {
ApplyOrCapture<D3D9StateFunction::Capture>();
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE D3D9StateBlock::Apply() {
m_applying = true;
ApplyOrCapture<D3D9StateFunction::Apply>();
m_applying = false;
return D3D_OK;
}
HRESULT D3D9StateBlock::SetVertexDeclaration(D3D9VertexDecl* pDecl) {
m_state.vertexDecl = pDecl;
m_captures.flags.set(D3D9CapturedStateFlag::VertexDecl);
return D3D_OK;
}
HRESULT D3D9StateBlock::SetIndices(D3D9IndexBuffer* pIndexData) {
m_state.indices = pIndexData;
m_captures.flags.set(D3D9CapturedStateFlag::Indices);
return D3D_OK;
}
HRESULT D3D9StateBlock::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) {
m_state.renderStates[State] = Value;
m_captures.flags.set(D3D9CapturedStateFlag::RenderStates);
m_captures.renderStates[State] = true;
return D3D_OK;
}
HRESULT D3D9StateBlock::SetStateSamplerState(
DWORD StateSampler,
D3DSAMPLERSTATETYPE Type,
DWORD Value) {
m_state.samplerStates[StateSampler][Type] = Value;
m_captures.flags.set(D3D9CapturedStateFlag::SamplerStates);
m_captures.samplers[StateSampler] = true;
m_captures.samplerStates[StateSampler][Type] = true;
return D3D_OK;
}
HRESULT D3D9StateBlock::SetStreamSource(
UINT StreamNumber,
D3D9VertexBuffer* pStreamData,
UINT OffsetInBytes,
UINT Stride) {
m_state.vertexBuffers[StreamNumber].vertexBuffer = pStreamData;
m_state.vertexBuffers[StreamNumber].offset = OffsetInBytes;
m_state.vertexBuffers[StreamNumber].stride = Stride;
m_captures.flags.set(D3D9CapturedStateFlag::VertexBuffers);
m_captures.vertexBuffers[StreamNumber] = true;
return D3D_OK;
}
HRESULT D3D9StateBlock::SetStreamSourceFreq(UINT StreamNumber, UINT Setting) {
m_state.streamFreq[StreamNumber] = Setting;
m_captures.flags.set(D3D9CapturedStateFlag::StreamFreq);
m_captures.streamFreq[StreamNumber] = true;
return D3D_OK;
}
HRESULT D3D9StateBlock::SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture) {
TextureChangePrivate(m_state.textures[StateSampler], pTexture);
m_captures.flags.set(D3D9CapturedStateFlag::Textures);
m_captures.textures[StateSampler] = true;
return D3D_OK;
}
HRESULT D3D9StateBlock::SetVertexShader(D3D9VertexShader* pShader) {
m_state.vertexShader = pShader;
m_captures.flags.set(D3D9CapturedStateFlag::VertexShader);
return D3D_OK;
}
HRESULT D3D9StateBlock::SetPixelShader(D3D9PixelShader* pShader) {
m_state.pixelShader = pShader;
m_captures.flags.set(D3D9CapturedStateFlag::PixelShader);
return D3D_OK;
}
HRESULT D3D9StateBlock::SetMaterial(const D3DMATERIAL9* pMaterial) {
m_state.material = *pMaterial;
m_captures.flags.set(D3D9CapturedStateFlag::Material);
return D3D_OK;
}
HRESULT D3D9StateBlock::SetStateTransform(uint32_t idx, const D3DMATRIX* pMatrix) {
m_state.transforms[idx] = ConvertMatrix(pMatrix);
m_captures.flags.set(D3D9CapturedStateFlag::Transforms);
m_captures.transforms.set(idx);
return D3D_OK;
}
HRESULT D3D9StateBlock::SetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD Value) {
m_state.textureStages[Stage][Type] = Value;
m_captures.flags.set(D3D9CapturedStateFlag::TextureStages);
m_captures.textureStages[Stage] = true;
m_captures.textureStageStates[Stage][Type] = true;
return D3D_OK;
}
HRESULT D3D9StateBlock::MultiplyStateTransform(uint32_t idx, const D3DMATRIX* pMatrix) {
m_state.transforms[idx] = ConvertMatrix(pMatrix) * m_state.transforms[idx];
m_captures.flags.set(D3D9CapturedStateFlag::Transforms);
m_captures.transforms.set(idx);
return D3D_OK;
}
HRESULT D3D9StateBlock::SetViewport(const D3DVIEWPORT9* pViewport) {
m_state.viewport = *pViewport;
m_captures.flags.set(D3D9CapturedStateFlag::Viewport);
return D3D_OK;
}
HRESULT D3D9StateBlock::SetScissorRect(const RECT* pRect) {
m_state.scissorRect = *pRect;
m_captures.flags.set(D3D9CapturedStateFlag::ScissorRect);
return D3D_OK;
}
HRESULT D3D9StateBlock::SetClipPlane(DWORD Index, const float* pPlane) {
for (uint32_t i = 0; i < 4; i++)
m_state.clipPlanes[Index].coeff[i] = pPlane[i];
m_captures.flags.set(D3D9CapturedStateFlag::ClipPlanes);
m_captures.clipPlanes[Index] = true;
return D3D_OK;
}
HRESULT D3D9StateBlock::SetVertexShaderConstantF(
UINT StartRegister,
const float* pConstantData,
UINT Vector4fCount) {
return SetShaderConstants<
DxsoProgramTypes::VertexShader,
D3D9ConstantType::Float>(
StartRegister,
pConstantData,
Vector4fCount);
}
HRESULT D3D9StateBlock::SetVertexShaderConstantI(
UINT StartRegister,
const int* pConstantData,
UINT Vector4iCount) {
return SetShaderConstants<
DxsoProgramTypes::VertexShader,
D3D9ConstantType::Int>(
StartRegister,
pConstantData,
Vector4iCount);
}
HRESULT D3D9StateBlock::SetVertexShaderConstantB(
UINT StartRegister,
const BOOL* pConstantData,
UINT BoolCount) {
return SetShaderConstants<
DxsoProgramTypes::VertexShader,
D3D9ConstantType::Bool>(
StartRegister,
pConstantData,
BoolCount);
}
HRESULT D3D9StateBlock::SetPixelShaderConstantF(
UINT StartRegister,
const float* pConstantData,
UINT Vector4fCount) {
return SetShaderConstants<
DxsoProgramTypes::PixelShader,
D3D9ConstantType::Float>(
StartRegister,
pConstantData,
Vector4fCount);
}
HRESULT D3D9StateBlock::SetPixelShaderConstantI(
UINT StartRegister,
const int* pConstantData,
UINT Vector4iCount) {
return SetShaderConstants<
DxsoProgramTypes::PixelShader,
D3D9ConstantType::Int>(
StartRegister,
pConstantData,
Vector4iCount);
}
HRESULT D3D9StateBlock::SetPixelShaderConstantB(
UINT StartRegister,
const BOOL* pConstantData,
UINT BoolCount) {
return SetShaderConstants<
DxsoProgramTypes::PixelShader,
D3D9ConstantType::Bool>(
StartRegister,
pConstantData,
BoolCount);
}
HRESULT D3D9StateBlock::SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits) {
m_state.vsConsts.bConsts[idx] &= ~mask;
m_state.vsConsts.bConsts[idx] |= bits & mask;
return D3D_OK;
}
HRESULT D3D9StateBlock::SetPixelBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits) {
m_state.psConsts.bConsts[idx] &= ~mask;
m_state.psConsts.bConsts[idx] |= bits & mask;
return D3D_OK;
}
void D3D9StateBlock::CapturePixelRenderStates() {
m_captures.flags.set(D3D9CapturedStateFlag::RenderStates);
m_captures.renderStates[D3DRS_ZENABLE] = true;
m_captures.renderStates[D3DRS_FILLMODE] = true;
m_captures.renderStates[D3DRS_SHADEMODE] = true;
m_captures.renderStates[D3DRS_ZWRITEENABLE] = true;
m_captures.renderStates[D3DRS_ALPHATESTENABLE] = true;
m_captures.renderStates[D3DRS_LASTPIXEL] = true;
m_captures.renderStates[D3DRS_SRCBLEND] = true;
m_captures.renderStates[D3DRS_DESTBLEND] = true;
m_captures.renderStates[D3DRS_ZFUNC] = true;
m_captures.renderStates[D3DRS_ALPHAREF] = true;
m_captures.renderStates[D3DRS_ALPHAFUNC] = true;
m_captures.renderStates[D3DRS_DITHERENABLE] = true;
m_captures.renderStates[D3DRS_FOGSTART] = true;
m_captures.renderStates[D3DRS_FOGEND] = true;
m_captures.renderStates[D3DRS_FOGDENSITY] = true;
m_captures.renderStates[D3DRS_ALPHABLENDENABLE] = true;
m_captures.renderStates[D3DRS_DEPTHBIAS] = true;
m_captures.renderStates[D3DRS_STENCILENABLE] = true;
m_captures.renderStates[D3DRS_STENCILFAIL] = true;
m_captures.renderStates[D3DRS_STENCILZFAIL] = true;
m_captures.renderStates[D3DRS_STENCILPASS] = true;
m_captures.renderStates[D3DRS_STENCILFUNC] = true;
m_captures.renderStates[D3DRS_STENCILREF] = true;
m_captures.renderStates[D3DRS_STENCILMASK] = true;
m_captures.renderStates[D3DRS_STENCILWRITEMASK] = true;
m_captures.renderStates[D3DRS_TEXTUREFACTOR] = true;
m_captures.renderStates[D3DRS_WRAP0] = true;
m_captures.renderStates[D3DRS_WRAP1] = true;
m_captures.renderStates[D3DRS_WRAP2] = true;
m_captures.renderStates[D3DRS_WRAP3] = true;
m_captures.renderStates[D3DRS_WRAP4] = true;
m_captures.renderStates[D3DRS_WRAP5] = true;
m_captures.renderStates[D3DRS_WRAP6] = true;
m_captures.renderStates[D3DRS_WRAP7] = true;
m_captures.renderStates[D3DRS_WRAP8] = true;
m_captures.renderStates[D3DRS_WRAP9] = true;
m_captures.renderStates[D3DRS_WRAP10] = true;
m_captures.renderStates[D3DRS_WRAP11] = true;
m_captures.renderStates[D3DRS_WRAP12] = true;
m_captures.renderStates[D3DRS_WRAP13] = true;
m_captures.renderStates[D3DRS_WRAP14] = true;
m_captures.renderStates[D3DRS_WRAP15] = true;
m_captures.renderStates[D3DRS_COLORWRITEENABLE] = true;
m_captures.renderStates[D3DRS_BLENDOP] = true;
m_captures.renderStates[D3DRS_SCISSORTESTENABLE] = true;
m_captures.renderStates[D3DRS_SLOPESCALEDEPTHBIAS] = true;
m_captures.renderStates[D3DRS_ANTIALIASEDLINEENABLE] = true;
m_captures.renderStates[D3DRS_TWOSIDEDSTENCILMODE] = true;
m_captures.renderStates[D3DRS_CCW_STENCILFAIL] = true;
m_captures.renderStates[D3DRS_CCW_STENCILZFAIL] = true;
m_captures.renderStates[D3DRS_CCW_STENCILPASS] = true;
m_captures.renderStates[D3DRS_CCW_STENCILFUNC] = true;
m_captures.renderStates[D3DRS_COLORWRITEENABLE1] = true;
m_captures.renderStates[D3DRS_COLORWRITEENABLE2] = true;
m_captures.renderStates[D3DRS_COLORWRITEENABLE3] = true;
m_captures.renderStates[D3DRS_BLENDFACTOR] = true;
m_captures.renderStates[D3DRS_SRGBWRITEENABLE] = true;
m_captures.renderStates[D3DRS_SEPARATEALPHABLENDENABLE] = true;
m_captures.renderStates[D3DRS_SRCBLENDALPHA] = true;
m_captures.renderStates[D3DRS_DESTBLENDALPHA] = true;
m_captures.renderStates[D3DRS_BLENDOPALPHA] = true;
}
void D3D9StateBlock::CapturePixelSamplerStates() {
m_captures.flags.set(D3D9CapturedStateFlag::SamplerStates);
for (uint32_t i = 0; i < 17; i++) {
m_captures.samplers[i] = true;
m_captures.samplerStates[i][D3DSAMP_ADDRESSU] = true;
m_captures.samplerStates[i][D3DSAMP_ADDRESSV] = true;
m_captures.samplerStates[i][D3DSAMP_ADDRESSW] = true;
m_captures.samplerStates[i][D3DSAMP_BORDERCOLOR] = true;
m_captures.samplerStates[i][D3DSAMP_MAGFILTER] = true;
m_captures.samplerStates[i][D3DSAMP_MINFILTER] = true;
m_captures.samplerStates[i][D3DSAMP_MIPFILTER] = true;
m_captures.samplerStates[i][D3DSAMP_MIPMAPLODBIAS] = true;
m_captures.samplerStates[i][D3DSAMP_MAXMIPLEVEL] = true;
m_captures.samplerStates[i][D3DSAMP_MAXANISOTROPY] = true;
m_captures.samplerStates[i][D3DSAMP_SRGBTEXTURE] = true;
m_captures.samplerStates[i][D3DSAMP_ELEMENTINDEX] = true;
}
}
void D3D9StateBlock::CapturePixelShaderStates() {
m_captures.flags.set(D3D9CapturedStateFlag::PixelShader);
m_captures.flags.set(D3D9CapturedStateFlag::PsConstants);
m_captures.psConsts.fConsts.flip();
m_captures.psConsts.iConsts.flip();
m_captures.psConsts.bConsts.flip();
}
void D3D9StateBlock::CaptureVertexRenderStates() {
m_captures.flags.set(D3D9CapturedStateFlag::RenderStates);
m_captures.renderStates[D3DRS_CULLMODE] = true;
m_captures.renderStates[D3DRS_FOGENABLE] = true;
m_captures.renderStates[D3DRS_FOGCOLOR] = true;
m_captures.renderStates[D3DRS_FOGTABLEMODE] = true;
m_captures.renderStates[D3DRS_FOGSTART] = true;
m_captures.renderStates[D3DRS_FOGEND] = true;
m_captures.renderStates[D3DRS_FOGDENSITY] = true;
m_captures.renderStates[D3DRS_RANGEFOGENABLE] = true;
m_captures.renderStates[D3DRS_AMBIENT] = true;
m_captures.renderStates[D3DRS_COLORVERTEX] = true;
m_captures.renderStates[D3DRS_FOGVERTEXMODE] = true;
m_captures.renderStates[D3DRS_CLIPPING] = true;
m_captures.renderStates[D3DRS_LIGHTING] = true;
m_captures.renderStates[D3DRS_LOCALVIEWER] = true;
m_captures.renderStates[D3DRS_EMISSIVEMATERIALSOURCE] = true;
m_captures.renderStates[D3DRS_AMBIENTMATERIALSOURCE] = true;
m_captures.renderStates[D3DRS_DIFFUSEMATERIALSOURCE] = true;
m_captures.renderStates[D3DRS_SPECULARMATERIALSOURCE] = true;
m_captures.renderStates[D3DRS_VERTEXBLEND] = true;
m_captures.renderStates[D3DRS_CLIPPLANEENABLE] = true;
m_captures.renderStates[D3DRS_POINTSIZE] = true;
m_captures.renderStates[D3DRS_POINTSIZE_MIN] = true;
m_captures.renderStates[D3DRS_POINTSPRITEENABLE] = true;
m_captures.renderStates[D3DRS_POINTSCALEENABLE] = true;
m_captures.renderStates[D3DRS_POINTSCALE_A] = true;
m_captures.renderStates[D3DRS_POINTSCALE_B] = true;
m_captures.renderStates[D3DRS_POINTSCALE_C] = true;
m_captures.renderStates[D3DRS_MULTISAMPLEANTIALIAS] = true;
m_captures.renderStates[D3DRS_MULTISAMPLEMASK] = true;
m_captures.renderStates[D3DRS_PATCHEDGESTYLE] = true;
m_captures.renderStates[D3DRS_POINTSIZE_MAX] = true;
m_captures.renderStates[D3DRS_INDEXEDVERTEXBLENDENABLE] = true;
m_captures.renderStates[D3DRS_TWEENFACTOR] = true;
m_captures.renderStates[D3DRS_POSITIONDEGREE] = true;
m_captures.renderStates[D3DRS_NORMALDEGREE] = true;
m_captures.renderStates[D3DRS_MINTESSELLATIONLEVEL] = true;
m_captures.renderStates[D3DRS_MAXTESSELLATIONLEVEL] = true;
m_captures.renderStates[D3DRS_ADAPTIVETESS_X] = true;
m_captures.renderStates[D3DRS_ADAPTIVETESS_Y] = true;
m_captures.renderStates[D3DRS_ADAPTIVETESS_Z] = true;
m_captures.renderStates[D3DRS_ADAPTIVETESS_W] = true;
m_captures.renderStates[D3DRS_ENABLEADAPTIVETESSELLATION] = true;
m_captures.renderStates[D3DRS_NORMALIZENORMALS] = true;
m_captures.renderStates[D3DRS_SPECULARENABLE] = true;
m_captures.renderStates[D3DRS_SHADEMODE] = true;
}
void D3D9StateBlock::CaptureVertexSamplerStates() {
m_captures.flags.set(D3D9CapturedStateFlag::SamplerStates);
for (uint32_t i = 17; i < SamplerCount; i++) {
m_captures.samplers[i] = true;
m_captures.samplerStates[i][D3DSAMP_DMAPOFFSET] = true;
}
}
void D3D9StateBlock::CaptureVertexShaderStates() {
m_captures.flags.set(D3D9CapturedStateFlag::VertexShader);
m_captures.flags.set(D3D9CapturedStateFlag::VsConstants);
m_captures.vsConsts.fConsts.flip();
m_captures.vsConsts.iConsts.flip();
m_captures.vsConsts.bConsts.flip();
}
void D3D9StateBlock::CaptureType(D3D9StateBlockType Type) {
if (Type == D3D9StateBlockType::PixelState || Type == D3D9StateBlockType::All) {
CapturePixelRenderStates();
CapturePixelSamplerStates();
CapturePixelShaderStates();
m_captures.flags.set(D3D9CapturedStateFlag::TextureStages);
m_captures.textureStages.flip();
for (auto& stage : m_captures.textureStageStates)
stage.flip();
}
if (Type == D3D9StateBlockType::VertexState || Type == D3D9StateBlockType::All) {
CaptureVertexRenderStates();
CaptureVertexSamplerStates();
CaptureVertexShaderStates();
m_captures.flags.set(D3D9CapturedStateFlag::VertexDecl);
m_captures.flags.set(D3D9CapturedStateFlag::StreamFreq);
for (uint32_t i = 0; i < caps::MaxStreams; i++)
m_captures.streamFreq[i] = true;
}
if (Type == D3D9StateBlockType::All) {
m_captures.flags.set(D3D9CapturedStateFlag::Textures);
m_captures.textures.flip();
m_captures.flags.set(D3D9CapturedStateFlag::VertexBuffers);
m_captures.vertexBuffers.flip();
m_captures.flags.set(D3D9CapturedStateFlag::Indices);
m_captures.flags.set(D3D9CapturedStateFlag::Viewport);
m_captures.flags.set(D3D9CapturedStateFlag::ScissorRect);
m_captures.flags.set(D3D9CapturedStateFlag::ClipPlanes);
m_captures.clipPlanes.flip();
m_captures.flags.set(D3D9CapturedStateFlag::Transforms);
m_captures.transforms.flip();
m_captures.flags.set(D3D9CapturedStateFlag::Material);
}
if (Type != D3D9StateBlockType::None)
this->Capture();
}
}

333
src/d3d9/d3d9_stateblock.h Normal file
View file

@ -0,0 +1,333 @@
#pragma once
#include "d3d9_device_child.h"
#include "d3d9_device.h"
#include "d3d9_state.h"
namespace dxvk {
enum class D3D9StateBlockType :uint32_t {
None,
VertexState,
PixelState,
All
};
inline D3D9StateBlockType ConvertStateBlockType(D3DSTATEBLOCKTYPE type) {
switch (type) {
case D3DSBT_PIXELSTATE: return D3D9StateBlockType::PixelState;
case D3DSBT_VERTEXSTATE: return D3D9StateBlockType::VertexState;
default:
case D3DSBT_ALL: return D3D9StateBlockType::All;
}
}
using D3D9StateBlockBase = D3D9DeviceChild<IDirect3DStateBlock9>;
class D3D9StateBlock : public D3D9StateBlockBase {
public:
D3D9StateBlock(D3D9DeviceEx* pDevice, D3D9StateBlockType Type);
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid,
void** ppvObject) final;
HRESULT STDMETHODCALLTYPE Capture() final;
HRESULT STDMETHODCALLTYPE Apply() final;
HRESULT SetVertexDeclaration(D3D9VertexDecl* pDecl);
HRESULT SetIndices(D3D9IndexBuffer* pIndexData);
HRESULT SetRenderState(D3DRENDERSTATETYPE State, DWORD Value);
HRESULT SetStateSamplerState(
DWORD StateSampler,
D3DSAMPLERSTATETYPE Type,
DWORD Value);
HRESULT SetStreamSource(
UINT StreamNumber,
D3D9VertexBuffer* pStreamData,
UINT OffsetInBytes,
UINT Stride);
HRESULT SetStreamSourceFreq(UINT StreamNumber, UINT Setting);
HRESULT SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture);
HRESULT SetVertexShader(D3D9VertexShader* pShader);
HRESULT SetPixelShader(D3D9PixelShader* pShader);
HRESULT SetMaterial(const D3DMATERIAL9* pMaterial);
HRESULT SetStateTransform(uint32_t idx, const D3DMATRIX* pMatrix);
HRESULT SetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD Value);
HRESULT MultiplyStateTransform(uint32_t idx, const D3DMATRIX* pMatrix);
HRESULT SetViewport(const D3DVIEWPORT9* pViewport);
HRESULT SetScissorRect(const RECT* pRect);
HRESULT SetClipPlane(DWORD Index, const float* pPlane);
HRESULT SetVertexShaderConstantF(
UINT StartRegister,
const float* pConstantData,
UINT Vector4fCount);
HRESULT SetVertexShaderConstantI(
UINT StartRegister,
const int* pConstantData,
UINT Vector4iCount);
HRESULT SetVertexShaderConstantB(
UINT StartRegister,
const BOOL* pConstantData,
UINT BoolCount);
HRESULT SetPixelShaderConstantF(
UINT StartRegister,
const float* pConstantData,
UINT Vector4fCount);
HRESULT SetPixelShaderConstantI(
UINT StartRegister,
const int* pConstantData,
UINT Vector4iCount);
HRESULT SetPixelShaderConstantB(
UINT StartRegister,
const BOOL* pConstantData,
UINT BoolCount);
enum class D3D9StateFunction {
Apply,
Capture
};
template <typename Dst, typename Src>
void ApplyOrCapture(Dst* dst, const Src* src) {
if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl))
dst->SetVertexDeclaration(src->vertexDecl.ptr());
if (m_captures.flags.test(D3D9CapturedStateFlag::StreamFreq)) {
for (uint32_t i = 0; i < caps::MaxStreams; i++) {
if (m_captures.streamFreq[i])
dst->SetStreamSourceFreq(i, src->streamFreq[i]);
}
}
if (m_captures.flags.test(D3D9CapturedStateFlag::Indices))
dst->SetIndices(src->indices.ptr());
if (m_captures.flags.test(D3D9CapturedStateFlag::RenderStates)) {
for (uint32_t i = 0; i < m_captures.renderStates.size(); i++) {
if (m_captures.renderStates[i])
dst->SetRenderState(D3DRENDERSTATETYPE(i), src->renderStates[i]);
}
}
if (m_captures.flags.test(D3D9CapturedStateFlag::SamplerStates)) {
for (uint32_t i = 0; i < m_captures.samplerStates.size(); i++) {
if (m_captures.samplers[i]) {
for (uint32_t j = 0; j < m_captures.samplerStates[i].size(); j++) {
if (m_captures.samplerStates[i][j])
dst->SetStateSamplerState(i, D3DSAMPLERSTATETYPE(j), src->samplerStates[i][j]);
}
}
}
}
if (m_captures.flags.test(D3D9CapturedStateFlag::VertexBuffers)) {
for (uint32_t i = 0; i < m_captures.vertexBuffers.size(); i++) {
if (m_captures.vertexBuffers[i]) {
const auto& vbo = src->vertexBuffers[i];
dst->SetStreamSource(
i,
vbo.vertexBuffer.ptr(),
vbo.offset,
vbo.stride);
}
}
}
if (m_captures.flags.test(D3D9CapturedStateFlag::Material))
dst->SetMaterial(&src->material);
if (m_captures.flags.test(D3D9CapturedStateFlag::Textures)) {
for (uint32_t i = 0; i < m_captures.textures.size(); i++) {
if (m_captures.textures[i])
dst->SetStateTexture(i, src->textures[i]);
}
}
if (m_captures.flags.test(D3D9CapturedStateFlag::VertexShader))
dst->SetVertexShader(src->vertexShader.ptr());
if (m_captures.flags.test(D3D9CapturedStateFlag::PixelShader))
dst->SetPixelShader(src->pixelShader.ptr());
if (m_captures.flags.test(D3D9CapturedStateFlag::Transforms)) {
for (uint32_t i = 0; i < m_captures.transforms.size(); i++) {
if (m_captures.transforms[i])
dst->SetStateTransform(i, reinterpret_cast<const D3DMATRIX*>(&src->transforms[i]));
}
}
if (m_captures.flags.test(D3D9CapturedStateFlag::TextureStages)) {
for (uint32_t i = 0; i < m_captures.textureStages.size(); i++) {
if (m_captures.textureStages[i]) {
for (uint32_t j = 0; j < m_captures.textureStageStates[i].size(); j++) {
if (m_captures.textureStageStates[i][j])
dst->SetTextureStageState(i, (D3DTEXTURESTAGESTATETYPE)j, src->textureStages[i][j]);
}
}
}
}
if (m_captures.flags.test(D3D9CapturedStateFlag::Viewport))
dst->SetViewport(&src->viewport);
if (m_captures.flags.test(D3D9CapturedStateFlag::ScissorRect))
dst->SetScissorRect(&src->scissorRect);
if (m_captures.flags.test(D3D9CapturedStateFlag::ClipPlanes)) {
for (uint32_t i = 0; i < m_captures.clipPlanes.size(); i++) {
if (m_captures.clipPlanes[i])
dst->SetClipPlane(i, src->clipPlanes[i].coeff);
}
}
if (m_captures.flags.test(D3D9CapturedStateFlag::VsConstants)) {
for (uint32_t i = 0; i < m_captures.vsConsts.fConsts.size(); i++) {
if (m_captures.vsConsts.fConsts[i])
dst->SetVertexShaderConstantF(i, (float*)&src->vsConsts.fConsts[i], 1);
}
for (uint32_t i = 0; i < m_captures.vsConsts.iConsts.size(); i++) {
if (m_captures.vsConsts.iConsts[i])
dst->SetVertexShaderConstantI(i, (int*)&src->vsConsts.iConsts[i], 1);
}
const uint32_t bitfieldCount = m_parent->GetVertexConstantLayout().bitmaskCount;
for (uint32_t i = 0; i < bitfieldCount; i++) {
uint32_t boolMask = 0;
for (uint32_t j = 0; j < 32; j++) {
if (m_captures.vsConsts.bConsts[i * 32 + j])
boolMask |= 1u << j;
}
dst->SetVertexBoolBitfield(i, boolMask, src->vsConsts.bConsts[i]);
}
}
if (m_captures.flags.test(D3D9CapturedStateFlag::PsConstants)) {
for (uint32_t i = 0; i < m_captures.psConsts.fConsts.size(); i++) {
if (m_captures.psConsts.fConsts[i])
dst->SetPixelShaderConstantF(i, (float*)&src->psConsts.fConsts[i], 1);
}
for (uint32_t i = 0; i < m_captures.psConsts.iConsts.size(); i++) {
if (m_captures.psConsts.iConsts[i])
dst->SetPixelShaderConstantI(i, (int*)&src->psConsts.iConsts[i], 1);
}
uint32_t boolMask = 0;
for (uint32_t i = 0; i < m_captures.psConsts.bConsts.size(); i++) {
if (m_captures.psConsts.bConsts[i])
boolMask |= 1u << i;
}
dst->SetPixelBoolBitfield(0, boolMask, src->psConsts.bConsts[0]);
}
}
template <D3D9StateFunction Func>
void ApplyOrCapture() {
if constexpr (Func == D3D9StateFunction::Apply)
ApplyOrCapture(m_parent, &m_state);
else if constexpr (Func == D3D9StateFunction::Capture)
ApplyOrCapture(this, m_deviceState);
}
template <
DxsoProgramType ProgramType,
D3D9ConstantType ConstantType,
typename T>
HRESULT SetShaderConstants(
UINT StartRegister,
const T* pConstantData,
UINT Count) {
auto SetHelper = [&](auto& setCaptures) {
if constexpr (ProgramType == DxsoProgramTypes::VertexShader)
m_captures.flags.set(D3D9CapturedStateFlag::VsConstants);
else
m_captures.flags.set(D3D9CapturedStateFlag::PsConstants);
for (uint32_t i = 0; i < Count; i++) {
uint32_t reg = StartRegister + i;
if constexpr (ConstantType == D3D9ConstantType::Float)
setCaptures.fConsts[reg] = true;
else if constexpr (ConstantType == D3D9ConstantType::Int)
setCaptures.iConsts[reg] = true;
else if constexpr (ConstantType == D3D9ConstantType::Bool)
setCaptures.bConsts[reg] = true;
}
UpdateStateConstants<
ProgramType,
ConstantType,
T>(
&m_state,
StartRegister,
pConstantData,
Count,
false);
return D3D_OK;
};
return ProgramType == DxsoProgramTypes::VertexShader
? SetHelper(m_captures.vsConsts)
: SetHelper(m_captures.psConsts);
}
HRESULT SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits);
HRESULT SetPixelBoolBitfield (uint32_t idx, uint32_t mask, uint32_t bits);
inline bool IsApplying() {
return m_applying;
}
private:
void CapturePixelRenderStates();
void CapturePixelSamplerStates();
void CapturePixelShaderStates();
void CaptureVertexRenderStates();
void CaptureVertexSamplerStates();
void CaptureVertexShaderStates();
void CaptureType(D3D9StateBlockType State);
D3D9CapturableState m_state;
D3D9StateCaptures m_captures;
D3D9CapturableState* m_deviceState;
bool m_applying = false;
};
}

106
src/d3d9/d3d9_subresource.h Normal file
View file

@ -0,0 +1,106 @@
#pragma once
#include "d3d9_resource.h"
#include "d3d9_common_texture.h"
namespace dxvk {
template <typename... Type>
class D3D9Subresource : public D3D9Resource<Type...> {
public:
D3D9Subresource(
D3D9DeviceEx* pDevice,
D3D9CommonTexture* pTexture,
UINT Face,
UINT MipLevel,
IDirect3DBaseTexture9* pContainer)
: D3D9Resource<Type...> ( pDevice )
, m_container ( pContainer )
, m_texture ( pTexture )
, m_face ( Face )
, m_mipLevel ( MipLevel ) { }
~D3D9Subresource() {
// We own the texture!
if (m_container == nullptr)
delete m_texture;
}
ULONG STDMETHODCALLTYPE AddRef() final {
if (m_container != nullptr)
return m_container->AddRef();
return D3D9Resource<Type...>::AddRef();
}
ULONG STDMETHODCALLTYPE Release() final {
if (m_container != nullptr)
return m_container->Release();
return D3D9Resource<Type...>::Release();
}
HRESULT STDMETHODCALLTYPE GetContainer(REFIID riid, void** ppContainer) final {
if (m_container != nullptr)
return m_container->QueryInterface(riid, ppContainer);
return this->GetDevice()->QueryInterface(riid, ppContainer);
}
D3D9CommonTexture* GetCommonTexture() {
return m_texture;
}
UINT GetFace() const {
return m_face;
}
UINT GetMipLevel() const {
return m_mipLevel;
}
UINT GetSubresource() const {
return m_texture->CalcSubresource(m_face, m_mipLevel);
}
Rc<DxvkImageView> GetImageView(bool Srgb) {
return m_texture->GetViews().SubresourceSample[m_face][m_mipLevel].Pick(Srgb);
}
Rc<DxvkImageView> GetRenderTargetView(bool Srgb) {
return m_texture->GetViews().SubresourceRenderTarget[m_face][m_mipLevel].Pick(Srgb);
}
VkImageLayout GetRenderTargetLayout() {
return m_texture->GetViews().GetRTLayout();
}
Rc<DxvkImageView> GetDepthStencilView() {
return m_texture->GetViews().SubresourceDepth[m_face][m_mipLevel];
}
VkImageLayout GetDepthLayout() {
return m_texture->GetViews().GetDepthLayout();
}
bool IsNull() {
return m_texture->Desc()->Format == D3D9Format::NULL_FORMAT;
}
IDirect3DBaseTexture9* GetBaseTexture() {
return m_container;
}
protected:
IDirect3DBaseTexture9* m_container;
D3D9CommonTexture* m_texture;
UINT m_face;
UINT m_mipLevel;
};
}

184
src/d3d9/d3d9_surface.cpp Normal file
View file

@ -0,0 +1,184 @@
#include "d3d9_surface.h"
#include "d3d9_texture.h"
#include "d3d9_device.h"
namespace dxvk {
D3D9Surface::D3D9Surface(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING Mapping)
: D3D9SurfaceBase(
pDevice,
new D3D9CommonTexture( pDevice, pDesc, D3DRTYPE_TEXTURE, Mapping ),
0, 0,
nullptr) { }
D3D9Surface::D3D9Surface(
D3D9DeviceEx* pDevice,
D3D9CommonTexture* pTexture,
UINT Face,
UINT MipLevel,
IDirect3DBaseTexture9* pContainer)
: D3D9SurfaceBase(
pDevice,
pTexture,
Face, MipLevel,
pContainer) { }
void D3D9Surface::AddRefPrivate() {
IDirect3DBaseTexture9* pContainer = this->m_container;
if (pContainer != nullptr) {
D3DRESOURCETYPE type = pContainer->GetType();
if (type == D3DRTYPE_TEXTURE)
reinterpret_cast<D3D9Texture2D*> (pContainer)->AddRefPrivate();
else //if (type == D3DRTYPE_CUBETEXTURE)
reinterpret_cast<D3D9TextureCube*>(pContainer)->AddRefPrivate();
return;
}
D3D9SurfaceBase::AddRefPrivate();
}
void D3D9Surface::ReleasePrivate() {
IDirect3DBaseTexture9* pContainer = this->m_container;
if (pContainer != nullptr) {
D3DRESOURCETYPE type = pContainer->GetType();
if (type == D3DRTYPE_TEXTURE)
reinterpret_cast<D3D9Texture2D*> (pContainer)->ReleasePrivate();
else //if (type == D3DRTYPE_CUBETEXTURE)
reinterpret_cast<D3D9TextureCube*>(pContainer)->ReleasePrivate();
return;
}
D3D9SurfaceBase::ReleasePrivate();
}
HRESULT STDMETHODCALLTYPE D3D9Surface::QueryInterface(REFIID riid, void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3DResource9)
|| riid == __uuidof(IDirect3DSurface9)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9Surface::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
D3DRESOURCETYPE STDMETHODCALLTYPE D3D9Surface::GetType() {
return D3DRTYPE_SURFACE;
}
HRESULT STDMETHODCALLTYPE D3D9Surface::GetDesc(D3DSURFACE_DESC *pDesc) {
if (pDesc == nullptr)
return D3DERR_INVALIDCALL;
auto& desc = *(m_texture->Desc());
pDesc->Format = static_cast<D3DFORMAT>(desc.Format);
pDesc->Type = D3DRTYPE_SURFACE;
pDesc->Usage = desc.Usage;
pDesc->Pool = desc.Pool;
pDesc->MultiSampleType = desc.MultiSample;
pDesc->MultiSampleQuality = desc.MultisampleQuality;
pDesc->Width = std::max(1u, desc.Width >> m_mipLevel);
pDesc->Height = std::max(1u, desc.Height >> m_mipLevel);
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE D3D9Surface::LockRect(D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
D3DBOX box;
if (pRect != nullptr) {
box.Left = pRect->left;
box.Right = pRect->right;
box.Top = pRect->top;
box.Bottom = pRect->bottom;
box.Front = 0;
box.Back = 1;
}
D3DLOCKED_BOX lockedBox;
HRESULT hr = m_parent->LockImage(
m_texture,
m_face, m_mipLevel,
&lockedBox,
pRect != nullptr ? &box : nullptr,
Flags);
pLockedRect->pBits = lockedBox.pBits;
pLockedRect->Pitch = lockedBox.RowPitch;
return hr;
}
HRESULT STDMETHODCALLTYPE D3D9Surface::UnlockRect() {
return m_parent->UnlockImage(
m_texture,
m_face, m_mipLevel);
}
HRESULT STDMETHODCALLTYPE D3D9Surface::GetDC(HDC *phDC) {
if (phDC == nullptr)
return D3DERR_INVALIDCALL;
const D3D9_COMMON_TEXTURE_DESC& desc = *m_texture->Desc();
D3DLOCKED_RECT lockedRect;
HRESULT hr = LockRect(&lockedRect, nullptr, 0);
if (FAILED(hr))
return hr;
D3DKMT_CREATEDCFROMMEMORY createInfo;
// In...
createInfo.pMemory = lockedRect.pBits;
createInfo.Format = static_cast<D3DFORMAT>(desc.Format);
createInfo.Width = desc.Width;
createInfo.Height = desc.Height;
createInfo.Pitch = lockedRect.Pitch;
createInfo.hDeviceDc = CreateCompatibleDC(NULL);
createInfo.pColorTable = nullptr;
// Out...
createInfo.hBitmap = nullptr;
createInfo.hDc = nullptr;
D3DKMTCreateDCFromMemory(&createInfo);
DeleteDC(createInfo.hDeviceDc);
// These should now be set...
m_dcDesc.hDC = createInfo.hDc;
m_dcDesc.hBitmap = createInfo.hBitmap;
*phDC = m_dcDesc.hDC;
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE D3D9Surface::ReleaseDC(HDC hDC) {
if (m_dcDesc.hDC == nullptr || m_dcDesc.hDC != hDC)
return D3DERR_INVALIDCALL;
D3DKMTDestroyDCFromMemory(&m_dcDesc);
HRESULT hr = UnlockRect();
if (FAILED(hr))
return hr;
return D3D_OK;
}
}

53
src/d3d9/d3d9_surface.h Normal file
View file

@ -0,0 +1,53 @@
#pragma once
#include "d3d9_subresource.h"
#include "d3d9_common_texture.h"
#include "../util/util_gdi.h"
namespace dxvk {
using D3D9GDIDesc = D3DKMT_DESTROYDCFROMMEMORY;
using D3D9SurfaceBase = D3D9Subresource<IDirect3DSurface9>;
class D3D9Surface final : public D3D9SurfaceBase {
public:
D3D9Surface(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING Mapping);
D3D9Surface(
D3D9DeviceEx* pDevice,
D3D9CommonTexture* pTexture,
UINT Face,
UINT MipLevel,
IDirect3DBaseTexture9* pContainer);
void AddRefPrivate();
void ReleasePrivate();
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;
HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC *pDesc) final;
HRESULT STDMETHODCALLTYPE LockRect(D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) final;
HRESULT STDMETHODCALLTYPE UnlockRect() final;
HRESULT STDMETHODCALLTYPE GetDC(HDC *phDC) final;
HRESULT STDMETHODCALLTYPE ReleaseDC(HDC hDC) final;
private:
D3D9GDIDesc m_dcDesc;
};
}

1216
src/d3d9/d3d9_swapchain.cpp Normal file

File diff suppressed because it is too large Load diff

226
src/d3d9/d3d9_swapchain.h Normal file
View file

@ -0,0 +1,226 @@
#pragma once
#include "d3d9_device_child.h"
#include "d3d9_device.h"
#include "d3d9_format.h"
#include "../dxvk/hud/dxvk_hud.h"
#include "../util/sync/sync_signal.h"
#include <vector>
namespace dxvk {
class D3D9Surface;
/**
* \brief Gamma control point
*
* Control points are stored as normalized
* 16-bit unsigned integer values that will
* be converted back to floats in the shader.
*/
struct D3D9_VK_GAMMA_CP {
uint16_t R, G, B, A;
};
using D3D9SwapChainExBase = D3D9DeviceChild<IDirect3DSwapChain9Ex>;
class D3D9SwapChainEx final : public D3D9SwapChainExBase {
static constexpr uint32_t NumControlPoints = 256;
public:
D3D9SwapChainEx(
D3D9DeviceEx* pDevice,
D3DPRESENT_PARAMETERS* pPresentParams,
const D3DDISPLAYMODEEX* pFullscreenDisplayMode);
~D3D9SwapChainEx();
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
HRESULT STDMETHODCALLTYPE Present(
const RECT* pSourceRect,
const RECT* pDestRect,
HWND hDestWindowOverride,
const RGNDATA* pDirtyRegion,
DWORD dwFlags);
HRESULT STDMETHODCALLTYPE GetFrontBufferData(IDirect3DSurface9* pDestSurface);
HRESULT STDMETHODCALLTYPE GetBackBuffer(
UINT iBackBuffer,
D3DBACKBUFFER_TYPE Type,
IDirect3DSurface9** ppBackBuffer);
HRESULT STDMETHODCALLTYPE GetRasterStatus(D3DRASTER_STATUS* pRasterStatus);
HRESULT STDMETHODCALLTYPE GetDisplayMode(D3DDISPLAYMODE* pMode);
HRESULT STDMETHODCALLTYPE GetPresentParameters(D3DPRESENT_PARAMETERS* pPresentationParameters);
HRESULT STDMETHODCALLTYPE GetLastPresentCount(UINT* pLastPresentCount);
HRESULT STDMETHODCALLTYPE GetPresentStats(D3DPRESENTSTATS* pPresentationStatistics);
HRESULT STDMETHODCALLTYPE GetDisplayModeEx(D3DDISPLAYMODEEX* pMode, D3DDISPLAYROTATION* pRotation);
void Reset(
D3DPRESENT_PARAMETERS* pPresentParams,
D3DDISPLAYMODEEX* pFullscreenDisplayMode);
HRESULT WaitForVBlank();
void SetGammaRamp(
DWORD Flags,
const D3DGAMMARAMP* pRamp);
void GetGammaRamp(D3DGAMMARAMP* pRamp);
void Invalidate(HWND hWindow);
HRESULT SetDialogBoxMode(bool bEnableDialogs);
D3D9Surface* GetBackBuffer(UINT iBackBuffer);
private:
enum BindingIds : uint32_t {
Image = 0,
Gamma = 1,
};
struct WindowState {
LONG style = 0;
LONG exstyle = 0;
RECT rect = { 0, 0, 0, 0 };
};
D3DPRESENT_PARAMETERS m_presentParams;
D3DGAMMARAMP m_ramp;
Rc<DxvkDevice> m_device;
Rc<DxvkContext> m_context;
Rc<vk::Presenter> m_presenter;
Rc<DxvkShader> m_vertShader;
Rc<DxvkShader> m_fragShader;
Rc<DxvkSampler> m_samplerFitting;
Rc<DxvkSampler> m_samplerScaling;
Rc<DxvkSampler> m_gammaSampler;
Rc<DxvkImage> m_gammaTexture;
Rc<DxvkImageView> m_gammaTextureView;
Rc<DxvkImage> m_swapImage;
Rc<DxvkImage> m_swapImageResolve;
Rc<DxvkImageView> m_swapImageView;
Rc<hud::Hud> m_hud;
DxvkInputAssemblyState m_iaState;
DxvkRasterizerState m_rsState;
DxvkMultisampleState m_msState;
DxvkDepthStencilState m_dsState;
DxvkLogicOpState m_loState;
DxvkBlendMode m_blendMode;
Com<D3D9Surface, false> m_backBuffer = nullptr;
RECT m_srcRect;
RECT m_dstRect;
DxvkSubmitStatus m_presentStatus;
std::vector<Rc<DxvkImageView>> m_imageViews;
uint64_t m_frameId = D3D9DeviceEx::MaxFrameLatency;
uint32_t m_frameLatencyCap = 0;
Rc<sync::Fence> m_frameLatencySignal;
bool m_dirty = true;
bool m_vsync = true;
bool m_dialog;
bool m_dialogChanged = false;
HWND m_window = nullptr;
HMONITOR m_monitor = nullptr;
MONITORINFOEXW m_monInfo;
WindowState m_windowState;
void PresentImage(UINT PresentInterval);
void SubmitPresent(const vk::PresenterSync& Sync, uint32_t FrameId);
void SynchronizePresent();
void RecreateSwapChain(
BOOL Vsync);
void CreatePresenter();
void CreateRenderTargetViews();
void CreateBackBuffer();
void CreateGammaTexture(
UINT NumControlPoints,
const D3D9_VK_GAMMA_CP* pControlPoints);
void DestroyGammaTexture();
void CreateHud();
void InitRenderState();
void InitSamplers();
void InitShaders();
void InitRamp();
uint32_t GetActualFrameLatency();
uint32_t PickFormats(
D3D9Format Format,
VkSurfaceFormatKHR* pDstFormats);
uint32_t PickPresentModes(
BOOL Vsync,
VkPresentModeKHR* pDstModes);
uint32_t PickImageCount(
UINT Preferred);
void NormalizePresentParameters(D3DPRESENT_PARAMETERS* pPresentParams);
HRESULT EnterFullscreenMode(
D3DPRESENT_PARAMETERS* pPresentParams,
const D3DDISPLAYMODEEX* pFullscreenDisplayMode);
HRESULT LeaveFullscreenMode();
HRESULT ChangeDisplayMode(
D3DPRESENT_PARAMETERS* pPresentParams,
const D3DDISPLAYMODEEX* pFullscreenDisplayMode);
HRESULT RestoreDisplayMode(HMONITOR hMonitor);
void UpdateMonitorInfo();
bool UpdatePresentRegion(const RECT* pSourceRect, const RECT* pDestRect);
VkExtent2D GetPresentExtent();
VkFullScreenExclusiveEXT PickFullscreenMode();
};
}

358
src/d3d9/d3d9_swvp_emu.cpp Normal file
View file

@ -0,0 +1,358 @@
#include "d3d9_swvp_emu.h"
#include "d3d9_device.h"
#include "d3d9_vertex_declaration.h"
#include "../spirv/spirv_module.h"
namespace dxvk {
// Doesn't compare everything, only what we use in SWVP.
size_t D3D9VertexDeclHash::operator () (const D3D9VertexElements& key) const {
DxvkHashState hash;
std::hash<BYTE> bytehash;
std::hash<WORD> wordhash;
for (auto& element : key) {
hash.add(wordhash(element.Stream));
hash.add(wordhash(element.Offset));
hash.add(bytehash(element.Type));
hash.add(bytehash(element.Method));
hash.add(bytehash(element.Usage));
hash.add(bytehash(element.UsageIndex));
}
return hash;
}
bool D3D9VertexDeclEq::operator () (const D3D9VertexElements& a, const D3D9VertexElements& b) const {
if (a.size() != b.size())
return false;
bool equal = true;
for (uint32_t i = 0; i < a.size(); i++)
equal &= std::memcmp(&a[i], &b[i], sizeof(a[0])) == 0;
return equal;
}
enum class DecltypeClass {
Float, Byte, Short, Dec, Half
};
enum DecltypeFlags {
Signed = 1,
Normalize = 2,
ReverseRGB = 4
};
struct Decltype {
DecltypeClass Class;
uint32_t VectorCount;
uint32_t Flags;
};
Decltype ClassifyDecltype(D3DDECLTYPE Type) {
switch (Type) {
case D3DDECLTYPE_FLOAT1: return { DecltypeClass::Float, 1, DecltypeFlags::Signed };
case D3DDECLTYPE_FLOAT2: return { DecltypeClass::Float, 2, DecltypeFlags::Signed };
case D3DDECLTYPE_FLOAT3: return { DecltypeClass::Float, 3, DecltypeFlags::Signed };
case D3DDECLTYPE_FLOAT4: return { DecltypeClass::Float, 4, DecltypeFlags::Signed };
case D3DDECLTYPE_D3DCOLOR: return { DecltypeClass::Byte, 4, DecltypeFlags::Normalize | DecltypeFlags::ReverseRGB };
case D3DDECLTYPE_UBYTE4: return { DecltypeClass::Byte, 4, 0 };
case D3DDECLTYPE_SHORT2: return { DecltypeClass::Short, 2, DecltypeFlags::Signed };
case D3DDECLTYPE_SHORT4: return { DecltypeClass::Short, 4, DecltypeFlags::Signed };
case D3DDECLTYPE_UBYTE4N: return { DecltypeClass::Byte, 4, DecltypeFlags::Normalize };
case D3DDECLTYPE_SHORT2N: return { DecltypeClass::Short, 2, DecltypeFlags::Signed | DecltypeFlags::Normalize };
case D3DDECLTYPE_SHORT4N: return { DecltypeClass::Short, 4, DecltypeFlags::Signed | DecltypeFlags::Normalize };
case D3DDECLTYPE_USHORT2N: return { DecltypeClass::Short, 2, DecltypeFlags::Normalize };
case D3DDECLTYPE_USHORT4N: return { DecltypeClass::Short, 4, DecltypeFlags::Normalize };
case D3DDECLTYPE_UDEC3: return { DecltypeClass::Dec, 3, 0 };
case D3DDECLTYPE_DEC3N: return { DecltypeClass::Dec, 3, DecltypeFlags::Signed | DecltypeFlags::Normalize };
case D3DDECLTYPE_FLOAT16_2: return { DecltypeClass::Half, 2, DecltypeFlags::Signed };
case D3DDECLTYPE_FLOAT16_4: return { DecltypeClass::Half, 4, DecltypeFlags::Signed };
default: return { DecltypeClass::Float, 4, DecltypeFlags::Signed };
}
}
class D3D9SWVPEmulatorGenerator {
public:
D3D9SWVPEmulatorGenerator(const std::string& name) {
m_entryPointId = m_module.allocateId();
m_module.setDebugSource(
spv::SourceLanguageUnknown, 0,
m_module.addDebugString(name.c_str()),
nullptr);
m_module.setMemoryModel(
spv::AddressingModelLogical,
spv::MemoryModelGLSL450);
m_module.enableCapability(spv::CapabilityGeometry);
m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeInputPoints);
m_module.setExecutionMode(m_entryPointId, spv::ExecutionModeOutputPoints);
// This has to be > 0 for some reason even though
// we will never emit a vertex
m_module.setOutputVertices(m_entryPointId, 1);
m_module.setInvocations(m_entryPointId, 1);
m_module.functionBegin(m_module.defVoidType(), m_entryPointId, m_module.defFunctionType(
m_module.defVoidType(), 0, nullptr), spv::FunctionControlMaskNone);
m_module.opLabel(m_module.allocateId());
}
void compile(const D3D9VertexDecl* pDecl) {
uint32_t uint_t = m_module.defIntType(32, false);
uint32_t float_t = m_module.defFloatType(32);
uint32_t vec4_t = m_module.defVectorType(float_t, 4);
uint32_t vec4_singular_array_t = m_module.defArrayType(vec4_t, m_module.constu32(1));
// Setup the buffer
uint32_t bufferSlot = getSWVPBufferSlot();
uint32_t arrayType = m_module.defRuntimeArrayTypeUnique(uint_t);
m_module.decorateArrayStride(arrayType, sizeof(uint32_t));
uint32_t buffer_t = m_module.defStructTypeUnique(1, &arrayType);
m_module.memberDecorateOffset(buffer_t, 0, 0);
m_module.decorate(buffer_t, spv::DecorationBufferBlock);
uint32_t buffer = m_module.newVar(m_module.defPointerType(buffer_t, spv::StorageClassUniform), spv::StorageClassUniform);
m_module.decorateDescriptorSet(buffer, 0);
m_module.decorateBinding(buffer, bufferSlot);
DxvkResourceSlot bufferRes;
bufferRes.slot = bufferSlot;
bufferRes.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bufferRes.view = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
bufferRes.access = VK_ACCESS_SHADER_WRITE_BIT;
m_resourceSlots.push_back(bufferRes);
// Load our builtins
uint32_t primitiveIdPtr = m_module.newVar(m_module.defPointerType(uint_t, spv::StorageClassInput), spv::StorageClassInput);
m_module.decorateBuiltIn(primitiveIdPtr, spv::BuiltInPrimitiveId);
m_entryPointInterfaces.push_back(primitiveIdPtr);
uint32_t primitiveId = m_module.opLoad(uint_t, primitiveIdPtr);
// The size of any given vertex
uint32_t vertexSize = m_module.constu32(pDecl->GetSize() / sizeof(uint32_t));
//The offset of this vertex from the beginning of the buffer
uint32_t thisVertexOffset = m_module.opIMul(uint_t, vertexSize, primitiveId);
for (auto& element : pDecl->GetElements()) {
// Load the slot associated with this element
DxsoSemantic semantic = { DxsoUsage(element.Usage), element.UsageIndex };
uint32_t elementPtr;
uint32_t elementVar;
elementPtr = m_module.newVar(m_module.defPointerType(vec4_singular_array_t, spv::StorageClassInput), spv::StorageClassInput);
if ((semantic.usage == DxsoUsage::Position || semantic.usage == DxsoUsage::PositionT) && element.UsageIndex == 0) {
// Load from builtin
m_module.decorateBuiltIn(elementPtr, spv::BuiltInPosition);
}
else {
// Load from slot
uint32_t slotIdx = RegisterLinkerSlot(semantic);
m_module.decorateLocation(elementPtr, slotIdx);
m_interfaceSlots.inputSlots |= 1u << slotIdx;
}
uint32_t zero = m_module.constu32(0);
elementVar = m_module.opAccessChain(m_module.defPointerType(vec4_t, spv::StorageClassInput), elementPtr, 1, &zero);
elementVar = m_module.opLoad(vec4_t, elementVar);
m_entryPointInterfaces.push_back(elementPtr);
// The offset of this element from the beginning of any given vertex
uint32_t perVertexElementOffset = m_module.constu32(element.Offset / sizeof(uint32_t));
// The offset of this element from the beginning of the buffer for **THIS** vertex
uint32_t elementOffset = m_module.opIAdd(uint_t, thisVertexOffset, perVertexElementOffset);
// Write to the buffer at the element offset for each part of the vector.
Decltype elementInfo = ClassifyDecltype(D3DDECLTYPE(element.Type));
if (elementInfo.Class == DecltypeClass::Dec) {
// TODO!
Logger::warn("Encountered DEC3/UDEC3N class, ignoring...");
continue;
}
uint32_t vecn_t = m_module.defVectorType(float_t, elementInfo.VectorCount);
uint32_t componentSet;
// Modifiers...
if (elementInfo.Flags & DecltypeFlags::ReverseRGB) {
std::array<uint32_t, 4> indices = { 2, 1, 0, 3 };
componentSet = m_module.opVectorShuffle(vecn_t, elementVar, elementVar, elementInfo.VectorCount, indices.data());
}
else {
std::array<uint32_t, 4> indices = { 0, 1, 2, 3 };
componentSet = m_module.opVectorShuffle(vecn_t, elementVar, elementVar, elementInfo.VectorCount, indices.data());
}
if (elementInfo.Flags & DecltypeFlags::Normalize)
componentSet = m_module.opVectorTimesScalar(vecn_t, componentSet, m_module.constf32(255.0f));
bool isSigned = elementInfo.Flags & DecltypeFlags::Signed;
// Convert the component to the correct type/value.
switch (elementInfo.Class) {
case DecltypeClass::Float: break; // Do nothing!
case DecltypeClass::Byte: {
m_module.enableCapability(spv::CapabilityInt8);
uint32_t type = m_module.defIntType(8, isSigned);
type = m_module.defVectorType(type, elementInfo.VectorCount);
componentSet = isSigned
? m_module.opConvertFtoS(type, componentSet)
: m_module.opConvertFtoU(type, componentSet);
break;
}
case DecltypeClass::Short: {
m_module.enableCapability(spv::CapabilityInt16);
uint32_t type = m_module.defIntType(16, isSigned);
type = m_module.defVectorType(type, elementInfo.VectorCount);
componentSet = isSigned
? m_module.opConvertFtoS(type, componentSet)
: m_module.opConvertFtoU(type, componentSet);
break;
}
case DecltypeClass::Half: {
m_module.enableCapability(spv::CapabilityFloat16);
uint32_t type = m_module.defFloatType(16);
type = m_module.defVectorType(type, elementInfo.VectorCount);
componentSet = m_module.opFConvert(type, componentSet);
break;
}
case DecltypeClass::Dec: {
// TODO!
break;
}
}
// Bitcast to dwords before we write.
uint32_t dwordCount = GetDecltypeSize(D3DDECLTYPE(element.Type)) / sizeof(uint32_t);
uint32_t dwordVector = m_module.opBitcast(
m_module.defVectorType(uint_t, dwordCount),
componentSet);
// Finally write each dword to the buffer!
for (uint32_t i = 0; i < dwordCount; i++) {
std::array<uint32_t, 2> bufferIndices = { m_module.constu32(0), elementOffset };
uint32_t writeDest = m_module.opAccessChain(m_module.defPointerType(uint_t, spv::StorageClassUniform), buffer, bufferIndices.size(), bufferIndices.data());
uint32_t currentDword = m_module.opCompositeExtract(uint_t, dwordVector, 1, &i);
m_module.opStore(writeDest, currentDword);
elementOffset = m_module.opIAdd(uint_t, elementOffset, m_module.constu32(1));
}
}
}
Rc<DxvkShader> finalize() {
m_module.opReturn();
m_module.functionEnd();
m_module.addEntryPoint(m_entryPointId,
spv::ExecutionModelGeometry, "main",
m_entryPointInterfaces.size(),
m_entryPointInterfaces.data());
m_module.setDebugName(m_entryPointId, "main");
DxvkShaderConstData constData = { };
return new DxvkShader(
VK_SHADER_STAGE_GEOMETRY_BIT,
m_resourceSlots.size(),
m_resourceSlots.data(),
m_interfaceSlots,
m_module.compile(),
DxvkShaderOptions(),
std::move(constData));
}
private:
SpirvModule m_module;
std::vector<uint32_t> m_entryPointInterfaces;
uint32_t m_entryPointId = 0;
std::vector<DxvkResourceSlot> m_resourceSlots;
DxvkInterfaceSlots m_interfaceSlots;
};
Rc<DxvkShader> D3D9SWVPEmulator::GetShaderModule(D3D9DeviceEx* pDevice, const D3D9VertexDecl* pDecl) {
auto& elements = pDecl->GetElements();
// Use the shader's unique key for the lookup
{ std::unique_lock<std::mutex> lock(m_mutex);
auto entry = m_modules.find(elements);
if (entry != m_modules.end())
return entry->second;
}
Sha1Hash hash = Sha1Hash::compute(
elements.data(), elements.size() * sizeof(elements[0]));
DxvkShaderKey key = { VK_SHADER_STAGE_GEOMETRY_BIT , hash };
std::string name = str::format("SWVP_", key.toString());
// This shader has not been compiled yet, so we have to create a
// new module. This takes a while, so we won't lock the structure.
D3D9SWVPEmulatorGenerator generator(name);
generator.compile(pDecl);
Rc<DxvkShader> shader = generator.finalize();
shader->setShaderKey(key);
pDevice->GetDXVKDevice()->registerShader(shader);
const std::string dumpPath = env::getEnvVar("DXVK_SHADER_DUMP_PATH");
if (dumpPath.size() != 0) {
std::ofstream dumpStream(
str::format(dumpPath, "/", name, ".spv"),
std::ios_base::binary | std::ios_base::trunc);
shader->dump(dumpStream);
}
// Insert the new module into the lookup table. If another thread
// has compiled the same shader in the meantime, we should return
// that object instead and discard the newly created module.
{ std::unique_lock<std::mutex> lock(m_mutex);
auto status = m_modules.insert({ elements, shader });
if (!status.second)
return status.first->second;
}
return shader;
}
}

36
src/d3d9/d3d9_swvp_emu.h Normal file
View file

@ -0,0 +1,36 @@
#pragma once
#include "d3d9_include.h"
#include "../dxvk/dxvk_shader.h"
namespace dxvk {
class D3D9VertexDecl;
class D3D9DeviceEx;
struct D3D9VertexDeclHash {
size_t operator () (const D3D9VertexElements& key) const;
};
struct D3D9VertexDeclEq {
bool operator () (const D3D9VertexElements& a, const D3D9VertexElements& b) const;
};
class D3D9SWVPEmulator {
public:
Rc<DxvkShader> GetShaderModule(D3D9DeviceEx* pDevice, const D3D9VertexDecl* pDecl);
private:
std::mutex m_mutex;
std::unordered_map<
D3D9VertexElements, Rc<DxvkShader>,
D3D9VertexDeclHash, D3D9VertexDeclEq> m_modules;
};
}

252
src/d3d9/d3d9_texture.cpp Normal file
View file

@ -0,0 +1,252 @@
#include "d3d9_texture.h"
#include "d3d9_util.h"
namespace dxvk {
// Direct3DTexture9
D3D9Texture2D::D3D9Texture2D(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING Mapping)
: D3D9Texture2DBase( pDevice, pDesc, D3DRTYPE_TEXTURE, Mapping ) { }
HRESULT STDMETHODCALLTYPE D3D9Texture2D::QueryInterface(REFIID riid, void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3DResource9)
|| riid == __uuidof(IDirect3DBaseTexture9)
|| riid == __uuidof(IDirect3DTexture9)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9Texture2D::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
D3DRESOURCETYPE STDMETHODCALLTYPE D3D9Texture2D::GetType() {
return D3DRTYPE_TEXTURE;
}
HRESULT STDMETHODCALLTYPE D3D9Texture2D::GetLevelDesc(UINT Level, D3DSURFACE_DESC *pDesc) {
auto* surface = GetSubresource(Level);
if (surface == nullptr)
return D3DERR_INVALIDCALL;
return surface->GetDesc(pDesc);
}
HRESULT STDMETHODCALLTYPE D3D9Texture2D::GetSurfaceLevel(UINT Level, IDirect3DSurface9** ppSurfaceLevel) {
InitReturnPtr(ppSurfaceLevel);
auto* surface = GetSubresource(Level);
if (ppSurfaceLevel == nullptr || surface == nullptr)
return D3DERR_INVALIDCALL;
*ppSurfaceLevel = ref(surface);
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE D3D9Texture2D::LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
auto* surface = GetSubresource(Level);
if (surface == nullptr || pLockedRect == nullptr)
return D3DERR_INVALIDCALL;
return surface->LockRect(pLockedRect, pRect, Flags);
}
HRESULT STDMETHODCALLTYPE D3D9Texture2D::UnlockRect(UINT Level) {
auto* surface = GetSubresource(Level);
if (surface == nullptr)
return D3DERR_INVALIDCALL;
return surface->UnlockRect();
}
HRESULT STDMETHODCALLTYPE D3D9Texture2D::AddDirtyRect(CONST RECT* pDirtyRect) {
return D3D_OK;
}
// Direct3DVolumeTexture9
D3D9Texture3D::D3D9Texture3D(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING Mapping)
: D3D9Texture3DBase( pDevice, pDesc, D3DRTYPE_VOLUMETEXTURE, Mapping ) { }
HRESULT STDMETHODCALLTYPE D3D9Texture3D::QueryInterface(REFIID riid, void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3DResource9)
|| riid == __uuidof(IDirect3DBaseTexture9)
|| riid == __uuidof(IDirect3DVolumeTexture9)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9Texture3D::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
D3DRESOURCETYPE STDMETHODCALLTYPE D3D9Texture3D::GetType() {
return D3DRTYPE_VOLUMETEXTURE;
}
HRESULT STDMETHODCALLTYPE D3D9Texture3D::GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc) {
auto* volume = GetSubresource(Level);
if (volume == nullptr)
return D3DERR_INVALIDCALL;
return volume->GetDesc(pDesc);
}
HRESULT STDMETHODCALLTYPE D3D9Texture3D::GetVolumeLevel(UINT Level, IDirect3DVolume9** ppVolumeLevel) {
InitReturnPtr(ppVolumeLevel);
auto* volume = GetSubresource(Level);
if (ppVolumeLevel == nullptr || volume == nullptr)
return D3DERR_INVALIDCALL;
*ppVolumeLevel = ref(volume);
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE D3D9Texture3D::LockBox(UINT Level, D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) {
auto* volume = GetSubresource(Level);
if (volume == nullptr || pLockedBox == nullptr)
return D3DERR_INVALIDCALL;
return volume->LockBox(pLockedBox, pBox, Flags);
}
HRESULT STDMETHODCALLTYPE D3D9Texture3D::UnlockBox(UINT Level) {
auto* volume = GetSubresource(Level);
if (volume == nullptr)
return D3DERR_INVALIDCALL;
return volume->UnlockBox();
}
HRESULT STDMETHODCALLTYPE D3D9Texture3D::AddDirtyBox(CONST D3DBOX* pDirtyBox) {
return D3D_OK;
}
// Direct3DCubeTexture9
D3D9TextureCube::D3D9TextureCube(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING Mapping)
: D3D9TextureCubeBase( pDevice, pDesc, D3DRTYPE_CUBETEXTURE, Mapping ) { }
HRESULT STDMETHODCALLTYPE D3D9TextureCube::QueryInterface(REFIID riid, void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3DResource9)
|| riid == __uuidof(IDirect3DBaseTexture9)
|| riid == __uuidof(IDirect3DCubeTexture9)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9TextureCube::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
D3DRESOURCETYPE STDMETHODCALLTYPE D3D9TextureCube::GetType() {
return D3DRTYPE_CUBETEXTURE;
}
HRESULT STDMETHODCALLTYPE D3D9TextureCube::GetLevelDesc(UINT Level, D3DSURFACE_DESC *pDesc) {
auto* surface = GetSubresource(Level);
if (surface == nullptr)
return D3DERR_INVALIDCALL;
return surface->GetDesc(pDesc);
}
HRESULT STDMETHODCALLTYPE D3D9TextureCube::GetCubeMapSurface(D3DCUBEMAP_FACES Face, UINT Level, IDirect3DSurface9** ppSurfaceLevel) {
InitReturnPtr(ppSurfaceLevel);
if (Level >= m_texture.Desc()->MipLevels)
return D3DERR_INVALIDCALL;
auto* surface = GetSubresource(
m_texture.CalcSubresource(UINT(Face), Level));
if (ppSurfaceLevel == nullptr || surface == nullptr)
return D3DERR_INVALIDCALL;
*ppSurfaceLevel = ref(surface);
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE D3D9TextureCube::LockRect(D3DCUBEMAP_FACES Face, UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
auto* surface = GetSubresource(
m_texture.CalcSubresource(UINT(Face), Level));
if (surface == nullptr || pLockedRect == nullptr)
return D3DERR_INVALIDCALL;
return surface->LockRect(pLockedRect, pRect, Flags);
}
HRESULT STDMETHODCALLTYPE D3D9TextureCube::UnlockRect(D3DCUBEMAP_FACES Face, UINT Level) {
auto* surface = GetSubresource(
m_texture.CalcSubresource(UINT(Face), Level));
if (surface == nullptr)
return D3DERR_INVALIDCALL;
return surface->UnlockRect();
}
HRESULT STDMETHODCALLTYPE D3D9TextureCube::AddDirtyRect(D3DCUBEMAP_FACES Face, CONST RECT* pDirtyRect) {
return D3D_OK;
}
}

236
src/d3d9/d3d9_texture.h Normal file
View file

@ -0,0 +1,236 @@
#pragma once
#include "d3d9_device.h"
#include "d3d9_surface.h"
#include "d3d9_volume.h"
#include "d3d9_util.h"
#include <vector>
#include <list>
#include <mutex>
#include <new>
namespace dxvk {
template <typename SubresourceType, typename... Base>
class D3D9BaseTexture : public D3D9Resource<Base...> {
public:
struct alignas(16) SubresourceData { uint8_t data[sizeof(SubresourceType)]; };
D3D9BaseTexture(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3DRESOURCETYPE ResourceType,
D3D9_VK_FORMAT_MAPPING Mapping)
: D3D9Resource<Base...> ( pDevice )
, m_texture ( pDevice, pDesc, ResourceType, Mapping )
, m_lod ( 0 )
, m_autogenFilter ( D3DTEXF_LINEAR ) {
const uint32_t arraySlices = m_texture.Desc()->ArraySize;
const uint32_t mipLevels = m_texture.Desc()->MipLevels;
m_subresources.resize(arraySlices * mipLevels);
for (uint32_t i = 0; i < arraySlices; i++) {
for (uint32_t j = 0; j < mipLevels; j++) {
const uint32_t subresource = m_texture.CalcSubresource(i, j);
SubresourceType* subObj = this->GetSubresource(subresource);
new (subObj) SubresourceType(
pDevice,
&m_texture,
i, j,
this);
}
}
}
~D3D9BaseTexture() {
for (uint32_t i = 0; i < m_subresources.size(); i++) {
SubresourceType* subObj = this->GetSubresource(i);
subObj->~SubresourceType();
}
}
DWORD STDMETHODCALLTYPE SetLOD(DWORD LODNew) final {
DWORD oldLod = m_lod;
m_lod = LODNew;
m_texture.RecreateSampledView(LODNew);
if (this->GetPrivateRefCount() > 0)
this->m_parent->MarkSamplersDirty();
return oldLod;
}
DWORD STDMETHODCALLTYPE GetLOD() final {
return m_lod;
}
DWORD STDMETHODCALLTYPE GetLevelCount() final {
return m_texture.Desc()->MipLevels;
}
HRESULT STDMETHODCALLTYPE SetAutoGenFilterType(D3DTEXTUREFILTERTYPE FilterType) final {
m_autogenFilter = FilterType;
return D3D_OK;
}
D3DTEXTUREFILTERTYPE STDMETHODCALLTYPE GetAutoGenFilterType() final {
return m_autogenFilter;
}
void STDMETHODCALLTYPE GenerateMipSubLevels() final {
if (m_texture.IsAutomaticMip())
this->m_parent->GenerateMips(&m_texture);
}
D3D9CommonTexture* GetCommonTexture() {
return &m_texture;
}
SubresourceType* GetSubresource(UINT Subresource) {
if (unlikely(Subresource >= m_subresources.size()))
return nullptr;
return reinterpret_cast<SubresourceType*>(&m_subresources[Subresource]);
}
protected:
D3D9CommonTexture m_texture;
DWORD m_lod;
D3DTEXTUREFILTERTYPE m_autogenFilter;
std::vector<SubresourceData> m_subresources;
};
using D3D9Texture2DBase = D3D9BaseTexture<D3D9Surface, IDirect3DTexture9>;
class D3D9Texture2D final : public D3D9Texture2DBase {
public:
D3D9Texture2D(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING Mapping);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
D3DRESOURCETYPE STDMETHODCALLTYPE GetType();
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC *pDesc);
HRESULT STDMETHODCALLTYPE GetSurfaceLevel(UINT Level, IDirect3DSurface9** ppSurfaceLevel);
HRESULT STDMETHODCALLTYPE LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags);
HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level);
HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect);
};
using D3D9Texture3DBase = D3D9BaseTexture<D3D9Volume, IDirect3DVolumeTexture9>;
class D3D9Texture3D final : public D3D9Texture3DBase {
public:
D3D9Texture3D(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING Mapping);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
D3DRESOURCETYPE STDMETHODCALLTYPE GetType();
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc);
HRESULT STDMETHODCALLTYPE GetVolumeLevel(UINT Level, IDirect3DVolume9** ppSurfaceLevel);
HRESULT STDMETHODCALLTYPE LockBox(UINT Level, D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags);
HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level);
HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox);
};
using D3D9TextureCubeBase = D3D9BaseTexture<D3D9Surface, IDirect3DCubeTexture9>;
class D3D9TextureCube final : public D3D9TextureCubeBase {
public:
D3D9TextureCube(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING Mapping);
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
D3DRESOURCETYPE STDMETHODCALLTYPE GetType();
HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC *pDesc);
HRESULT STDMETHODCALLTYPE GetCubeMapSurface(D3DCUBEMAP_FACES Face, UINT Level, IDirect3DSurface9** ppSurfaceLevel);
HRESULT STDMETHODCALLTYPE LockRect(D3DCUBEMAP_FACES Face, UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags);
HRESULT STDMETHODCALLTYPE UnlockRect(D3DCUBEMAP_FACES Face, UINT Level);
HRESULT STDMETHODCALLTYPE AddDirtyRect(D3DCUBEMAP_FACES Face, CONST RECT* pDirtyRect);
};
inline D3D9CommonTexture* GetCommonTexture(IDirect3DBaseTexture9* ptr) {
if (ptr == nullptr)
return nullptr;
switch (ptr->GetType()) {
case D3DRTYPE_TEXTURE: return static_cast<D3D9Texture2D*> (ptr)->GetCommonTexture();
case D3DRTYPE_CUBETEXTURE: return static_cast<D3D9TextureCube*>(ptr)->GetCommonTexture();
case D3DRTYPE_VOLUMETEXTURE: return static_cast<D3D9Texture3D*> (ptr)->GetCommonTexture();
default:
Logger::warn("Unknown texture resource type."); break;
}
return nullptr;
}
inline D3D9CommonTexture* GetCommonTexture(D3D9Surface* ptr) {
if (ptr == nullptr)
return nullptr;
return ptr->GetCommonTexture();
}
inline D3D9CommonTexture* GetCommonTexture(IDirect3DSurface9* ptr) {
return GetCommonTexture(static_cast<D3D9Surface*>(ptr));
}
inline void TextureRefPrivate(IDirect3DBaseTexture9* tex, bool AddRef) {
if (tex == nullptr)
return;
switch (tex->GetType()) {
case D3DRTYPE_TEXTURE: CastRefPrivate<D3D9Texture2D> (tex, AddRef); break;
case D3DRTYPE_CUBETEXTURE: CastRefPrivate<D3D9TextureCube>(tex, AddRef); break;
case D3DRTYPE_VOLUMETEXTURE: CastRefPrivate<D3D9Texture3D> (tex, AddRef); break;
default:
Logger::warn("Unknown texture resource type."); break;
}
}
inline void TextureChangePrivate(IDirect3DBaseTexture9*& dst, IDirect3DBaseTexture9* src) {
TextureRefPrivate(dst, false);
TextureRefPrivate(src, true);
dst = src;
}
}

414
src/d3d9/d3d9_util.cpp Normal file
View file

@ -0,0 +1,414 @@
#include "d3d9_util.h"
namespace dxvk {
typedef HRESULT (STDMETHODCALLTYPE *D3DXDisassembleShader) (
const void* pShader,
BOOL EnableColorCode,
char* pComments,
ID3DBlob** ppDisassembly); // ppDisassembly is actually a D3DXBUFFER, but it has the exact same vtable as a ID3DBlob at the start.
D3DXDisassembleShader g_pfnDisassembleShader = nullptr;
HRESULT DisassembleShader(
const void* pShader,
BOOL EnableColorCode,
char* pComments,
ID3DBlob** ppDisassembly) {
if (g_pfnDisassembleShader == nullptr) {
HMODULE d3d9x = LoadLibraryA("d3dx9.dll");
if (d3d9x == nullptr)
d3d9x = LoadLibraryA("d3dx9_43.dll");
g_pfnDisassembleShader =
reinterpret_cast<D3DXDisassembleShader>(GetProcAddress(d3d9x, "D3DXDisassembleShader"));
}
if (g_pfnDisassembleShader == nullptr)
return D3DERR_INVALIDCALL;
return g_pfnDisassembleShader(
pShader,
EnableColorCode,
pComments,
ppDisassembly);
}
HRESULT DecodeMultiSampleType(
D3DMULTISAMPLE_TYPE MultiSample,
DWORD MultisampleQuality,
VkSampleCountFlagBits* pCount) {
uint32_t sampleCount = std::max<uint32_t>(MultiSample, 1u);
// Check if this is a power of two...
if (sampleCount & (sampleCount - 1))
return D3DERR_INVALIDCALL;
if (MultiSample == D3DMULTISAMPLE_NONMASKABLE)
sampleCount = 1u << MultisampleQuality;
if (pCount != nullptr)
*pCount = VkSampleCountFlagBits(sampleCount);
return D3D_OK;
}
VkFormat GetPackedDepthStencilFormat(D3D9Format Format) {
switch (Format) {
case D3D9Format::D15S1:
return VK_FORMAT_D16_UNORM_S8_UINT; // This should never happen!
case D3D9Format::D16:
case D3D9Format::D16_LOCKABLE:
case D3D9Format::DF16:
return VK_FORMAT_D16_UNORM;
case D3D9Format::D24X8:
case D3D9Format::DF24:
return VK_FORMAT_X8_D24_UNORM_PACK32;
case D3D9Format::D24X4S4:
case D3D9Format::D24FS8:
case D3D9Format::D24S8:
case D3D9Format::INTZ:
return VK_FORMAT_D24_UNORM_S8_UINT;
case D3D9Format::D32:
case D3D9Format::D32_LOCKABLE:
case D3D9Format::D32F_LOCKABLE:
return VK_FORMAT_D32_SFLOAT;
case D3D9Format::S8_LOCKABLE:
return VK_FORMAT_S8_UINT;
default:
return VK_FORMAT_UNDEFINED;
}
}
VkFormatFeatureFlags GetImageFormatFeatures(DWORD Usage) {
VkFormatFeatureFlags features = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
if (Usage & D3DUSAGE_DEPTHSTENCIL)
features |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (Usage & D3DUSAGE_RENDERTARGET)
features |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
return features;
}
VkImageUsageFlags GetImageUsageFlags(DWORD Usage) {
VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT;
if (Usage & D3DUSAGE_DEPTHSTENCIL)
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (Usage & D3DUSAGE_RENDERTARGET)
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
return usage;
}
uint32_t GetVertexCount(D3DPRIMITIVETYPE type, UINT count) {
switch (type) {
default:
case D3DPT_TRIANGLELIST: return count * 3;
case D3DPT_POINTLIST: return count;
case D3DPT_LINELIST: return count * 2;
case D3DPT_LINESTRIP: return count + 1;
case D3DPT_TRIANGLESTRIP: return count + 2;
case D3DPT_TRIANGLEFAN: return count + 2;
}
}
DxvkInputAssemblyState DecodeInputAssemblyState(D3DPRIMITIVETYPE type) {
switch (type) {
default:
case D3DPT_TRIANGLELIST:
return { VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_FALSE, 0 };
case D3DPT_POINTLIST:
return { VK_PRIMITIVE_TOPOLOGY_POINT_LIST, VK_FALSE, 0 };
case D3DPT_LINELIST:
return { VK_PRIMITIVE_TOPOLOGY_LINE_LIST, VK_FALSE, 0 };
case D3DPT_LINESTRIP:
return { VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, VK_TRUE, 0 };
case D3DPT_TRIANGLESTRIP:
return { VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, VK_TRUE, 0 };
case D3DPT_TRIANGLEFAN:
return { VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, VK_TRUE, 0 };
}
}
VkBlendFactor DecodeBlendFactor(D3DBLEND BlendFactor, bool IsAlpha) {
switch (BlendFactor) {
default:
case D3DBLEND_ZERO: return VK_BLEND_FACTOR_ZERO;
case D3DBLEND_ONE: return VK_BLEND_FACTOR_ONE;
case D3DBLEND_SRCCOLOR: return VK_BLEND_FACTOR_SRC_COLOR;
case D3DBLEND_INVSRCCOLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
case D3DBLEND_SRCALPHA: return VK_BLEND_FACTOR_SRC_ALPHA;
case D3DBLEND_INVSRCALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
case D3DBLEND_DESTALPHA: return VK_BLEND_FACTOR_DST_ALPHA;
case D3DBLEND_INVDESTALPHA: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
case D3DBLEND_DESTCOLOR: return VK_BLEND_FACTOR_DST_COLOR;
case D3DBLEND_INVDESTCOLOR: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
case D3DBLEND_SRCALPHASAT: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
case D3DBLEND_BOTHSRCALPHA: return VK_BLEND_FACTOR_SRC_ALPHA;
case D3DBLEND_BOTHINVSRCALPHA: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
case D3DBLEND_BLENDFACTOR: return IsAlpha ? VK_BLEND_FACTOR_CONSTANT_ALPHA : VK_BLEND_FACTOR_CONSTANT_COLOR;
case D3DBLEND_INVBLENDFACTOR: return IsAlpha ? VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA : VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
case D3DBLEND_SRCCOLOR2: return VK_BLEND_FACTOR_SRC1_COLOR;
case D3DBLEND_INVSRCCOLOR2: return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
}
}
VkBlendOp DecodeBlendOp(D3DBLENDOP BlendOp) {
switch (BlendOp) {
default:
case D3DBLENDOP_ADD: return VK_BLEND_OP_ADD;
case D3DBLENDOP_SUBTRACT: return VK_BLEND_OP_SUBTRACT;
case D3DBLENDOP_REVSUBTRACT: return VK_BLEND_OP_REVERSE_SUBTRACT;
case D3DBLENDOP_MIN: return VK_BLEND_OP_MIN;
case D3DBLENDOP_MAX: return VK_BLEND_OP_MAX;
}
}
VkFilter DecodeFilter(D3DTEXTUREFILTERTYPE Filter) {
switch (Filter) {
case D3DTEXF_NONE:
case D3DTEXF_POINT:
return VK_FILTER_NEAREST;
default:
return VK_FILTER_LINEAR;
}
}
D3D9MipFilter DecodeMipFilter(D3DTEXTUREFILTERTYPE Filter) {
D3D9MipFilter filter;
filter.MipsEnabled = Filter != D3DTEXF_NONE;
switch (Filter) {
case D3DTEXF_POINT:
case D3DTEXF_NONE:
filter.MipFilter = VK_SAMPLER_MIPMAP_MODE_NEAREST; break;
default:
filter.MipFilter = VK_SAMPLER_MIPMAP_MODE_LINEAR; break;
}
return filter;
}
bool IsAnisotropic(D3DTEXTUREFILTERTYPE Filter) {
return Filter == D3DTEXF_ANISOTROPIC;
}
VkSamplerAddressMode DecodeAddressMode(D3DTEXTUREADDRESS Mode) {
switch (Mode) {
default:
case D3DTADDRESS_WRAP:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case D3DTADDRESS_MIRROR:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
case D3DTADDRESS_CLAMP:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case D3DTADDRESS_BORDER:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
case D3DTADDRESS_MIRRORONCE:
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
}
}
VkCompareOp DecodeCompareOp(D3DCMPFUNC Func) {
switch (Func) {
default:
case D3DCMP_NEVER: return VK_COMPARE_OP_NEVER;
case D3DCMP_LESS: return VK_COMPARE_OP_LESS;
case D3DCMP_EQUAL: return VK_COMPARE_OP_EQUAL;
case D3DCMP_LESSEQUAL: return VK_COMPARE_OP_LESS_OR_EQUAL;
case D3DCMP_GREATER: return VK_COMPARE_OP_GREATER;
case D3DCMP_NOTEQUAL: return VK_COMPARE_OP_NOT_EQUAL;
case D3DCMP_GREATEREQUAL: return VK_COMPARE_OP_GREATER_OR_EQUAL;
case D3DCMP_ALWAYS: return VK_COMPARE_OP_ALWAYS;
}
}
VkStencilOp DecodeStencilOp(D3DSTENCILOP Op) {
switch (Op) {
default:
case D3DSTENCILOP_KEEP: return VK_STENCIL_OP_KEEP;
case D3DSTENCILOP_ZERO: return VK_STENCIL_OP_ZERO;
case D3DSTENCILOP_REPLACE: return VK_STENCIL_OP_REPLACE;
case D3DSTENCILOP_INCRSAT: return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
case D3DSTENCILOP_DECRSAT: return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
case D3DSTENCILOP_INVERT: return VK_STENCIL_OP_INVERT;
case D3DSTENCILOP_INCR: return VK_STENCIL_OP_INCREMENT_AND_WRAP;
case D3DSTENCILOP_DECR: return VK_STENCIL_OP_DECREMENT_AND_WRAP;
}
}
VkCullModeFlags DecodeCullMode(D3DCULL Mode) {
switch (Mode) {
case D3DCULL_NONE: return VK_CULL_MODE_NONE;
case D3DCULL_CW: return VK_CULL_MODE_FRONT_BIT;
default:
case D3DCULL_CCW: return VK_CULL_MODE_BACK_BIT;
}
}
VkPolygonMode DecodeFillMode(D3DFILLMODE Mode) {
switch (Mode) {
case D3DFILL_POINT: return VK_POLYGON_MODE_POINT;
case D3DFILL_WIREFRAME: return VK_POLYGON_MODE_LINE;
default:
case D3DFILL_SOLID: return VK_POLYGON_MODE_FILL;
}
}
VkIndexType DecodeIndexType(D3D9Format Format) {
return Format == D3D9Format::INDEX16
? VK_INDEX_TYPE_UINT16
: VK_INDEX_TYPE_UINT32;
}
VkFormat DecodeDecltype(D3DDECLTYPE Type) {
switch (Type) {
case D3DDECLTYPE_FLOAT1: return VK_FORMAT_R32_SFLOAT;
case D3DDECLTYPE_FLOAT2: return VK_FORMAT_R32G32_SFLOAT;
case D3DDECLTYPE_FLOAT3: return VK_FORMAT_R32G32B32_SFLOAT;
case D3DDECLTYPE_FLOAT4: return VK_FORMAT_R32G32B32A32_SFLOAT;
case D3DDECLTYPE_D3DCOLOR: return VK_FORMAT_B8G8R8A8_UNORM;
case D3DDECLTYPE_UBYTE4: return VK_FORMAT_R8G8B8A8_USCALED;
case D3DDECLTYPE_SHORT2: return VK_FORMAT_R16G16_SSCALED;
case D3DDECLTYPE_SHORT4: return VK_FORMAT_R16G16B16A16_SSCALED;
case D3DDECLTYPE_UBYTE4N: return VK_FORMAT_R8G8B8A8_UNORM;
case D3DDECLTYPE_SHORT2N: return VK_FORMAT_R16G16_SNORM;
case D3DDECLTYPE_SHORT4N: return VK_FORMAT_R16G16B16A16_SNORM;
case D3DDECLTYPE_USHORT2N: return VK_FORMAT_R16G16_UNORM;
case D3DDECLTYPE_USHORT4N: return VK_FORMAT_R16G16B16A16_UNORM;
case D3DDECLTYPE_UDEC3: return VK_FORMAT_A2B10G10R10_USCALED_PACK32;
case D3DDECLTYPE_FLOAT16_2: return VK_FORMAT_R16G16_SFLOAT;
case D3DDECLTYPE_FLOAT16_4: return VK_FORMAT_R16G16B16A16_SFLOAT;
case D3DDECLTYPE_DEC3N: return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
case D3DDECLTYPE_UNUSED:
default: return VK_FORMAT_UNDEFINED;
}
}
void ConvertBox(D3DBOX box, VkOffset3D& offset, VkExtent3D& extent) {
offset.x = box.Left;
offset.y = box.Top;
offset.z = box.Front;
extent.width = box.Right - box.Left;
extent.height = box.Bottom - box.Top;
extent.depth = box.Back - box.Front;
}
void ConvertRect(RECT rect, VkOffset3D& offset, VkExtent3D& extent) {
offset.x = rect.left;
offset.y = rect.top;
offset.z = 0;
extent.width = rect.right - rect.left;
extent.height = rect.bottom - rect.top;
extent.depth = 1;
}
void ConvertRect(RECT rect, VkOffset2D& offset, VkExtent2D& extent) {
offset.x = rect.left;
offset.y = rect.top;
extent.width = rect.right - rect.left;
extent.height = rect.bottom - rect.top;
}
uint32_t GetDecltypeSize(D3DDECLTYPE Type) {
switch (Type) {
case D3DDECLTYPE_FLOAT1: return 1 * sizeof(float);
case D3DDECLTYPE_FLOAT2: return 2 * sizeof(float);
case D3DDECLTYPE_FLOAT3: return 3 * sizeof(float);
case D3DDECLTYPE_FLOAT4: return 4 * sizeof(float);
case D3DDECLTYPE_D3DCOLOR: return 1 * sizeof(DWORD);
case D3DDECLTYPE_UBYTE4: return 4 * sizeof(BYTE);
case D3DDECLTYPE_SHORT2: return 2 * sizeof(short);
case D3DDECLTYPE_SHORT4: return 4 * sizeof(short);
case D3DDECLTYPE_UBYTE4N: return 4 * sizeof(BYTE);
case D3DDECLTYPE_SHORT2N: return 2 * sizeof(short);
case D3DDECLTYPE_SHORT4N: return 4 * sizeof(short);
case D3DDECLTYPE_USHORT2N: return 2 * sizeof(short);
case D3DDECLTYPE_USHORT4N: return 4 * sizeof(short);
case D3DDECLTYPE_UDEC3: return 4;
case D3DDECLTYPE_DEC3N: return 4;
case D3DDECLTYPE_FLOAT16_2: return 2 * 2;
case D3DDECLTYPE_FLOAT16_4: return 4 * 2;
default: return 0;
}
}
uint32_t GetDecltypeCount(D3DDECLTYPE Type) {
switch (Type) {
case D3DDECLTYPE_FLOAT1: return 1;
case D3DDECLTYPE_FLOAT2: return 2;
case D3DDECLTYPE_FLOAT3: return 3;
case D3DDECLTYPE_FLOAT4: return 4;
case D3DDECLTYPE_D3DCOLOR: return 4;
case D3DDECLTYPE_UBYTE4: return 4;
case D3DDECLTYPE_SHORT2: return 2;
case D3DDECLTYPE_SHORT4: return 4;
case D3DDECLTYPE_UBYTE4N: return 4;
case D3DDECLTYPE_SHORT2N: return 2;
case D3DDECLTYPE_SHORT4N: return 4;
case D3DDECLTYPE_USHORT2N: return 2;
case D3DDECLTYPE_USHORT4N: return 4;
case D3DDECLTYPE_UDEC3: return 3;
case D3DDECLTYPE_DEC3N: return 3;
case D3DDECLTYPE_FLOAT16_2: return 2;
case D3DDECLTYPE_FLOAT16_4: return 4;
default: return 0;
}
}
bool IsDepthFormat(D3D9Format Format) {
return Format == D3D9Format::D16_LOCKABLE
|| Format == D3D9Format::D32
|| Format == D3D9Format::D15S1
|| Format == D3D9Format::D24S8
|| Format == D3D9Format::D24X8
|| Format == D3D9Format::D24X4S4
|| Format == D3D9Format::D16
|| Format == D3D9Format::D32F_LOCKABLE
|| Format == D3D9Format::D24FS8
|| Format == D3D9Format::D32_LOCKABLE
|| Format == D3D9Format::DF16
|| Format == D3D9Format::DF24
|| Format == D3D9Format::INTZ;
}
}

202
src/d3d9/d3d9_util.h Normal file
View file

@ -0,0 +1,202 @@
#pragma once
#include "d3d9_include.h"
#include "d3d9_format.h"
#include "../dxso/dxso_common.h"
#include "../dxvk/dxvk_device.h"
#include "../util/util_matrix.h"
#include <d3dcommon.h>
namespace dxvk {
struct D3D9ShaderMasks {
uint32_t samplerMask;
uint32_t rtMask;
};
struct D3D9MipFilter {
bool MipsEnabled;
VkSamplerMipmapMode MipFilter;
};
struct D3D9BlendState {
D3DBLEND Src;
D3DBLEND Dst;
D3DBLENDOP Op;
};
inline void FixupBlendState(D3D9BlendState& State) {
// Old DirectX 6 HW feature that still exists...
// Yuck!
if (unlikely(State.Src == D3DBLEND_BOTHSRCALPHA)) {
State.Src = D3DBLEND_SRCALPHA;
State.Dst = D3DBLEND_INVSRCALPHA;
}
else if (unlikely(State.Src == D3DBLEND_BOTHINVSRCALPHA)) {
State.Src = D3DBLEND_INVSRCALPHA;
State.Dst = D3DBLEND_SRCALPHA;
}
}
inline bool InvalidSampler(DWORD Sampler) {
if (Sampler > 15 && Sampler < D3DDMAPSAMPLER)
return true;
if (Sampler > D3DVERTEXTEXTURESAMPLER3)
return true;
return false;
}
inline DWORD RemapSamplerState(DWORD Sampler) {
if (Sampler >= D3DDMAPSAMPLER)
Sampler = 16 + (Sampler - D3DDMAPSAMPLER);
return Sampler;
}
inline std::pair<DxsoProgramType, DWORD> RemapStateSamplerShader(DWORD Sampler) {
if (Sampler >= 17)
return std::make_pair(DxsoProgramTypes::VertexShader, Sampler - 17);
return std::make_pair(DxsoProgramTypes::PixelShader, Sampler);
}
inline std::pair<DxsoProgramType, DWORD> RemapSamplerShader(DWORD Sampler) {
Sampler = RemapSamplerState(Sampler);
return RemapStateSamplerShader(Sampler);
}
template <typename T, typename J>
void CastRefPrivate(J* ptr, bool AddRef) {
if (ptr == nullptr)
return;
T* castedPtr = reinterpret_cast<T*>(ptr);
AddRef ? castedPtr->AddRefPrivate() : castedPtr->ReleasePrivate();
}
HRESULT DisassembleShader(
const void* pShader,
BOOL EnableColorCode,
char* pComments,
ID3DBlob** ppDisassembly);
HRESULT DecodeMultiSampleType(
D3DMULTISAMPLE_TYPE MultiSample,
DWORD MultisampleQuality,
VkSampleCountFlagBits* pCount);
VkFormat GetPackedDepthStencilFormat(D3D9Format Format);
VkFormatFeatureFlags GetImageFormatFeatures(DWORD Usage);
VkImageUsageFlags GetImageUsageFlags(DWORD Usage);
inline void DecodeD3DCOLOR(D3DCOLOR color, float* rgba) {
// Encoded in D3DCOLOR as argb
rgba[3] = (float)((color & 0xff000000) >> 24) / 255.0f;
rgba[0] = (float)((color & 0x00ff0000) >> 16) / 255.0f;
rgba[1] = (float)((color & 0x0000ff00) >> 8) / 255.0f;
rgba[2] = (float)((color & 0x000000ff)) / 255.0f;
}
inline VkFormat PickSRGB(VkFormat format, VkFormat srgbFormat, bool srgb) {
return srgb ? srgbFormat : format;
}
inline VkShaderStageFlagBits GetShaderStage(DxsoProgramType ProgramType) {
switch (ProgramType) {
case DxsoProgramTypes::VertexShader: return VK_SHADER_STAGE_VERTEX_BIT;
case DxsoProgramTypes::PixelShader: return VK_SHADER_STAGE_FRAGMENT_BIT;
default: return VkShaderStageFlagBits(0);
}
}
inline uint32_t GetTransformIndex(D3DTRANSFORMSTATETYPE Type) {
if (Type == D3DTS_VIEW)
return 0;
if (Type == D3DTS_PROJECTION)
return 1;
if (Type >= D3DTS_TEXTURE0 && Type <= D3DTS_TEXTURE7)
return 2 + (Type - D3DTS_TEXTURE0);
return 10 + (Type - D3DTS_WORLD);
}
inline Matrix4 ConvertMatrix(const D3DMATRIX* Matrix) {
if (Matrix == nullptr) // Identity.
return Matrix4();
return *(reinterpret_cast<const Matrix4*>(Matrix));
}
uint32_t GetVertexCount(D3DPRIMITIVETYPE type, UINT count);
DxvkInputAssemblyState DecodeInputAssemblyState(D3DPRIMITIVETYPE type);
VkBlendFactor DecodeBlendFactor(D3DBLEND BlendFactor, bool IsAlpha);
VkBlendOp DecodeBlendOp(D3DBLENDOP BlendOp);
VkFilter DecodeFilter(D3DTEXTUREFILTERTYPE Filter);
D3D9MipFilter DecodeMipFilter(D3DTEXTUREFILTERTYPE Filter);
bool IsAnisotropic(D3DTEXTUREFILTERTYPE Filter);
VkSamplerAddressMode DecodeAddressMode(D3DTEXTUREADDRESS Mode);
VkCompareOp DecodeCompareOp(D3DCMPFUNC Func);
VkStencilOp DecodeStencilOp(D3DSTENCILOP Op);
VkCullModeFlags DecodeCullMode(D3DCULL Mode);
VkPolygonMode DecodeFillMode(D3DFILLMODE Mode);
VkIndexType DecodeIndexType(D3D9Format Format);
VkFormat DecodeDecltype(D3DDECLTYPE Type);
uint32_t GetDecltypeSize(D3DDECLTYPE Type);
uint32_t GetDecltypeCount(D3DDECLTYPE Type);
void ConvertBox(D3DBOX box, VkOffset3D& offset, VkExtent3D& extent);
void ConvertRect(RECT rect, VkOffset3D& offset, VkExtent3D& extent);
void ConvertRect(RECT rect, VkOffset2D& offset, VkExtent2D& extent);
template<typename T>
UINT CompactSparseList(T* pData, UINT Mask) {
uint32_t count = 0;
while (Mask != 0) {
uint32_t id = bit::tzcnt(Mask);
pData[count++] = pData[id];
Mask &= Mask - 1;
}
return count;
}
bool IsDepthFormat(D3D9Format Format);
inline bool IsPoolManaged(D3DPOOL Pool) {
return Pool == D3DPOOL_MANAGED || Pool == D3DPOOL_MANAGED_EX;
}
inline D3DRENDERSTATETYPE ColorWriteIndex(uint32_t i) {
return D3DRENDERSTATETYPE(i ? D3DRS_COLORWRITEENABLE1 + i - 1 : D3DRS_COLORWRITEENABLE);
}
}

View file

@ -0,0 +1,231 @@
#include "d3d9_vertex_declaration.h"
#include "d3d9_util.h"
#include <algorithm>
#include <cstring>
namespace dxvk {
D3D9VertexDecl::D3D9VertexDecl(
D3D9DeviceEx* pDevice,
DWORD FVF)
: D3D9VertexDeclBase(pDevice) {
this->SetFVF(FVF);
this->Classify();
}
D3D9VertexDecl::D3D9VertexDecl(
D3D9DeviceEx* pDevice,
const D3DVERTEXELEMENT9* pVertexElements,
uint32_t DeclCount)
: D3D9VertexDeclBase( pDevice )
, m_elements ( DeclCount )
, m_fvf ( 0 ) {
std::copy(pVertexElements, pVertexElements + DeclCount, m_elements.begin());
this->Classify();
}
HRESULT STDMETHODCALLTYPE D3D9VertexDecl::QueryInterface(
REFIID riid,
void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3DVertexDeclaration9)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9VertexDecl::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE D3D9VertexDecl::GetDeclaration(
D3DVERTEXELEMENT9* pElement,
UINT* pNumElements) {
if (pNumElements == nullptr)
return D3DERR_INVALIDCALL;
*pNumElements = UINT(m_elements.size()) + 1u; // Account for D3DDECL_END
if (pElement == nullptr)
return D3D_OK;
// The native runtime ignores pNumElements here...
std::copy(m_elements.begin(), m_elements.end(), pElement);
pElement[m_elements.size()] = D3DDECL_END();
return D3D_OK;
}
void D3D9VertexDecl::SetFVF(DWORD FVF) {
m_fvf = FVF;
std::array<D3DVERTEXELEMENT9, 16> elements;
uint32_t elemCount = 0;
uint32_t texCount = 0;
uint32_t betas = 0;
uint8_t betaIdx = 0xFF;
switch (FVF & D3DFVF_POSITION_MASK) {
case D3DFVF_XYZ:
case D3DFVF_XYZB1:
case D3DFVF_XYZB2:
case D3DFVF_XYZB3:
case D3DFVF_XYZB4:
case D3DFVF_XYZB5:
elements[elemCount].Type = D3DDECLTYPE_FLOAT3;
elements[elemCount].Usage = D3DDECLUSAGE_POSITION;
elements[elemCount].UsageIndex = 0;
elemCount++;
if ((FVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZ)
break;
betas = (((FVF & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1) + 1;
if (FVF & D3DFVF_LASTBETA_D3DCOLOR)
betaIdx = D3DDECLTYPE_D3DCOLOR;
else if (FVF & D3DFVF_LASTBETA_UBYTE4)
betaIdx = D3DDECLTYPE_UBYTE4;
else if ((FVF & D3DFVF_XYZB5) == D3DFVF_XYZB5)
betaIdx = D3DDECLTYPE_FLOAT1;
if (betaIdx != 0xFF)
betas--;
if (betas > 0) {
switch (betas) {
case 1: elements[elemCount].Type = D3DDECLTYPE_FLOAT1; break;
case 2: elements[elemCount].Type = D3DDECLTYPE_FLOAT2; break;
case 3: elements[elemCount].Type = D3DDECLTYPE_FLOAT3; break;
case 4: elements[elemCount].Type = D3DDECLTYPE_FLOAT4; break;
default: break;
}
elements[elemCount].Usage = D3DDECLUSAGE_BLENDWEIGHT;
elements[elemCount].UsageIndex = 0;
elemCount++;
}
if (betaIdx != 0xFF) {
elements[elemCount].Type = betaIdx;
elements[elemCount].Usage = D3DDECLUSAGE_BLENDINDICES;
elements[elemCount].UsageIndex = 0;
elemCount++;
}
break;
case D3DFVF_XYZW:
case D3DFVF_XYZRHW:
elements[elemCount].Type = D3DDECLTYPE_FLOAT4;
elements[elemCount].Usage =
((FVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZW)
? D3DDECLUSAGE_POSITION
: D3DDECLUSAGE_POSITIONT;
elements[elemCount].UsageIndex = 0;
elemCount++;
break;
default:
break;
}
if (FVF & D3DFVF_NORMAL) {
elements[elemCount].Type = D3DDECLTYPE_FLOAT3;
elements[elemCount].Usage = D3DDECLUSAGE_NORMAL;
elements[elemCount].UsageIndex = 0;
elemCount++;
}
if (FVF & D3DFVF_PSIZE) {
elements[elemCount].Type = D3DDECLTYPE_FLOAT1;
elements[elemCount].Usage = D3DDECLUSAGE_PSIZE;
elements[elemCount].UsageIndex = 0;
elemCount++;
}
if (FVF & D3DFVF_DIFFUSE) {
elements[elemCount].Type = D3DDECLTYPE_D3DCOLOR;
elements[elemCount].Usage = D3DDECLUSAGE_COLOR;
elements[elemCount].UsageIndex = 0;
elemCount++;
}
if (FVF & D3DFVF_SPECULAR) {
elements[elemCount].Type = D3DDECLTYPE_D3DCOLOR;
elements[elemCount].Usage = D3DDECLUSAGE_COLOR;
elements[elemCount].UsageIndex = 1;
elemCount++;
}
texCount = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
texCount = std::min(texCount, 8u);
for (uint32_t i = 0; i < texCount; i++) {
switch ((FVF >> (16 + i * 2)) & 0x3) {
case D3DFVF_TEXTUREFORMAT1:
elements[elemCount].Type = D3DDECLTYPE_FLOAT1;
break;
case D3DFVF_TEXTUREFORMAT2:
elements[elemCount].Type = D3DDECLTYPE_FLOAT2;
break;
case D3DFVF_TEXTUREFORMAT3:
elements[elemCount].Type = D3DDECLTYPE_FLOAT3;
break;
case D3DFVF_TEXTUREFORMAT4:
elements[elemCount].Type = D3DDECLTYPE_FLOAT4;
break;
default:
break;
}
elements[elemCount].Usage = D3DDECLUSAGE_TEXCOORD;
elements[elemCount].UsageIndex = i;
elemCount++;
}
for (uint32_t i = 0; i < elemCount; i++) {
elements[i].Stream = 0;
elements[i].Offset = (i == 0)
? 0
: (elements[i - 1].Offset + GetDecltypeSize(D3DDECLTYPE(elements[i - 1].Type)));
elements[i].Method = D3DDECLMETHOD_DEFAULT;
}
m_elements.resize(elemCount);
std::copy(elements.begin(), elements.begin() + elemCount, m_elements.data());
}
void D3D9VertexDecl::Classify() {
for (const auto& element : m_elements) {
if (element.Usage == D3DDECLUSAGE_COLOR && element.UsageIndex == 0)
m_flags.set(D3D9VertexDeclFlag::HasColor0);
else if (element.Usage == D3DDECLUSAGE_COLOR && element.UsageIndex == 1)
m_flags.set(D3D9VertexDeclFlag::HasColor1);
else if (element.Usage == D3DDECLUSAGE_POSITIONT)
m_flags.set(D3D9VertexDeclFlag::HasPositionT);
else if (element.Usage == D3DDECLUSAGE_PSIZE)
m_flags.set(D3D9VertexDeclFlag::HasPointSize);
else if (element.Usage == D3DDECLUSAGE_FOG)
m_flags.set(D3D9VertexDeclFlag::HasFog);
else if (element.Usage == D3DDECLUSAGE_BLENDWEIGHT)
m_flags.set(D3D9VertexDeclFlag::HasBlendWeight);
else if (element.Usage == D3DDECLUSAGE_BLENDINDICES)
m_flags.set(D3D9VertexDeclFlag::HasBlendIndices);
if (element.Usage == D3DDECLUSAGE_TEXCOORD)
m_texcoordMask |= GetDecltypeCount(D3DDECLTYPE(element.Type)) << (element.UsageIndex * 3);
}
}
}

View file

@ -0,0 +1,83 @@
#pragma once
#include "d3d9_device_child.h"
#include "d3d9_util.h"
#include <vector>
namespace dxvk {
enum D3D9VertexDeclFlag {
HasColor0,
HasColor1,
HasPositionT,
HasPointSize,
HasFog,
HasBlendWeight,
HasBlendIndices
};
using D3D9VertexDeclFlags = Flags<D3D9VertexDeclFlag>;
using D3D9VertexDeclBase = D3D9DeviceChild<IDirect3DVertexDeclaration9>;
class D3D9VertexDecl final : public D3D9VertexDeclBase {
public:
D3D9VertexDecl(
D3D9DeviceEx* pDevice,
DWORD FVF);
D3D9VertexDecl(
D3D9DeviceEx* pDevice,
const D3DVERTEXELEMENT9* pVertexElements,
uint32_t DeclCount);
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid,
void** ppvObject);
HRESULT STDMETHODCALLTYPE GetDeclaration(
D3DVERTEXELEMENT9* pElement,
UINT* pNumElements);
inline DWORD GetFVF() {
return m_fvf;
}
void SetFVF(DWORD FVF);
const D3D9VertexElements& GetElements() const {
return m_elements;
}
UINT GetSize() const {
if (m_elements.size() == 0)
return 0;
auto& end = m_elements.back();
return end.Offset + GetDecltypeSize(D3DDECLTYPE(end.Type));
}
bool TestFlag(D3D9VertexDeclFlag flag) const {
return m_flags.test(flag);
}
uint32_t GetTexcoordMask() const {
return m_texcoordMask;
}
private:
void Classify();
D3D9VertexDeclFlags m_flags;
D3D9VertexElements m_elements;
DWORD m_fvf;
uint32_t m_texcoordMask = 0;
};
}

110
src/d3d9/d3d9_volume.cpp Normal file
View file

@ -0,0 +1,110 @@
#include "d3d9_volume.h"
#include "d3d9_device.h"
#include "d3d9_texture.h"
namespace dxvk {
D3D9Volume::D3D9Volume(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING Mapping)
: D3D9VolumeBase(
pDevice,
new D3D9CommonTexture( pDevice, pDesc, D3DRTYPE_VOLUMETEXTURE, Mapping ),
0, 0,
nullptr) { }
D3D9Volume::D3D9Volume(
D3D9DeviceEx* pDevice,
D3D9CommonTexture* pTexture,
UINT Face,
UINT MipLevel,
IDirect3DBaseTexture9* pContainer)
: D3D9VolumeBase(
pDevice,
pTexture,
Face, MipLevel,
pContainer) { }
void D3D9Volume::AddRefPrivate() {
IDirect3DBaseTexture9* pContainer = this->m_container;
if (pContainer != nullptr) {
reinterpret_cast<D3D9Texture3D*> (pContainer)->AddRefPrivate();
return;
}
D3D9VolumeBase::AddRefPrivate();
}
void D3D9Volume::ReleasePrivate() {
IDirect3DBaseTexture9* pContainer = this->m_container;
if (pContainer != nullptr) {
reinterpret_cast<D3D9Texture3D*> (pContainer)->ReleasePrivate();
return;
}
D3D9VolumeBase::ReleasePrivate();
}
HRESULT STDMETHODCALLTYPE D3D9Volume::QueryInterface(REFIID riid, void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;
*ppvObject = nullptr;
if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3DResource9)
|| riid == __uuidof(IDirect3DVolume9)) {
*ppvObject = ref(this);
return S_OK;
}
Logger::warn("D3D9Volume::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE D3D9Volume::GetDesc(D3DVOLUME_DESC *pDesc) {
if (pDesc == nullptr)
return D3DERR_INVALIDCALL;
auto& desc = *(m_texture->Desc());
pDesc->Format = static_cast<D3DFORMAT>(desc.Format);
pDesc->Type = D3DRTYPE_VOLUME;
pDesc->Usage = desc.Usage;
pDesc->Pool = desc.Pool;
pDesc->Width = std::max(1u, desc.Width >> m_mipLevel);
pDesc->Height = std::max(1u, desc.Height >> m_mipLevel);
pDesc->Depth = std::max(1u, desc.Depth >> m_mipLevel);
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE D3D9Volume::LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) {
return m_parent->LockImage(
m_texture,
m_face, m_mipLevel,
pLockedBox,
pBox,
Flags);
}
HRESULT STDMETHODCALLTYPE D3D9Volume::UnlockBox() {
return m_parent->UnlockImage(
m_texture,
m_face, m_mipLevel);
}
}

39
src/d3d9/d3d9_volume.h Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include "d3d9_subresource.h"
#include "d3d9_common_texture.h"
namespace dxvk {
using D3D9VolumeBase = D3D9Subresource<IDirect3DVolume9>;
class D3D9Volume final : public D3D9VolumeBase {
public:
D3D9Volume(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3D9_VK_FORMAT_MAPPING Mapping);
D3D9Volume(
D3D9DeviceEx* pDevice,
D3D9CommonTexture* pTexture,
UINT Face,
UINT MipLevel,
IDirect3DBaseTexture9* pContainer);
void AddRefPrivate();
void ReleasePrivate();
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
HRESULT STDMETHODCALLTYPE GetDesc(D3DVOLUME_DESC *pDesc) final;
HRESULT STDMETHODCALLTYPE LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) final;
HRESULT STDMETHODCALLTYPE UnlockBox() final;
};
}

52
src/d3d9/meson.build Normal file
View file

@ -0,0 +1,52 @@
d3d9_res = wrc_generator.process('version.rc')
d3d9_shaders = files([
'shaders/d3d9_presenter_frag.frag',
'shaders/d3d9_presenter_vert.vert',
'shaders/d3d9_convert_yuy2_uyvy.comp'
])
d3d9_src = [
'd3d9_main.cpp',
'd3d9_interface.cpp',
'd3d9_adapter.cpp',
'd3d9_monitor.cpp',
'd3d9_device.cpp',
'd3d9_state.cpp',
'd3d9_cursor.cpp',
'd3d9_swapchain.cpp',
'd3d9_format.cpp',
'd3d9_common_texture.cpp',
'd3d9_texture.cpp',
'd3d9_surface.cpp',
'd3d9_volume.cpp',
'd3d9_common_buffer.cpp',
'd3d9_buffer.cpp',
'd3d9_shader.cpp',
'd3d9_vertex_declaration.cpp',
'd3d9_query.cpp',
'd3d9_multithread.cpp',
'd3d9_options.cpp',
'd3d9_stateblock.cpp',
'd3d9_sampler.cpp',
'd3d9_util.cpp',
'd3d9_initializer.cpp',
'd3d9_fixed_function.cpp',
'd3d9_names.cpp',
'd3d9_swvp_emu.cpp',
'd3d9_format_helpers.cpp',
'd3d9_hud.cpp'
]
d3d9_dll = shared_library('d3d9'+dll_ext, d3d9_src, glsl_generator.process(d3d9_shaders), d3d9_res,
name_prefix : '',
dependencies : [ dxso_dep, dxvk_dep ],
include_directories : dxvk_include_path,
install : true,
objects : not dxvk_msvc ? 'd3d9'+def_spec_ext : [],
vs_module_defs : 'd3d9'+def_spec_ext,
override_options : ['cpp_std='+dxvk_cpp_std])
d3d9_dep = declare_dependency(
link_with : [ d3d9_dll ],
include_directories : [ dxvk_include_path ])

View file

@ -0,0 +1,63 @@
#version 450
layout(constant_id = 1249) const bool s_is_uyvy = false;
layout(
local_size_x = 8,
local_size_y = 8,
local_size_z = 1) in;
layout(binding = 0)
writeonly uniform image2D dst;
layout(binding = 1)
readonly buffer yuy2_buffer_t {
uint data[];
} src;
layout(push_constant)
uniform u_info_t {
uvec2 extent;
} u_info;
mat3x4 g_yuv_to_rgb = {
{ 298 / 256, 0, 409 / 256, 0.5 },
{ 298 / 256, -100 / 256, -208 / 256, 0.5 },
{ 298 / 256, 516 / 256, 0, 0.5 }
};
vec4 convertYUV(vec3 cde) {
vec3 value = vec4(cde, 1 / 255.0) * g_yuv_to_rgb;
return vec4(clamp(value, 0, 1), 1);
}
void main() {
ivec3 thread_id = ivec3(gl_GlobalInvocationID);
if (all(lessThan(thread_id.xy, u_info.extent))) {
uint offset = thread_id.x
+ thread_id.y * u_info.extent.x;
vec4 data = unpackUnorm4x8(src.data[offset]);
// Flip around stuff for UYVY
if (s_is_uyvy)
data = data.yxwz;
float c0 = data.x - (16 / 255.0);
float d = data.y - (128 / 255.0);
float c1 = data.z - (16 / 255.0);
float e = data.w - (128 / 255.0);
vec4 color0 = convertYUV(vec3(c0, d, e));
vec4 color1 = convertYUV(vec3(c1, d, e));
// YUY2 has a macropixel of [2, 1]
// so we write 2 pixels in this run.
ivec2 writePos = thread_id.xy * ivec2(2, 1);
imageStore(dst, ivec2(writePos.x, writePos.y), color0);
imageStore(dst, ivec2(writePos.x + 1, writePos.y), color1);
}
}

View file

@ -0,0 +1,21 @@
#version 450
layout(constant_id = 1) const bool s_gamma_bound = true;
layout(binding = 0) uniform sampler2D s_image;
layout(binding = 1) uniform sampler1D s_gamma;
layout(location = 0) in vec2 i_texcoord;
layout(location = 0) out vec4 o_color;
void main() {
o_color = texture(s_image, i_texcoord);
if (s_gamma_bound) {
o_color = vec4(
texture(s_gamma, o_color.r).r,
texture(s_gamma, o_color.g).g,
texture(s_gamma, o_color.b).b,
o_color.a);
}
}

View file

@ -0,0 +1,21 @@
#version 450
layout(location = 0) out vec2 o_texcoord;
layout(push_constant) uniform present_info_t {
vec2 scale;
vec2 offset;
} u_presentInfo;
void main() {
vec2 coord = vec2(
float(gl_VertexIndex & 2),
float(gl_VertexIndex & 1) * 2.0f);
gl_Position = vec4(-1.0f + 2.0f * coord, 0.0f, 1.0f);
coord *= u_presentInfo.scale;
coord += u_presentInfo.offset;
o_texcoord = coord;
}

31
src/d3d9/version.rc Normal file
View file

@ -0,0 +1,31 @@
#include <windows.h>
// DLL version information.
VS_VERSION_INFO VERSIONINFO
FILEVERSION 10,0,17763,1
PRODUCTVERSION 10,0,17763,1
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080904b0"
BEGIN
VALUE "CompanyName", "DXVK"
VALUE "FileDescription", "Direct3D 9 Runtime"
VALUE "FileVersion", "10.0.17763.1 (WinBuild.160101.0800)"
VALUE "InternalName", "D3D9.dll"
VALUE "LegalCopyright", "zlib/libpng license"
VALUE "OriginalFilename", "D3D9.dll"
VALUE "ProductName", "DXVK"
VALUE "ProductVersion", "10.0.17763.1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0809, 1200
END
END

View file

@ -0,0 +1,47 @@
#include "dxso_analysis.h"
namespace dxvk {
DxsoAnalyzer::DxsoAnalyzer(
DxsoAnalysisInfo& analysis)
: m_analysis(&analysis) { }
void DxsoAnalyzer::processInstruction(
const DxsoInstructionContext& ctx) {
DxsoOpcode opcode = ctx.instruction.opcode;
if (opcode == DxsoOpcode::TexKill)
m_analysis->usesKill = true;
if (opcode == DxsoOpcode::DsX
|| opcode == DxsoOpcode::DsY
|| opcode == DxsoOpcode::Tex
|| opcode == DxsoOpcode::TexCoord
|| opcode == DxsoOpcode::TexBem
|| opcode == DxsoOpcode::TexBemL
|| opcode == DxsoOpcode::TexReg2Ar
|| opcode == DxsoOpcode::TexReg2Gb
|| opcode == DxsoOpcode::TexM3x2Pad
|| opcode == DxsoOpcode::TexM3x2Tex
|| opcode == DxsoOpcode::TexM3x3Pad
|| opcode == DxsoOpcode::TexM3x3Tex
|| opcode == DxsoOpcode::TexM3x3Spec
|| opcode == DxsoOpcode::TexM3x3VSpec
|| opcode == DxsoOpcode::TexReg2Rgb
|| opcode == DxsoOpcode::TexDp3Tex
|| opcode == DxsoOpcode::TexM3x2Depth
|| opcode == DxsoOpcode::TexDp3
|| opcode == DxsoOpcode::TexM3x3
// Explicit LOD.
//|| opcode == DxsoOpcode::TexLdd
//|| opcode == DxsoOpcode::TexLdl
|| opcode == DxsoOpcode::TexDepth)
m_analysis->usesDerivatives = true;
}
void DxsoAnalyzer::finalize(size_t tokenCount) {
m_analysis->bytecodeByteLength = tokenCount * sizeof(uint32_t);
}
}

37
src/dxso/dxso_analysis.h Normal file
View file

@ -0,0 +1,37 @@
#pragma once
#include "dxso_modinfo.h"
#include "dxso_decoder.h"
namespace dxvk {
struct DxsoAnalysisInfo {
uint32_t bytecodeByteLength;
bool usesDerivatives = false;
bool usesKill = false;
};
class DxsoAnalyzer {
public:
DxsoAnalyzer(
DxsoAnalysisInfo& analysis);
/**
* \brief Processes a single instruction
* \param [in] ins The instruction
*/
void processInstruction(
const DxsoInstructionContext& ctx);
void finalize(size_t tokenCount);
private:
DxsoAnalysisInfo* m_analysis = nullptr;
};
}

28
src/dxso/dxso_code.cpp Normal file
View file

@ -0,0 +1,28 @@
#include "dxso_code.h"
namespace dxvk {
DxsoCode::DxsoCode(DxsoReader& reader) {
m_code =
reinterpret_cast<const uint32_t*>(reader.currentPtr());
}
const uint32_t* DxsoCodeIter::ptrAt(uint32_t id) const {
return m_ptr + id;
}
uint32_t DxsoCodeIter::at(uint32_t id) const {
return m_ptr[id];
}
uint32_t DxsoCodeIter::read() {
return *(m_ptr++);
}
DxsoCodeIter DxsoCodeIter::skip(uint32_t n) const {
return DxsoCodeIter(m_ptr + n);
}
}

54
src/dxso/dxso_code.h Normal file
View file

@ -0,0 +1,54 @@
#pragma once
#include "dxso_include.h"
#include "dxso_reader.h"
#include <vector>
#include <cstdint>
namespace dxvk {
/**
* \brief DXBC code iterator
*
* Convenient pointer wrapper that allows
* reading the code token stream.
*/
class DxsoCodeIter {
public:
DxsoCodeIter(
const uint32_t* ptr)
: m_ptr(ptr) { }
const uint32_t* ptrAt(uint32_t id) const;
uint32_t at(uint32_t id) const;
uint32_t read();
DxsoCodeIter skip(uint32_t n) const;
private:
const uint32_t* m_ptr = nullptr;
};
class DxsoCode {
public:
DxsoCode(DxsoReader& reader);
DxsoCodeIter iter() const {
return DxsoCodeIter(m_code);
}
private:
const uint32_t* m_code;
};
}

26
src/dxso/dxso_common.cpp Normal file
View file

@ -0,0 +1,26 @@
#include "dxso_common.h"
namespace dxvk {
VkShaderStageFlagBits DxsoProgramInfo::shaderStage() const {
switch (m_type) {
case DxsoProgramTypes::PixelShader: return VK_SHADER_STAGE_FRAGMENT_BIT;
case DxsoProgramTypes::VertexShader: return VK_SHADER_STAGE_VERTEX_BIT;
default: break;
}
throw DxvkError("DxsoProgramInfo::shaderStage: Unsupported program type");
}
spv::ExecutionModel DxsoProgramInfo::executionModel() const {
switch (m_type) {
case DxsoProgramTypes::PixelShader: return spv::ExecutionModelFragment;
case DxsoProgramTypes::VertexShader: return spv::ExecutionModelVertex;
default: break;
}
throw DxvkError("DxsoProgramInfo::executionModel: Unsupported program type");
}
}

88
src/dxso/dxso_common.h Normal file
View file

@ -0,0 +1,88 @@
#pragma once
#include "dxso_include.h"
#include <cstdint>
namespace dxvk {
/**
* \brief DXSO Program type
*
* Defines the shader stage that a DXSO
* module has been compiled for.
*/
namespace DxsoProgramTypes {
enum DxsoProgramType : uint16_t {
VertexShader = 0,
PixelShader = 1,
Count = 2,
};
}
using DxsoProgramType = DxsoProgramTypes::DxsoProgramType;
class DxsoProgramInfo {
public:
DxsoProgramInfo() { }
DxsoProgramInfo(
DxsoProgramType type,
uint32_t minorVersion,
uint32_t majorVersion)
: m_type{ type }
, m_minorVersion{ minorVersion }
, m_majorVersion{ majorVersion } {}
/**
* \brief Program type
* \returns Program type
*/
DxsoProgramType type() const {
return m_type;
}
/**
* \brief Vulkan shader stage
*
* The \c VkShaderStageFlagBits constant
* that corresponds to the program type.
* \returns Vulkan shader stage
*/
VkShaderStageFlagBits shaderStage() const;
/**
* \brief SPIR-V execution model
*
* The execution model that corresponds
* to the Vulkan shader stage.
* \returns SPIR-V execution model
*/
spv::ExecutionModel executionModel() const;
/**
* \brief Minor version
* \returns The minor version of the shader model.
*/
uint32_t minorVersion() const {
return m_minorVersion;
}
/**
* \brief Major version
* \returns The major version of the shader model.
*/
uint32_t majorVersion() const {
return m_majorVersion;
}
private:
DxsoProgramType m_type;
uint32_t m_minorVersion;
uint32_t m_majorVersion;
};
}

3579
src/dxso/dxso_compiler.cpp Normal file

File diff suppressed because it is too large Load diff

674
src/dxso/dxso_compiler.h Normal file
View file

@ -0,0 +1,674 @@
#pragma once
#include "dxso_decoder.h"
#include "dxso_header.h"
#include "dxso_modinfo.h"
#include "dxso_isgn.h"
#include "../d3d9/d3d9_constant_layout.h"
#include "../d3d9/d3d9_shader_permutations.h"
#include "../spirv/spirv_module.h"
namespace dxvk {
/**
* \brief Scalar value type
*
* Enumerates possible register component
* types. Scalar types are represented as
* a one-component vector type.
*/
enum class DxsoScalarType : uint32_t {
Uint32 = 0,
Sint32 = 1,
Float32 = 2,
Bool = 3,
};
/**
* \brief Vector type
*
* Convenience struct that stores a scalar
* type and a component count. The compiler
* can use this to generate SPIR-V types.
*/
struct DxsoVectorType {
DxsoScalarType ctype;
uint32_t ccount;
};
/**
* \brief Array type
*
* Convenience struct that stores a scalar type, a
* component count and an array size. An array of
* length 0 will be evaluated to a vector type. The
* compiler can use this to generate SPIR-V types.
*/
struct DxsoArrayType {
DxsoScalarType ctype;
uint32_t ccount;
uint32_t alength;
};
/**
* \brief Register info
*
* Stores the array type of a register and
* its storage class. The compiler can use
* this to generate SPIR-V pointer types.
*/
struct DxsoRegisterInfo {
DxsoArrayType type;
spv::StorageClass sclass;
};
/**
* \brief Register value
*
* Stores a vector type and a SPIR-V ID that
* represents an intermediate value. This is
* used to track the type of such values.
*/
struct DxsoRegisterValue {
DxsoVectorType type;
uint32_t id;
};
/**
* \brief Register pointer
*
* Stores a vector type and a SPIR-V ID that
* represents a pointer to such a vector. This
* can be used to load registers conveniently.
*/
struct DxsoRegisterPointer {
DxsoVectorType type;
uint32_t id = 0;
};
/**
* \brief Sampler info
*
* Stores a vector type and a SPIR-V ID that
* represents a pointer to such a vector. This
* can be used to load registers conveniently.
*/
struct DxsoSamplerInfo {
uint32_t dimensions = 0;
uint32_t varId = 0;
uint32_t typeId = 0;
};
enum DxsoSamplerType : uint32_t {
SamplerTypeTexture2D = 0,
SamplerTypeTexture3D = 1,
SamplerTypeTextureCube,
SamplerTypeCount
};
inline auto SamplerTypeFromTextureType(DxsoTextureType type) {
switch (type) {
default:
case DxsoTextureType::Texture2D: return SamplerTypeTexture2D; break;
case DxsoTextureType::Texture3D: return SamplerTypeTexture3D; break;
case DxsoTextureType::TextureCube: return SamplerTypeTextureCube; break;
}
}
struct DxsoSampler {
DxsoSamplerInfo color[SamplerTypeCount];
DxsoSamplerInfo depth[SamplerTypeCount];
uint32_t depthSpecConst;
DxsoTextureType type;
};
struct DxsoAnalysisInfo;
/**
* \brief Vertex shader-specific structure
*/
struct DxsoCompilerVsPart {
uint32_t functionId = 0;
////////////////////
// Address register
DxsoRegisterPointer addr;
//////////////////////////////
// Rasterizer output registers
DxsoRegisterPointer oPos;
DxsoRegisterPointer oPSize;
};
/**
* \brief Pixel shader-specific structure
*/
struct DxsoCompilerPsPart {
uint32_t functionId = 0;
uint32_t samplerTypeSpec = 0;
uint32_t projectionSpec = 0;
//////////////
// Misc Types
DxsoRegisterPointer vPos;
DxsoRegisterPointer vFace;
///////////////////
// Colour Outputs
std::array<DxsoRegisterPointer, 4> oColor;
////////////////
// Depth output
DxsoRegisterPointer oDepth;
////////////////
// Shared State
uint32_t sharedState = 0;
uint32_t killState = 0;
uint32_t builtinLaneId = 0;
uint32_t diffuseColorIn = 0;
uint32_t specularColorIn = 0;
};
struct DxsoCfgBlockIf {
uint32_t ztestId;
uint32_t labelIf;
uint32_t labelElse;
uint32_t labelEnd;
size_t headerPtr;
};
struct DxsoCfgBlockLoop {
uint32_t labelHeader;
uint32_t labelBegin;
uint32_t labelContinue;
uint32_t labelBreak;
uint32_t iteratorPtr;
uint32_t strideVar;
uint32_t countBackup;
};
enum class DxsoCfgBlockType : uint32_t {
If, Loop
};
struct DxsoCfgBlock {
DxsoCfgBlockType type;
union {
DxsoCfgBlockIf b_if;
DxsoCfgBlockLoop b_loop;
};
};
using DxsoSrcArray = std::array<DxsoRegisterValue, DxsoMaxOperandCount>;
class DxsoCompiler {
public:
DxsoCompiler(
const std::string& fileName,
const DxsoModuleInfo& moduleInfo,
const DxsoProgramInfo& programInfo,
const DxsoAnalysisInfo& analysis,
const D3D9ConstantLayout& layout);
/**
* \brief Processes a single instruction
* \param [in] ins The instruction
*/
void processInstruction(
const DxsoInstructionContext& ctx);
/**
* \brief Finalizes the shader
*/
void finalize();
/**
* \brief Compiles the shader
* \returns The final shader objects
*/
DxsoPermutations compile();
const DxsoIsgn& isgn() { return m_isgn; }
const DxsoIsgn& osgn() { return m_osgn; }
const DxsoShaderMetaInfo& meta() { return m_meta; }
const DxsoDefinedConstants& constants() { return m_constants; }
uint32_t usedSamplers() const { return m_usedSamplers; }
uint32_t usedRTs() const { return m_usedRTs; }
private:
DxsoModuleInfo m_moduleInfo;
DxsoProgramInfo m_programInfo;
const DxsoAnalysisInfo* m_analysis;
const D3D9ConstantLayout* m_layout;
DxsoShaderMetaInfo m_meta;
DxsoDefinedConstants m_constants;
SpirvModule m_module;
///////////////////////////////////////////////////////
// Resource slot description for the shader. This will
// be used to map D3D9 bindings to DXVK bindings.
std::vector<DxvkResourceSlot> m_resourceSlots;
////////////////////////////////////////////////
// Temporary r# vector registers with immediate
// indexing, and x# vector array registers.
std::array<
DxsoRegisterPointer,
DxsoMaxTempRegs> m_rRegs;
////////////////////////////////////////////////
// Predicate registers
std::array<
DxsoRegisterPointer,
1> m_pRegs;
//////////////////////////////////////////////////////////////////
// Array of input values. Since v# and o# registers are indexable
// in DXSO, we need to copy them into an array first.
uint32_t m_vArray = 0;
uint32_t m_oArray = 0;
////////////////////////////////
// Input and output signatures
DxsoIsgn m_isgn;
DxsoIsgn m_osgn;
////////////////////////////////////
// Ptr to the constant buffer array
uint32_t m_cBuffer;
////////////////////////////////////////
// Constant buffer deffed mappings
std::array<uint32_t, caps::MaxFloatConstantsSoftware> m_cFloat;
std::array<uint32_t, caps::MaxOtherConstantsSoftware> m_cInt;
std::array<uint32_t, caps::MaxOtherConstantsSoftware> m_cBool;
//////////////////////
// Loop counter
DxsoRegisterPointer m_loopCounter;
///////////////////////////////////
// Working tex/coord registers (PS)
std::array<
DxsoRegisterPointer,
DxsoMaxTextureRegs> m_tRegs;
///////////////////////////////////////////////
// Control flow information. Stores labels for
// currently active if-else blocks and loops.
std::vector<DxsoCfgBlock> m_controlFlowBlocks;
//////////////////////////////////////////////
// Function state tracking. Required in order
// to properly end functions in some cases.
bool m_insideFunction = false;
////////////
// Samplers
std::array<DxsoSampler, 17> m_samplers;
////////////////////////////////////////////
// What io regswe need to
// NOT generate semantics for
uint16_t m_explicitInputs = 0;
uint16_t m_explicitOutputs = 0;
///////////////////////////////////////////////////
// Entry point description - we'll need to declare
// the function ID and all input/output variables.
std::vector<uint32_t> m_entryPointInterfaces;
uint32_t m_entryPointId = 0;
////////////////////////////////////////////
// Inter-stage shader interface slots. Also
// covers vertex input and fragment output.
DxvkInterfaceSlots m_interfaceSlots;
///////////////////////////////////
// Shader-specific data structures
DxsoCompilerVsPart m_vs;
DxsoCompilerPsPart m_ps;
DxsoRegisterPointer m_fog;
//////////////////////////////////////////
// Bit masks containing used samplers
// and render targets for hazard tracking
uint32_t m_usedSamplers;
uint32_t m_usedRTs;
uint32_t m_rsBlock = 0;
uint32_t m_mainFuncLabel = 0;
//////////////////////////////////////
// Common function definition methods
void emitInit();
//////////////////////
// Common shader dcls
void emitDclConstantBuffer();
void emitDclInputArray();
void emitDclOutputArray();
/////////////////////////////////
// Shader initialization methods
void emitVsInit();
void emitPsSharedConstants();
void emitPsInit();
void emitFunctionBegin(
uint32_t entryPoint,
uint32_t returnType,
uint32_t funcType);
void emitFunctionEnd();
uint32_t emitFunctionLabel();
void emitMainFunctionBegin();
///////////////////////////////
// Variable definition methods
uint32_t emitNewVariable(
const DxsoRegisterInfo& info);
uint32_t emitNewVariableDefault(
const DxsoRegisterInfo& info,
uint32_t value);
uint32_t emitNewBuiltinVariable(
const DxsoRegisterInfo& info,
spv::BuiltIn builtIn,
const char* name,
uint32_t value);
DxsoCfgBlock* cfgFindBlock(
const std::initializer_list<DxsoCfgBlockType>& types);
void emitDclInterface(
bool input,
uint32_t regNumber,
DxsoSemantic semantic,
DxsoRegMask mask,
bool centroid);
void emitDclSampler(
uint32_t idx,
DxsoTextureType type);
bool defineInput(uint32_t idx) {
bool alreadyDefined = m_interfaceSlots.inputSlots & 1u << idx;
m_interfaceSlots.inputSlots |= 1u << idx;
return alreadyDefined;
}
bool defineOutput(uint32_t idx) {
bool alreadyDefined = m_interfaceSlots.outputSlots & 1u << idx;
m_interfaceSlots.outputSlots |= 1u << idx;
return alreadyDefined;
}
uint32_t emitArrayIndex(
uint32_t idx,
const DxsoBaseRegister* relative);
DxsoRegisterPointer emitInputPtr(
bool texture,
const DxsoBaseRegister& reg,
const DxsoBaseRegister* relative);
DxsoRegisterPointer emitRegisterPtr(
const char* name,
DxsoScalarType ctype,
uint32_t ccount,
uint32_t defaultVal,
spv::StorageClass storageClass = spv::StorageClassPrivate,
spv::BuiltIn builtIn = spv::BuiltInMax);
DxsoRegisterValue emitLoadConstant(
const DxsoBaseRegister& reg,
const DxsoBaseRegister* relative);
DxsoRegisterPointer emitOutputPtr(
bool texcrdOut,
const DxsoBaseRegister& reg,
const DxsoBaseRegister* relative);
DxsoRegisterPointer emitGetOperandPtr(
const DxsoBaseRegister& reg,
const DxsoBaseRegister* relative);
DxsoRegisterPointer emitGetOperandPtr(
const DxsoRegister& reg) {
return this->emitGetOperandPtr(
reg,
reg.hasRelative ? &reg.relative : nullptr);
}
uint32_t emitBoolComparison(DxsoVectorType type, DxsoComparison cmp, uint32_t a, uint32_t b);
DxsoRegisterValue emitValueLoad(
DxsoRegisterPointer ptr);
void emitDstStore(
DxsoRegisterPointer ptr,
DxsoRegisterValue value,
DxsoRegMask writeMask,
bool saturate,
DxsoRegisterValue predicate,
int8_t shift,
DxsoRegisterId regId) {
if (regId.type == DxsoRegisterType::RasterizerOut && regId.num == RasterOutFog)
saturate = true;
if (value.type.ctype == DxsoScalarType::Float32) {
const uint32_t typeId = getVectorTypeId(value.type);
// Saturating only makes sense on floats
if (saturate) {
value.id = m_module.opNClamp(
typeId, value.id,
m_module.constfReplicant(0.0f, value.type.ccount),
m_module.constfReplicant(1.0f, value.type.ccount));
}
// There doesn't seem to be a nice float bitshift method for float vectors
// in Spirv that I can see... Resorting to multiplication.
if (shift != 0) {
float shiftAmount = shift < 0
? 1.0f / (1 << -shift)
: float(1 << shift);
uint32_t shiftConst = m_module.constf32(shiftAmount);
if (value.type.ccount == 1)
value.id = m_module.opFMul(typeId, value.id, shiftConst);
else
value.id = m_module.opVectorTimesScalar(typeId, value.id, shiftConst);
}
}
this->emitValueStore(ptr, value, writeMask, predicate);
}
DxsoRegisterValue applyPredicate(DxsoRegisterValue pred, DxsoRegisterValue dst, DxsoRegisterValue src);
void emitValueStore(
DxsoRegisterPointer ptr,
DxsoRegisterValue value,
DxsoRegMask writeMask,
DxsoRegisterValue predicate);
DxsoRegisterValue emitClampBoundReplicant(
DxsoRegisterValue srcValue,
float lb,
float ub);
DxsoRegisterValue emitSaturate(
DxsoRegisterValue srcValue);
DxsoRegisterValue emitDot(
DxsoRegisterValue a,
DxsoRegisterValue b);
DxsoRegisterValue emitRegisterInsert(
DxsoRegisterValue dstValue,
DxsoRegisterValue srcValue,
DxsoRegMask srcMask);
DxsoRegisterValue emitRegisterLoadRaw(
const DxsoBaseRegister& reg,
const DxsoBaseRegister* relative);
DxsoRegisterValue emitRegisterExtend(
DxsoRegisterValue value,
uint32_t size);
DxsoRegisterValue emitSrcOperandPreSwizzleModifiers(
DxsoRegisterValue value,
DxsoRegModifier modifier);
DxsoRegisterValue emitSrcOperandPostSwizzleModifiers(
DxsoRegisterValue value,
DxsoRegModifier modifier);
DxsoRegisterValue emitRegisterSwizzle(
DxsoRegisterValue value,
DxsoRegSwizzle swizzle,
DxsoRegMask writeMask);
DxsoRegisterValue emitRegisterLoad(
const DxsoBaseRegister& reg,
DxsoRegMask writeMask,
const DxsoBaseRegister* relative);
DxsoRegisterValue emitRegisterLoad(
const DxsoRegister& reg,
DxsoRegMask writeMask) {
return this->emitRegisterLoad(
reg, writeMask,
reg.hasRelative ? &reg.relative : nullptr);
}
DxsoRegisterValue emitPredicateLoad(const DxsoInstructionContext& ctx) {
if (!ctx.instruction.predicated)
return DxsoRegisterValue();
return emitRegisterLoad(ctx.pred, IdentityWriteMask);
}
DxsoRegisterValue emitRegisterLoadTexcoord(
const DxsoRegister& reg,
DxsoRegMask writeMask) {
DxsoRegister lookup = reg;
if (reg.id.type == DxsoRegisterType::Texture)
lookup.id.type = DxsoRegisterType::PixelTexcoord;
return this->emitRegisterLoad(lookup, writeMask);
}
Rc<DxvkShader> compileShader();
///////////////////////////////
// Handle shader ops
void emitDcl(const DxsoInstructionContext& ctx);
void emitDef(const DxsoInstructionContext& ctx);
void emitDefF(const DxsoInstructionContext& ctx);
void emitDefI(const DxsoInstructionContext& ctx);
void emitDefB(const DxsoInstructionContext& ctx);
bool isScalarRegister(DxsoRegisterId id);
void emitMov(const DxsoInstructionContext& ctx);
void emitPredicateOp(const DxsoInstructionContext& ctx);
void emitVectorAlu(const DxsoInstructionContext& ctx);
void emitMatrixAlu(const DxsoInstructionContext& ctx);
void emitControlFlowGenericLoop(
bool count,
uint32_t initialVar,
uint32_t strideVar,
uint32_t iterationCountVar);
void emitControlFlowGenericLoopEnd();
void emitControlFlowRep(const DxsoInstructionContext& ctx);
void emitControlFlowEndRep(const DxsoInstructionContext& ctx);
void emitControlFlowLoop(const DxsoInstructionContext& ctx);
void emitControlFlowEndLoop(const DxsoInstructionContext& ctx);
void emitControlFlowBreak(const DxsoInstructionContext& ctx);
void emitControlFlowBreakC(const DxsoInstructionContext& ctx);
void emitControlFlowIf(const DxsoInstructionContext& ctx);
void emitControlFlowElse(const DxsoInstructionContext& ctx);
void emitControlFlowEndIf(const DxsoInstructionContext& ctx);
void emitTexCoord(const DxsoInstructionContext& ctx);
void emitTextureSample(const DxsoInstructionContext& ctx);
void emitTextureKill(const DxsoInstructionContext& ctx);
uint32_t emitSample(
bool projected,
uint32_t resultType,
uint32_t sampledImage,
uint32_t coordinates,
uint32_t reference,
const SpirvImageOperands& operands);
///////////////////////////////
// Shader finalization methods
void emitInputSetup();
void emitVsClipping();
void setupRenderStateInfo();
void emitFog();
void emitPsProcessing();
void emitOutputDepthClamp();
void emitLinkerOutputSetup();
void emitVsFinalize();
void emitPsFinalize();
///////////////////////////
// Type definition methods
uint32_t getScalarTypeId(
DxsoScalarType type);
uint32_t getVectorTypeId(
const DxsoVectorType& type);
uint32_t getArrayTypeId(
const DxsoArrayType& type);
uint32_t getPointerTypeId(
const DxsoRegisterInfo& type);
};
}

19
src/dxso/dxso_ctab.cpp Normal file
View file

@ -0,0 +1,19 @@
#include "dxso_ctab.h"
namespace dxvk {
DxsoCtab::DxsoCtab(DxsoReader& reader, uint32_t commentTokenCount) {
m_size = reader.readu32();
if (m_size != sizeof(DxsoCtab))
throw DxvkError("DxsoCtab: ctab size invalid");
m_creator = reader.readu32();
m_version = reader.readu32();
m_constants = reader.readu32();
m_constantInfo = reader.readu32();
m_flags = reader.readu32();
m_target = reader.readu32();
}
}

32
src/dxso/dxso_ctab.h Normal file
View file

@ -0,0 +1,32 @@
#pragma once
#include "dxso_common.h"
#include "dxso_reader.h"
namespace dxvk {
/**
* \brief DXSO CTAB
*
* Stores meta information about the shader
*/
class DxsoCtab : public RcObject {
public:
DxsoCtab(DxsoReader& reader, uint32_t commentTokenCount);
private:
uint32_t m_size;
uint32_t m_creator;
uint32_t m_version;
uint32_t m_constants;
uint32_t m_constantInfo;
uint32_t m_flags;
uint32_t m_target;
};
}

276
src/dxso/dxso_decoder.cpp Normal file
View file

@ -0,0 +1,276 @@
#include "dxso_decoder.h"
#include "dxso_tables.h"
namespace dxvk {
bool DxsoSemantic::operator== (const DxsoSemantic& b) const {
return usage == b.usage && usageIndex == b.usageIndex;
}
bool DxsoSemantic::operator!= (const DxsoSemantic& b) const {
return usage != b.usage || usageIndex != b.usageIndex;
}
uint32_t DxsoDecodeContext::decodeInstructionLength(uint32_t token) {
auto opcode = m_ctx.instruction.opcode;
uint32_t length = 0;
const auto& info = this->getProgramInfo();
// Comment ops have their own system for getting length.
if (opcode == DxsoOpcode::Comment)
return (token & 0x7fff0000) >> 16;
if (opcode == DxsoOpcode::End)
return 0;
// SM2.0 and above has the length of the op in instruction count baked into it.
// SM1.4 and below have fixed lengths and run off expectation.
// Phase does not respect the following rules. :shrug:
if (opcode != DxsoOpcode::Phase) {
if (info.majorVersion() >= 2)
length = (token & 0x0f000000) >> 24;
else
length = DxsoGetDefaultOpcodeLength(opcode);
}
// We've already logged this...
if (length == InvalidOpcodeLength)
return 0;
// SM 1.4 has an extra param on Tex and TexCoord
// As stated before, it also doesn't have the length of the op baked into the opcode
if (info.majorVersion() == 1
&& info.minorVersion() == 4) {
switch (opcode) {
case DxsoOpcode::TexCoord:
case DxsoOpcode::Tex: length += 1;
default: break;
}
}
return length;
}
bool DxsoDecodeContext::relativeAddressingUsesToken(
DxsoInstructionArgumentType type) {
auto& info = this->getProgramInfo();
return (info.majorVersion() >= 2 && type == DxsoInstructionArgumentType::Source)
|| (info.majorVersion() >= 3 && type == DxsoInstructionArgumentType::Destination);
}
void DxsoDecodeContext::decodeDeclaration(DxsoCodeIter& iter) {
uint32_t dclToken = iter.read();
m_ctx.dcl.textureType = static_cast<DxsoTextureType>((dclToken & 0x78000000) >> 27);
m_ctx.dcl.semantic.usage = static_cast<DxsoUsage>(dclToken & 0x0000000f);
m_ctx.dcl.semantic.usageIndex = (dclToken & 0x000f0000) >> 16;
}
void DxsoDecodeContext::decodeDefinition(DxsoOpcode opcode, DxsoCodeIter& iter) {
const uint32_t instructionLength = std::min(m_ctx.instruction.tokenLength - 1, 4u);
for (uint32_t i = 0; i < instructionLength; i++)
m_ctx.def.uint32[i] = iter.read();
}
void DxsoDecodeContext::decodeBaseRegister(
DxsoBaseRegister& reg,
uint32_t token) {
reg.id.type = static_cast<DxsoRegisterType>(
((token & 0x00001800) >> 8)
| ((token & 0x70000000) >> 28));
reg.id.num = token & 0x000007ff;
}
void DxsoDecodeContext::decodeGenericRegister(
DxsoRegister& reg,
uint32_t token) {
this->decodeBaseRegister(reg, token);
reg.hasRelative = (token & (1 << 13)) == 8192;
reg.relative.id = DxsoRegisterId {
DxsoRegisterType::Addr, 0 };
reg.relative.swizzle = IdentitySwizzle;
reg.centroid = token & (4 << 20);
reg.partialPrecision = token & (2 << 20);
}
void DxsoDecodeContext::decodeRelativeRegister(
DxsoBaseRegister& reg,
uint32_t token) {
this->decodeBaseRegister(reg, token);
reg.swizzle = DxsoRegSwizzle(
uint8_t((token & 0x00ff0000) >> 16));
}
bool DxsoDecodeContext::decodeDestinationRegister(DxsoCodeIter& iter) {
uint32_t token = iter.read();
this->decodeGenericRegister(m_ctx.dst, token);
m_ctx.dst.mask = DxsoRegMask(
uint8_t((token & 0x000f0000) >> 16));
m_ctx.dst.saturate = (token & (1 << 20)) != 0;
m_ctx.dst.shift = (token & 0x0f000000) >> 24;
m_ctx.dst.shift = (m_ctx.dst.shift & 0x7) - (m_ctx.dst.shift & 0x8);
const bool extraToken =
relativeAddressingUsesToken(DxsoInstructionArgumentType::Destination);
if (m_ctx.dst.hasRelative && extraToken) {
this->decodeRelativeRegister(m_ctx.dst.relative, iter.read());
return true;
}
return false;
}
bool DxsoDecodeContext::decodeSourceRegister(uint32_t i, DxsoCodeIter& iter) {
if (i >= m_ctx.src.size())
throw DxvkError("DxsoDecodeContext::decodeSourceRegister: source register out of range.");
uint32_t token = iter.read();
this->decodeGenericRegister(m_ctx.src[i], token);
m_ctx.src[i].swizzle = DxsoRegSwizzle(
uint8_t((token & 0x00ff0000) >> 16));
m_ctx.src[i].modifier = static_cast<DxsoRegModifier>(
(token & 0x0f000000) >> 24);
const bool extraToken =
relativeAddressingUsesToken(DxsoInstructionArgumentType::Source);
if (m_ctx.src[i].hasRelative && extraToken) {
this->decodeRelativeRegister(m_ctx.src[i].relative, iter.read());
return true;
}
return false;
}
void DxsoDecodeContext::decodePredicateRegister(DxsoCodeIter& iter) {
uint32_t token = iter.read();
this->decodeGenericRegister(m_ctx.pred, token);
m_ctx.pred.swizzle = DxsoRegSwizzle(
uint8_t((token & 0x00ff0000) >> 16));
m_ctx.pred.modifier = static_cast<DxsoRegModifier>(
(token & 0x0f000000) >> 24);
}
bool DxsoDecodeContext::decodeInstruction(DxsoCodeIter& iter) {
uint32_t token = iter.read();
m_ctx.instruction.opcode = static_cast<DxsoOpcode>(
token & 0x0000ffff);
m_ctx.instruction.predicated = token & (1 << 28);
m_ctx.instruction.specificData.uint32 =
(token & 0x00ff0000) >> 16;
m_ctx.instruction.tokenLength =
this->decodeInstructionLength(token);
uint32_t tokenLength =
m_ctx.instruction.tokenLength;
switch (m_ctx.instruction.opcode) {
case DxsoOpcode::If:
case DxsoOpcode::Ifc:
case DxsoOpcode::Rep:
case DxsoOpcode::Loop:
case DxsoOpcode::BreakC:
case DxsoOpcode::BreakP: {
uint32_t sourceIdx = 0;
for (uint32_t i = 0; i < tokenLength; i++) {
if (this->decodeSourceRegister(sourceIdx, iter))
i++;
sourceIdx++;
}
return true;
}
case DxsoOpcode::Dcl:
this->decodeDeclaration(iter);
this->decodeDestinationRegister(iter);
return true;
case DxsoOpcode::Def:
case DxsoOpcode::DefI:
case DxsoOpcode::DefB:
this->decodeDestinationRegister(iter);
this->decodeDefinition(
m_ctx.instruction.opcode, iter);
return true;
case DxsoOpcode::Comment:
iter = iter.skip(tokenLength);
return true;
default: {
uint32_t sourceIdx = 0;
for (uint32_t i = 0; i < tokenLength; i++) {
if (i == 0) {
if (this->decodeDestinationRegister(iter))
i++;
}
else if (i == 1 && m_ctx.instruction.predicated) {
// Relative addressing makes no sense
// for predicate registers.
this->decodePredicateRegister(iter);
}
else {
if (this->decodeSourceRegister(sourceIdx, iter))
i++;
sourceIdx++;
}
}
return true;
}
case DxsoOpcode::End:
return false;
}
}
std::ostream& operator << (std::ostream& os, DxsoUsage usage) {
switch (usage) {
case DxsoUsage::Position: os << "Position"; break;
case DxsoUsage::BlendWeight: os << "BlendWeight"; break;
case DxsoUsage::BlendIndices: os << "BlendIndices"; break;
case DxsoUsage::Normal: os << "Normal"; break;
case DxsoUsage::PointSize: os << "PointSize"; break;
case DxsoUsage::Texcoord: os << "Texcoord"; break;
case DxsoUsage::Tangent: os << "Tangent"; break;
case DxsoUsage::Binormal: os << "Binormal"; break;
case DxsoUsage::TessFactor: os << "TessFactor"; break;
case DxsoUsage::PositionT: os << "PositionT"; break;
case DxsoUsage::Color: os << "Color"; break;
case DxsoUsage::Fog: os << "Fog"; break;
case DxsoUsage::Depth: os << "Depth"; break;
case DxsoUsage::Sample: os << "Sample"; break;
default:
os << "Invalid Format (" << static_cast<uint32_t>(usage) << ")"; break;
}
return os;
}
}

271
src/dxso/dxso_decoder.h Normal file
View file

@ -0,0 +1,271 @@
#pragma once
#include "dxso_common.h"
#include "dxso_enums.h"
#include "dxso_code.h"
namespace dxvk {
constexpr size_t DxsoMaxTempRegs = 32;
constexpr size_t DxsoMaxTextureRegs = 10;
constexpr size_t DxsoMaxInterfaceRegs = 16;
constexpr size_t DxsoMaxOperandCount = 8;
constexpr uint32_t DxsoRegModifierShift = 24;
class DxsoDecodeContext;
/**
* \brief Source operand modifiers
*
* These are applied after loading
* an operand register.
*/
enum class DxsoRegModifier : uint32_t {
None = 0, // r
Neg = 1, // -r
Bias = 2, // r - 0.5
BiasNeg = 3, // -(r - 0.5)
Sign = 4, // fma(r, 2.0f, -1.0f)
SignNeg = 5, // -fma(r, 2.0f, -1.0f)
Comp = 6, // 1 - r
X2 = 7, // r * 2
X2Neg = 8, // -r * 2
Dz = 9, // r / r.z
Dw = 10, // r / r.w
Abs = 11, // abs(r)
AbsNeg = 12, // -abs(r)
Not = 13, // !r
};
enum class DxsoInstructionArgumentType : uint16_t {
Source,
Destination
};
enum class DxsoComparison : uint32_t {
// < = >
Never = 0, // 0 0 0
GreaterThan = 1, // 0 0 1
Equal = 2, // 0 1 0
GreaterEqual = 3, // 0 1 1
LessThan = 4, // 1 0 0
NotEqual = 5, // 1 0 1
LessEqual = 6, // 1 1 0
Always = 7 // 1 1 1
};
enum class DxsoTexLdMode : uint32_t {
Regular = 0,
Project = 1,
Bias = 2
};
union DxsoOpcodeSpecificData {
DxsoComparison comparison;
DxsoTexLdMode texld;
uint32_t uint32;
};
struct DxsoShaderInstruction {
DxsoOpcode opcode;
bool predicated;
DxsoOpcodeSpecificData specificData;
uint32_t tokenLength;
};
struct DxsoRegisterId {
DxsoRegisterType type;
uint32_t num;
bool operator == (const DxsoRegisterId& other) const { return type == other.type && num == other.num; }
bool operator != (const DxsoRegisterId& other) const { return type != other.type || num != other.num; }
};
class DxsoRegMask {
public:
DxsoRegMask(uint8_t mask)
: m_mask(mask) { }
DxsoRegMask(bool x, bool y, bool z, bool w)
: m_mask((x ? 0x1 : 0) | (y ? 0x2 : 0)
| (z ? 0x4 : 0) | (w ? 0x8 : 0)) { }
bool operator [] (uint32_t id) const {
return ((m_mask & (1u << id)) != 0);
}
uint32_t popCount() const {
const uint8_t n[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 3, 3, 4 };
return n[m_mask & 0xF];
}
uint32_t firstSet() const {
const uint8_t n[16] = { 4, 0, 1, 0, 2, 0, 1, 0,
3, 0, 1, 0, 2, 0, 1, 0 };
return n[m_mask & 0xF];
}
uint32_t minComponents() const {
const uint8_t n[16] = { 0, 1, 2, 2, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4 };
return n[m_mask & 0xF];
}
bool operator == (const DxsoRegMask& other) const { return m_mask == other.m_mask; }
bool operator != (const DxsoRegMask& other) const { return m_mask != other.m_mask; }
private:
uint8_t m_mask;
};
const DxsoRegMask IdentityWriteMask = DxsoRegMask(true, true, true, true);
class DxsoRegSwizzle {
public:
DxsoRegSwizzle(uint8_t mask)
: m_mask(mask) { }
DxsoRegSwizzle(uint32_t x, uint32_t y, uint32_t z, uint32_t w)
: m_mask((x << 0) | (y << 2) | (z << 4) | (w << 6)) {}
uint32_t operator [] (uint32_t id) const {
return (m_mask >> (id + id)) & 0x3;
}
bool operator == (const DxsoRegSwizzle& other) const { return m_mask == other.m_mask; }
bool operator != (const DxsoRegSwizzle& other) const { return m_mask != other.m_mask; }
private:
uint8_t m_mask;
};
const DxsoRegSwizzle IdentitySwizzle{ 0, 1, 2, 3 };
struct DxsoBaseRegister {
DxsoRegisterId id = { DxsoRegisterType::Temp, 0 };
bool centroid = false;
bool partialPrecision = false;
bool saturate = false;
DxsoRegModifier modifier = DxsoRegModifier::None;
DxsoRegMask mask = IdentityWriteMask;
DxsoRegSwizzle swizzle = IdentitySwizzle;
int8_t shift = 0;
};
struct DxsoRegister : public DxsoBaseRegister {
bool hasRelative = false;
DxsoBaseRegister relative;
};
struct DxsoSemantic {
DxsoUsage usage;
uint32_t usageIndex;
bool operator== (const DxsoSemantic& b) const;
bool operator!= (const DxsoSemantic& b) const;
};
struct DxsoDeclaration {
DxsoSemantic semantic;
DxsoTextureType textureType;
};
union DxsoDefinition {
float float32[4];
int32_t int32[4];
// Not a type we actually use in compiler, but used for decoding.
uint32_t uint32[4];
};
struct DxsoInstructionContext {
DxsoShaderInstruction instruction;
DxsoRegister pred;
DxsoRegister dst;
std::array<
DxsoRegister,
DxsoMaxOperandCount> src;
DxsoDefinition def;
DxsoDeclaration dcl;
};
class DxsoDecodeContext {
public:
DxsoDecodeContext(const DxsoProgramInfo& programInfo)
: m_programInfo( programInfo ) { }
/**
* \brief Retrieves current instruction context
*
* This is only valid after a call to \ref decode.
* \returns Reference to last decoded instruction & its context
*/
const DxsoInstructionContext& getInstructionContext() const {
return m_ctx;
}
const DxsoProgramInfo& getProgramInfo() const {
return m_programInfo;
}
/**
* \brief Decodes an instruction
*
* This also advances the given code slice by the
* number of dwords consumed by the instruction.
* \param [in] code Code slice
*/
bool decodeInstruction(DxsoCodeIter& iter);
private:
uint32_t decodeInstructionLength(uint32_t token);
void decodeBaseRegister(
DxsoBaseRegister& reg,
uint32_t token);
void decodeGenericRegister(
DxsoRegister& reg,
uint32_t token);
void decodeRelativeRegister(
DxsoBaseRegister& reg,
uint32_t token);
// Returns whether an extra token was read.
bool decodeDestinationRegister(DxsoCodeIter& iter);
bool decodeSourceRegister(uint32_t i, DxsoCodeIter& iter);
void decodePredicateRegister(DxsoCodeIter& iter);
void decodeDeclaration(DxsoCodeIter& iter);
void decodeDefinition(DxsoOpcode opcode, DxsoCodeIter& iter);
bool relativeAddressingUsesToken(DxsoInstructionArgumentType type);
const DxsoProgramInfo& m_programInfo;
DxsoInstructionContext m_ctx;
};
std::ostream& operator << (std::ostream& os, DxsoUsage usage);
}

101
src/dxso/dxso_enums.cpp Normal file
View file

@ -0,0 +1,101 @@
#include "dxso_enums.h"
namespace dxvk {
std::ostream& operator << (std::ostream& os, DxsoOpcode opcode) {
switch (opcode) {
case DxsoOpcode::Nop: os << "Nop"; break;
case DxsoOpcode::Mov: os << "Mov"; break;
case DxsoOpcode::Add: os << "Add"; break;
case DxsoOpcode::Sub: os << "Sub"; break;
case DxsoOpcode::Mad: os << "Mad"; break;
case DxsoOpcode::Mul: os << "Mul"; break;
case DxsoOpcode::Rcp: os << "Rcp"; break;
case DxsoOpcode::Rsq: os << "Rsq"; break;
case DxsoOpcode::Dp3: os << "Dp3"; break;
case DxsoOpcode::Dp4: os << "Dp4"; break;
case DxsoOpcode::Min: os << "Min"; break;
case DxsoOpcode::Max: os << "Max"; break;
case DxsoOpcode::Slt: os << "Slt"; break;
case DxsoOpcode::Sge: os << "Sge"; break;
case DxsoOpcode::Exp: os << "Exp"; break;
case DxsoOpcode::Log: os << "Log"; break;
case DxsoOpcode::Lit: os << "Lit"; break;
case DxsoOpcode::Dst: os << "Dst"; break;
case DxsoOpcode::Lrp: os << "Lrp"; break;
case DxsoOpcode::Frc: os << "Frc"; break;
case DxsoOpcode::M4x4: os << "M4x4"; break;
case DxsoOpcode::M4x3: os << "M4x3"; break;
case DxsoOpcode::M3x4: os << "M3x4"; break;
case DxsoOpcode::M3x3: os << "M3x3"; break;
case DxsoOpcode::M3x2: os << "M3x2"; break;
case DxsoOpcode::Call: os << "Call"; break;
case DxsoOpcode::CallNz: os << "CallNz"; break;
case DxsoOpcode::Loop: os << "Loop"; break;
case DxsoOpcode::Ret: os << "Ret"; break;
case DxsoOpcode::EndLoop: os << "EndLoop"; break;
case DxsoOpcode::Label: os << "Label"; break;
case DxsoOpcode::Dcl: os << "Dcl"; break;
case DxsoOpcode::Pow: os << "Pow"; break;
case DxsoOpcode::Crs: os << "Crs"; break;
case DxsoOpcode::Sgn: os << "Sgn"; break;
case DxsoOpcode::Abs: os << "Abs"; break;
case DxsoOpcode::Nrm: os << "Nrm"; break;
case DxsoOpcode::SinCos: os << "SinCos"; break;
case DxsoOpcode::Rep: os << "Rep"; break;
case DxsoOpcode::EndRep: os << "EndRep"; break;
case DxsoOpcode::If: os << "If"; break;
case DxsoOpcode::Ifc: os << "Ifc"; break;
case DxsoOpcode::Else: os << "Else"; break;
case DxsoOpcode::EndIf: os << "EndIf"; break;
case DxsoOpcode::Break: os << "Break"; break;
case DxsoOpcode::BreakC: os << "BreakC"; break;
case DxsoOpcode::Mova: os << "Mova"; break;
case DxsoOpcode::DefB: os << "DefB"; break;
case DxsoOpcode::DefI: os << "DefI"; break;
case DxsoOpcode::TexCoord: os << "TexCoord"; break;
case DxsoOpcode::TexKill: os << "TexKill"; break;
case DxsoOpcode::Tex: os << "Tex"; break;
case DxsoOpcode::TexBem: os << "TexBem"; break;
case DxsoOpcode::TexBemL: os << "TexBemL"; break;
case DxsoOpcode::TexReg2Ar: os << "TexReg2Ar"; break;
case DxsoOpcode::TexReg2Gb: os << "TexReg2Gb"; break;
case DxsoOpcode::TexM3x2Pad: os << "TexM3x2Pad"; break;
case DxsoOpcode::TexM3x2Tex: os << "TexM3x2Tex"; break;
case DxsoOpcode::TexM3x3Pad: os << "TexM3x3Pad"; break;
case DxsoOpcode::TexM3x3Tex: os << "TexM3x3Tex"; break;
case DxsoOpcode::Reserved0: os << "Reserved0"; break;
case DxsoOpcode::TexM3x3Spec: os << "TexM3x3Spec"; break;
case DxsoOpcode::TexM3x3VSpec: os << "TexM3x3VSpec"; break;
case DxsoOpcode::ExpP: os << "ExpP"; break;
case DxsoOpcode::LogP: os << "LogP"; break;
case DxsoOpcode::Cnd: os << "Cnd"; break;
case DxsoOpcode::Def: os << "Def"; break;
case DxsoOpcode::TexReg2Rgb: os << "TexReg2Rgb"; break;
case DxsoOpcode::TexDp3Tex: os << "TexDp3Tex"; break;
case DxsoOpcode::TexM3x2Depth: os << "TexM3x2Depth"; break;
case DxsoOpcode::TexDp3: os << "TexDp3"; break;
case DxsoOpcode::TexM3x3: os << "TexM3x3"; break;
case DxsoOpcode::TexDepth: os << "TexDepth"; break;
case DxsoOpcode::Cmp: os << "Cmp"; break;
case DxsoOpcode::Bem: os << "Bem"; break;
case DxsoOpcode::Dp2Add: os << "Dp2Add"; break;
case DxsoOpcode::DsX: os << "DsX"; break;
case DxsoOpcode::DsY: os << "DsY"; break;
case DxsoOpcode::TexLdd: os << "TexLdd"; break;
case DxsoOpcode::SetP: os << "SetP"; break;
case DxsoOpcode::TexLdl: os << "TexLdl"; break;
case DxsoOpcode::BreakP: os << "BreakP"; break;
case DxsoOpcode::Phase: os << "Phase"; break;
case DxsoOpcode::Comment: os << "Comment"; break;
case DxsoOpcode::End: os << "End"; break;
default:
os << "Invalid Opcode (" << static_cast<uint32_t>(opcode) << ")"; break;
}
return os;
}
}

164
src/dxso/dxso_enums.h Normal file
View file

@ -0,0 +1,164 @@
#pragma once
#include "dxso_include.h"
#include <cstdint>
namespace dxvk {
/**
* \brief Instruction code listing
*/
enum class DxsoOpcode : uint32_t {
Nop = 0,
Mov ,
Add ,
Sub ,
Mad ,
Mul ,
Rcp ,
Rsq ,
Dp3 ,
Dp4 ,
Min ,
Max ,
Slt ,
Sge ,
Exp ,
Log ,
Lit ,
Dst ,
Lrp ,
Frc ,
M4x4 ,
M4x3 ,
M3x4 ,
M3x3 ,
M3x2 ,
Call ,
CallNz ,
Loop ,
Ret ,
EndLoop ,
Label ,
Dcl ,
Pow ,
Crs ,
Sgn ,
Abs ,
Nrm ,
SinCos ,
Rep ,
EndRep ,
If ,
Ifc ,
Else ,
EndIf ,
Break ,
BreakC ,
Mova ,
DefB ,
DefI ,
TexCoord = 64,
TexKill ,
Tex ,
TexBem ,
TexBemL ,
TexReg2Ar ,
TexReg2Gb ,
TexM3x2Pad ,
TexM3x2Tex ,
TexM3x3Pad ,
TexM3x3Tex ,
Reserved0 ,
TexM3x3Spec ,
TexM3x3VSpec ,
ExpP ,
LogP ,
Cnd ,
Def ,
TexReg2Rgb ,
TexDp3Tex ,
TexM3x2Depth ,
TexDp3 ,
TexM3x3 ,
TexDepth ,
Cmp ,
Bem ,
Dp2Add ,
DsX ,
DsY ,
TexLdd ,
SetP ,
TexLdl ,
BreakP ,
Phase = 0xfffd,
Comment = 0xfffe,
End = 0xffff
};
std::ostream& operator << (std::ostream& os, DxsoOpcode opcode);
enum class DxsoRegisterType : uint32_t {
Temp = 0, // Temporary Register File
Input = 1, // Input Register File
Const = 2, // Constant Register File
Addr = 3, // Address Register (VS)
Texture = 3, // Texture Register File (PS)
RasterizerOut = 4, // Rasterizer Register File
AttributeOut = 5, // Attribute Output Register File
TexcoordOut = 6, // Texture Coordinate Output Register File
Output = 6, // Output register file for VS3.0+
ConstInt = 7, // Constant Integer Vector Register File
ColorOut = 8, // Color Output Register File
DepthOut = 9, // Depth Output Register File
Sampler = 10, // Sampler State Register File
Const2 = 11, // Constant Register File 2048 - 4095
Const3 = 12, // Constant Register File 4096 - 6143
Const4 = 13, // Constant Register File 6144 - 8191
ConstBool = 14, // Constant Boolean register file
Loop = 15, // Loop counter register file
TempFloat16 = 16, // 16-bit float temp register file
MiscType = 17, // Miscellaneous (single) registers.
Label = 18, // Label
Predicate = 19, // Predicate register
PixelTexcoord = 20
};
enum class DxsoUsage : uint32_t {
Position = 0,
BlendWeight, // 1
BlendIndices, // 2
Normal, // 3
PointSize, // 4
Texcoord, // 5
Tangent, // 6
Binormal, // 7
TessFactor, // 8
PositionT, // 9
Color, // 10
Fog, // 11
Depth, // 12
Sample, // 13
};
enum class DxsoTextureType : uint32_t {
Texture2D = 2,
TextureCube = 3,
Texture3D = 4
};
enum DxsoReasterizerOutIndices : uint32_t {
RasterOutPosition = 0,
RasterOutFog = 1,
RasterOutPointSize = 2
};
enum DxsoMiscTypeIndices : uint32_t {
MiscTypePosition,
MiscTypeFace,
};
}

24
src/dxso/dxso_header.cpp Normal file
View file

@ -0,0 +1,24 @@
#include "dxso_header.h"
namespace dxvk {
DxsoHeader::DxsoHeader(DxsoReader& reader) {
uint32_t headerToken = reader.readu32();
uint32_t headerTypeMask = headerToken & 0xffff0000;
DxsoProgramType programType;
if (headerTypeMask == 0xffff0000)
programType = DxsoProgramTypes::PixelShader;
else if (headerTypeMask == 0xfffe0000)
programType = DxsoProgramTypes::VertexShader;
else
throw DxvkError("DxsoHeader: invalid header - invalid version");
const uint32_t majorVersion = (headerToken >> 8) & 0xff;
const uint32_t minorVersion = headerToken & 0xff;
m_info = DxsoProgramInfo{ programType, minorVersion, majorVersion };
}
}

31
src/dxso/dxso_header.h Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#include "dxso_common.h"
#include "dxso_reader.h"
namespace dxvk {
/**
* \brief DXSO header
*
* Stores meta information about the shader such
* as the version and the type.
*/
class DxsoHeader {
public:
DxsoHeader(DxsoReader& reader);
const DxsoProgramInfo& info() const {
return m_info;
}
private:
DxsoProgramInfo m_info;
};
}

0
src/dxso/dxso_helpers.h Normal file
View file

18
src/dxso/dxso_include.h Normal file
View file

@ -0,0 +1,18 @@
#pragma once
#include "../dxvk/dxvk_shader.h"
#include "../util/com/com_guid.h"
#include "../util/com/com_object.h"
#include "../util/com/com_pointer.h"
#include "../util/log/log.h"
#include "../util/log/log_debug.h"
#include "../util/rc/util_rc.h"
#include "../util/rc/util_rc_ptr.h"
#include "../util/util_bit.h"
#include "../util/util_enum.h"
#include "../util/util_error.h"
#include "../util/util_string.h"

39
src/dxso/dxso_isgn.h Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include "dxso_decoder.h"
namespace dxvk {
struct DxsoIsgnEntry {
uint32_t regNumber = 0;
uint32_t slot = 0;
DxsoSemantic semantic = DxsoSemantic{ DxsoUsage::Position, 0 };
DxsoRegMask mask = IdentityWriteMask;
bool centroid = false;
};
struct DxsoIsgn {
std::array<
DxsoIsgnEntry,
2 * DxsoMaxInterfaceRegs> elems;
uint32_t elemCount = 0;
};
struct DxsoDefinedConstant {
uint32_t uboIdx;
// Only float constants may be indexed.
// So that's the only ones we care about putting in the UBO.
float float32[4];
};
using DxsoDefinedConstants = std::vector<DxsoDefinedConstant>;
struct DxsoShaderMetaInfo {
bool needsConstantCopies = false;
uint32_t maxConstIndexF = 0;
uint32_t maxConstIndexI = 0;
uint32_t maxConstIndexB = 0;
};
}

17
src/dxso/dxso_modinfo.h Normal file
View file

@ -0,0 +1,17 @@
#pragma once
#include "dxso_options.h"
namespace dxvk {
/**
* \brief Shader module info
*
* Stores information which may affect shader compilation.
* This data can be supplied by the client API implementation.
*/
struct DxsoModuleInfo {
DxsoOptions options;
};
}

Some files were not shown because too many files have changed in this diff Show more