From 86733c2b482576f8135ae304d7423ac5579623c9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 22 Feb 2023 23:35:12 +0200 Subject: [PATCH] [d3d8] Squash patch snap-20230222 --- LICENSE | 1 + README.md | 13 +- dxvk.conf | 8 + meson_options.txt | 1 + src/d3d8/d3d8.def | 3 + src/d3d8/d3d8_blit.cpp | 320 +++++++++ src/d3d8/d3d8_buffer.h | 83 +++ src/d3d8/d3d8_caps.h | 8 + src/d3d8/d3d8_d3d9_util.h | 136 ++++ src/d3d8/d3d8_device.cpp | 553 ++++++++++++++ src/d3d8/d3d8_device.h | 877 +++++++++++++++++++++++ src/d3d8/d3d8_device_child.h | 68 ++ src/d3d8/d3d8_include.h | 226 ++++++ src/d3d8/d3d8_interface.cpp | 143 ++++ src/d3d8/d3d8_interface.h | 178 +++++ src/d3d8/d3d8_main.cpp | 37 + src/d3d8/d3d8_options.h | 21 + src/d3d8/d3d8_resource.h | 101 +++ src/d3d8/d3d8_shader.cpp | 329 +++++++++ src/d3d8/d3d8_shader.h | 14 + src/d3d8/d3d8_state_block.cpp | 44 ++ src/d3d8/d3d8_state_block.h | 116 +++ src/d3d8/d3d8_subresource.h | 61 ++ src/d3d8/d3d8_surface.cpp | 25 + src/d3d8/d3d8_surface.h | 84 +++ src/d3d8/d3d8_swapchain.h | 42 ++ src/d3d8/d3d8_texture.h | 243 +++++++ src/d3d8/d3d8_volume.h | 40 ++ src/d3d8/d3d8_wrapped_object.h | 68 ++ src/d3d8/meson.build | 25 + src/d3d8/version.rc | 31 + src/d3d9/d3d9_bridge.cpp | 87 +++ src/d3d9/d3d9_bridge.h | 111 +++ src/d3d9/d3d9_common_texture.cpp | 6 +- src/d3d9/d3d9_device.cpp | 27 +- src/d3d9/d3d9_device.h | 9 +- src/d3d9/d3d9_fixed_function.cpp | 85 ++- src/d3d9/d3d9_fixed_function.h | 35 + src/d3d9/d3d9_interface.cpp | 7 + src/d3d9/d3d9_interface.h | 3 + src/d3d9/d3d9_spec_constants.h | 6 +- src/d3d9/d3d9_swapchain.cpp | 12 +- src/d3d9/d3d9_swapchain.h | 4 + src/d3d9/meson.build | 4 +- src/dxso/dxso_compiler.cpp | 24 + src/dxso/dxso_options.h | 10 + src/meson.build | 9 +- src/util/config/config.cpp | 23 + src/util/util_vector.h | 2 +- src/vulkan/vulkan_util.h | 5 + 54 files changed, 4785 insertions(+), 28 deletions(-) create mode 100755 build_d3d8.sh create mode 100755 setup_d3d8.sh create mode 100644 src/d3d8/d3d8.def create mode 100644 src/d3d8/d3d8_blit.cpp create mode 100644 src/d3d8/d3d8_buffer.h create mode 100644 src/d3d8/d3d8_caps.h create mode 100644 src/d3d8/d3d8_d3d9_util.h create mode 100644 src/d3d8/d3d8_device.cpp create mode 100644 src/d3d8/d3d8_device.h create mode 100644 src/d3d8/d3d8_device_child.h create mode 100644 src/d3d8/d3d8_include.h create mode 100644 src/d3d8/d3d8_interface.cpp create mode 100644 src/d3d8/d3d8_interface.h create mode 100644 src/d3d8/d3d8_main.cpp create mode 100644 src/d3d8/d3d8_options.h create mode 100644 src/d3d8/d3d8_resource.h create mode 100644 src/d3d8/d3d8_shader.cpp create mode 100644 src/d3d8/d3d8_shader.h create mode 100644 src/d3d8/d3d8_state_block.cpp create mode 100644 src/d3d8/d3d8_state_block.h create mode 100644 src/d3d8/d3d8_subresource.h create mode 100644 src/d3d8/d3d8_surface.cpp create mode 100644 src/d3d8/d3d8_surface.h create mode 100644 src/d3d8/d3d8_swapchain.h create mode 100644 src/d3d8/d3d8_texture.h create mode 100644 src/d3d8/d3d8_volume.h create mode 100644 src/d3d8/d3d8_wrapped_object.h create mode 100644 src/d3d8/meson.build create mode 100644 src/d3d8/version.rc create mode 100644 src/d3d9/d3d9_bridge.cpp create mode 100644 src/d3d9/d3d9_bridge.h diff --git a/LICENSE b/LICENSE index 253cadd6..5ba42b5f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ Copyright (c) 2017 Philip Rebohle Copyright (c) 2019 Joshua Ashton + Copyright (c) 2020 Jeffrey Ellison zlib/libpng license diff --git a/README.md b/README.md index ae06e41a..205c6533 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,17 @@ +# D8VK + +An implementation of Direct3D 8 for DXVK. + +D8VK Documentation: + - [List of Supported Games](https://github.com/AlpyneDreams/d8vk/wiki) + - [List of D3D8 Samples](https://github.com/AlpyneDreams/d8vk/wiki/D3D8-Samples) + - [Building & Debugging](https://github.com/AlpyneDreams/d8vk/wiki/Building-&-Debugging) + +--- + # DXVK -A Vulkan-based translation layer for Direct3D 9/10/11 which allows running 3D applications on Linux using Wine. +A Vulkan-based translation layer for Direct3D 8/9/10/11 which allows running 3D applications on Linux using Wine. For the current status of the project, please refer to the [project wiki](https://github.com/doitsujin/dxvk/wiki). diff --git a/dxvk.conf b/dxvk.conf index 626c187a..e5db2be8 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -604,3 +604,11 @@ # DO NOT CHANGE THIS UNLESS YOU HAVE A VERY GOOD REASON. # d3d9.textureMemory = 100 + +# Use NVIDIA Shadow Buffers +# +# Vendor behavior for GeForce3 and GeForce4 cards that allows +# sampling depth textures with non-normalized Z coordinates +# and applies hardware shadow filtering. + +# d3d8.useShadowBuffers = False diff --git a/meson_options.txt b/meson_options.txt index 5ac9ea7b..2a621ece 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,5 @@ option('enable_dxgi', type : 'boolean', value : true, description: 'Build DXGI') +option('enable_d3d8', type : 'boolean', value : true, description: 'Build D3D8') 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') diff --git a/src/d3d8/d3d8.def b/src/d3d8/d3d8.def new file mode 100644 index 00000000..62aca0c9 --- /dev/null +++ b/src/d3d8/d3d8.def @@ -0,0 +1,3 @@ +LIBRARY D3D8.DLL +EXPORTS + Direct3DCreate8 @ 5 diff --git a/src/d3d8/d3d8_blit.cpp b/src/d3d8/d3d8_blit.cpp new file mode 100644 index 00000000..4a3ebbb8 --- /dev/null +++ b/src/d3d8/d3d8_blit.cpp @@ -0,0 +1,320 @@ +#include "d3d8_device.h" + +/** + * Implements all cases of CopyRects + */ + +namespace dxvk { + + static constexpr bool isDXT(d3d9::D3DFORMAT fmt) { + return fmt == d3d9::D3DFMT_DXT1 + || fmt == d3d9::D3DFMT_DXT2 + || fmt == d3d9::D3DFMT_DXT3 + || fmt == d3d9::D3DFMT_DXT4 + || fmt == d3d9::D3DFMT_DXT5; + } + + // Copies texture rect in system mem using memcpy. + // Rects must be congruent, but need not be aligned. + HRESULT copyTextureBuffers( + D3D8Surface* src, + D3D8Surface* dst, + const d3d9::D3DSURFACE_DESC& srcDesc, + const d3d9::D3DSURFACE_DESC& dstDesc, + const RECT& srcRect, + const RECT& dstRect) { + HRESULT res = D3D_OK; + D3DLOCKED_RECT srcLocked, dstLocked; + + // CopyRects cannot perform format conversions. + if (srcDesc.Format != dstDesc.Format) + return D3DERR_INVALIDCALL; + + bool compressed = isDXT(srcDesc.Format); + + res = src->LockRect(&srcLocked, &srcRect, D3DLOCK_READONLY); + if (FAILED(res)) + return res; + + res = dst->LockRect(&dstLocked, &dstRect, 0); + if (FAILED(res)) { + src->UnlockRect(); + return res; + } + + auto rows = srcRect.bottom - srcRect.top; + auto cols = srcRect.right - srcRect.left; + auto bpp = srcLocked.Pitch / srcDesc.Width; + + if (!compressed + && srcRect.left == 0 + && srcRect.right == LONG(srcDesc.Width) + && srcDesc.Width == dstDesc.Width + && srcLocked.Pitch == dstLocked.Pitch) { + + // If copying the entire texture into a congruent destination, + // we can do this in one continuous copy. + std::memcpy(dstLocked.pBits, srcLocked.pBits, srcLocked.Pitch * rows); + + } else { + // Bytes per row of the rect + auto amplitude = cols * bpp; + + // Handle DXT compressed textures. + // TODO: Are rects always 4x4 aligned? + if (compressed) { + // Assume that DXT blocks are 4x4 pixels. + constexpr UINT blockWidth = 4; + constexpr UINT blockHeight = 4; + + // Compute rect dimensions in 4x4 blocks + UINT rectWidthBlocks = cols / blockWidth; + UINT rectHeightBlocks = rows / blockHeight; + + // Compute total texture width in blocks + // to derive block size in bytes using the pitch. + UINT texWidthBlocks = std::max(srcDesc.Width / blockWidth, 1u); + UINT bytesPerBlock = srcLocked.Pitch / texWidthBlocks; + + // Copy H/4 rows of W/4 blocks + amplitude = rectWidthBlocks * bytesPerBlock; + rows = rectHeightBlocks; + } + + // Copy one row at a time + size_t srcOffset = 0, dstOffset = 0; + for (auto i = 0; i < rows; i++) { + std::memcpy( + (uint8_t*)dstLocked.pBits + dstOffset, + (uint8_t*)srcLocked.pBits + srcOffset, + amplitude); + srcOffset += srcLocked.Pitch; + dstOffset += dstLocked.Pitch; + } + } + + res = dst->UnlockRect(); + res = src->UnlockRect(); + return res; + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::CopyRects( + IDirect3DSurface8* pSourceSurface, + CONST RECT* pSourceRectsArray, + UINT cRects, + IDirect3DSurface8* pDestinationSurface, + CONST POINT* pDestPointsArray) { + + if (pSourceSurface == NULL || pDestinationSurface == NULL) { + return D3DERR_INVALIDCALL; + } + + // TODO: No format conversion, no stretching, no clipping. + // All src/dest rectangles must fit within the dest surface. + + Com src = static_cast(pSourceSurface); + Com dst = static_cast(pDestinationSurface); + + d3d9::D3DSURFACE_DESC srcDesc, dstDesc; + src->GetD3D9()->GetDesc(&srcDesc); + dst->GetD3D9()->GetDesc(&dstDesc); + + // If pSourceRectsArray is NULL, then the entire surface is copied + RECT rect; + POINT point = { 0, 0 }; + if (pSourceRectsArray == NULL) { + cRects = 1; + rect.top = rect.left = 0; + rect.right = srcDesc.Width; + rect.bottom = srcDesc.Height; + pSourceRectsArray = ▭ + + pDestPointsArray = &point; + } + + HRESULT res = D3DERR_INVALIDCALL; + + for (UINT i = 0; i < cRects; i++) { + + RECT srcRect, dstRect; + srcRect = pSourceRectsArray[i]; + + // True if the copy is asymmetric + bool asymmetric = true; + // True if the copy requires stretching (not technically supported) + bool stretch = true; + // True if the copy is not perfectly aligned (supported) + bool offset = true; + + if (pDestPointsArray != NULL) { + dstRect.left = pDestPointsArray[i].x; + dstRect.right = dstRect.left + (srcRect.right - srcRect.left); + dstRect.top = pDestPointsArray[i].y; + dstRect.bottom = dstRect.top + (srcRect.bottom - srcRect.top); + asymmetric = dstRect.left != srcRect.left || dstRect.top != srcRect.top + || dstRect.right != srcRect.right || dstRect.bottom != srcRect.bottom; + + stretch = (dstRect.right-dstRect.left) != (srcRect.right-srcRect.left) + || (dstRect.bottom-dstRect.top) != (srcRect.bottom-srcRect.top); + + offset = !stretch && asymmetric; + } else { + dstRect = srcRect; + asymmetric = stretch = offset = false; + } + + POINT dstPt = { dstRect.left, dstRect.top }; + + res = D3DERR_INVALIDCALL; + + switch (dstDesc.Pool) { + + // Dest: DEFAULT + case D3DPOOL_DEFAULT: + switch (srcDesc.Pool) { + case D3DPOOL_DEFAULT: { + // default -> default: use StretchRect + res = GetD3D9()->StretchRect( + src->GetD3D9(), + &srcRect, + dst->GetD3D9(), + &dstRect, + d3d9::D3DTEXF_NONE + ); + goto done; + } + case D3DPOOL_MANAGED: { + // MANAGED -> DEFAULT: UpdateTextureFromBuffer + res = m_bridge->UpdateTextureFromBuffer( + src->GetD3D9(), + dst->GetD3D9(), + &srcRect, + &dstPt + ); + goto done; + } + case D3DPOOL_SYSTEMMEM: { + // system mem -> default: use UpdateSurface + res = GetD3D9()->UpdateSurface( + src->GetD3D9(), + &srcRect, + dst->GetD3D9(), + &dstPt + ); + goto done; + } + case D3DPOOL_SCRATCH: + default: { + // TODO: Unhandled case. + goto unhandled; + } + } break; + + // Dest: MANAGED + case D3DPOOL_MANAGED: + switch (srcDesc.Pool) { + case D3DPOOL_DEFAULT: { + // TODO: (copy on GPU) + goto unhandled; + } + case D3DPOOL_MANAGED: + case D3DPOOL_SYSTEMMEM: { + // SYSTEMMEM -> MANAGED: LockRect / memcpy + + if (stretch) { + res = D3DERR_INVALIDCALL; + goto done; + } + + res = copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect); + + goto done; + } + case D3DPOOL_SCRATCH: + default: { + // TODO: Unhandled case. + goto unhandled; + } + } break; + + // DEST: SYSTEMMEM + case D3DPOOL_SYSTEMMEM: { + + // RT (DEFAULT) -> SYSTEMMEM: Use GetRenderTargetData as fast path if possible + if ((srcDesc.Usage & D3DUSAGE_RENDERTARGET || m_renderTarget == src)) { + + // GetRenderTargetData works if the formats and sizes match + if (srcDesc.MultiSampleType == d3d9::D3DMULTISAMPLE_NONE + && srcDesc.Width == dstDesc.Width + && srcDesc.Height == dstDesc.Height + && srcDesc.Format == dstDesc.Format + && !asymmetric) { + res = GetD3D9()->GetRenderTargetData(src->GetD3D9(), dst->GetD3D9()); + goto done; + } + } + + switch (srcDesc.Pool) { + case D3DPOOL_DEFAULT: { + // Get temporary off-screen surface for stretching. + Com pBlitImage = dst->GetBlitImage(); + + // Stretch the source RT to the temporary surface. + res = GetD3D9()->StretchRect( + src->GetD3D9(), + &srcRect, + pBlitImage.ptr(), + &dstRect, + d3d9::D3DTEXF_NONE); + if (FAILED(res)) { + goto done; + } + + // Now sync the rendertarget data into main memory. + res = GetD3D9()->GetRenderTargetData(pBlitImage.ptr(), dst->GetD3D9()); + goto done; + } + + // SYSMEM/MANAGED -> SYSMEM: LockRect / memcpy + case D3DPOOL_MANAGED: + case D3DPOOL_SYSTEMMEM: { + if (stretch) { + res = D3DERR_INVALIDCALL; + goto done; + } + + res = copyTextureBuffers(src.ptr(), dst.ptr(), srcDesc, dstDesc, srcRect, dstRect); + goto done; + } + case D3DPOOL_SCRATCH: + default: { + // TODO: Unhandled case. + goto unhandled; + } + } break; + } + + // DEST: SCRATCH + case D3DPOOL_SCRATCH: + default: { + // TODO: Unhandled case. + goto unhandled; + } + } + + unhandled: + Logger::debug(str::format("CopyRects: Hit unhandled case from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool)); + return D3DERR_INVALIDCALL; + + done: + if (FAILED(res)) { + Logger::debug(str::format("CopyRects: FAILED to copy from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool)); + return res; + } + + } + + return res; + + } +} \ No newline at end of file diff --git a/src/d3d8/d3d8_buffer.h b/src/d3d8/d3d8_buffer.h new file mode 100644 index 00000000..daf88eeb --- /dev/null +++ b/src/d3d8/d3d8_buffer.h @@ -0,0 +1,83 @@ +#pragma once + +// Implements IDirect3DVertexBuffer8 and IDirect3DIndexBuffer8 + +#include "d3d8_include.h" +#include "d3d8_resource.h" + +namespace dxvk { + + template + class D3D8Buffer : public D3D8Resource { + + public: + + D3D8Buffer( + D3D8DeviceEx* pDevice, + Com&& pBuffer) + : D3D8Resource ( pDevice, std::move(pBuffer) ) { + } + + HRESULT STDMETHODCALLTYPE Lock( + UINT OffsetToLock, + UINT SizeToLock, + BYTE** ppbData, + DWORD Flags) { + return this->GetD3D9()->Lock( + OffsetToLock, + SizeToLock, + reinterpret_cast(ppbData), + Flags); + } + + HRESULT STDMETHODCALLTYPE Unlock() { + return this->GetD3D9()->Unlock(); + } + + void STDMETHODCALLTYPE PreLoad() { + this->GetD3D9()->PreLoad(); + } + + }; + + + using D3D8VertexBufferBase = D3D8Buffer; + class D3D8VertexBuffer final : public D3D8VertexBufferBase { + + public: + + D3D8VertexBuffer( + D3D8DeviceEx* pDevice, + Com&& pBuffer) + : D3D8VertexBufferBase(pDevice, std::move(pBuffer)) { + } + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_VERTEXBUFFER; } + + HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) final { + // TODO: Remove reinterpret cast. + return GetD3D9()->GetDesc(reinterpret_cast(pDesc)); + } + + }; + + using D3D8IndexBufferBase = D3D8Buffer; + class D3D8IndexBuffer final : public D3D8IndexBufferBase { + + public: + + D3D8IndexBuffer( + D3D8DeviceEx* pDevice, + Com&& pBuffer) + : D3D8IndexBufferBase(pDevice, std::move(pBuffer)) { + } + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_INDEXBUFFER; } + + HRESULT STDMETHODCALLTYPE GetDesc(D3DINDEXBUFFER_DESC* pDesc) final { + // TODO: Remove reinterpret_cast. + return GetD3D9()->GetDesc(reinterpret_cast(pDesc)); + } + + }; +} \ No newline at end of file diff --git a/src/d3d8/d3d8_caps.h b/src/d3d8/d3d8_caps.h new file mode 100644 index 00000000..714c7432 --- /dev/null +++ b/src/d3d8/d3d8_caps.h @@ -0,0 +1,8 @@ +#pragma once + +namespace dxvk::d8caps { + + constexpr uint32_t MAX_TEXTURE_STAGES = 8; + constexpr uint32_t MAX_STREAMS = 16; + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_d3d9_util.h b/src/d3d8/d3d8_d3d9_util.h new file mode 100644 index 00000000..4374c3fb --- /dev/null +++ b/src/d3d8/d3d8_d3d9_util.h @@ -0,0 +1,136 @@ +#pragma once + +/** +* Utility functions for converting +* between DirectX8 and DirectX9 types. +*/ + +#include "d3d8_include.h" + +namespace dxvk { + + // (8<-9) D3DCAPSX: Writes to D3DCAPS8 from D3DCAPS9 + inline void ConvertCaps8(const d3d9::D3DCAPS9& caps9, D3DCAPS8* pCaps8) { + + // should be aligned + std::memcpy(pCaps8, &caps9, sizeof(D3DCAPS8)); + + // Max supported shader model is 1.4 + pCaps8->VertexShaderVersion = D3DVS_VERSION(1, 4); + pCaps8->PixelShaderVersion = D3DPS_VERSION(1, 4); + + // This was removed by D3D9. We can probably render windowed. + pCaps8->Caps2 |= D3DCAPS2_CANRENDERWINDOWED; + } + + // (9<-8) D3DD3DPRESENT_PARAMETERS: Returns D3D9's params given an input for D3D8 + inline d3d9::D3DPRESENT_PARAMETERS ConvertPresentParameters9(const D3DPRESENT_PARAMETERS* pParams) { + + d3d9::D3DPRESENT_PARAMETERS params; + params.BackBufferWidth = pParams->BackBufferWidth; + params.BackBufferHeight = pParams->BackBufferHeight; + params.BackBufferFormat = d3d9::D3DFORMAT(pParams->BackBufferFormat); + params.BackBufferCount = pParams->BackBufferCount; + + params.MultiSampleType = d3d9::D3DMULTISAMPLE_TYPE(pParams->MultiSampleType); + params.MultiSampleQuality = 0; // (D3D8: no MultiSampleQuality), TODO: get a value for this + + + UINT PresentationInterval = pParams->FullScreen_PresentationInterval; + + if (pParams->Windowed) { + + if (unlikely(PresentationInterval != D3DPRESENT_INTERVAL_DEFAULT)) { + // TODO: what does dx8 do if windowed app sets FullScreen_PresentationInterval? + Logger::warn(str::format( + "D3D8 Application is windowed yet requested FullScreen_PresentationInterval ", PresentationInterval, + " (should be D3DPRESENT_INTERVAL_DEFAULT). This will be ignored.")); + } + + // D3D8: For windowed swap chain, the back buffer is copied to the window immediately. + PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + } + + D3DSWAPEFFECT SwapEffect = pParams->SwapEffect; + + // D3DSWAPEFFECT_COPY_VSYNC has been removed + if (SwapEffect == D3DSWAPEFFECT_COPY_VSYNC) { + + SwapEffect = D3DSWAPEFFECT_COPY; + + // D3D8: In windowed mode, D3DSWAPEFFECT_COPY_VSYNC enables VSYNC. + // In fullscreen, D3DPRESENT_INTERVAL_IMMEDIATE is meaningless. + if (pParams->Windowed || (PresentationInterval & D3DPRESENT_INTERVAL_IMMEDIATE) != 0) { + PresentationInterval = D3DPRESENT_INTERVAL_ONE; + // TODO: what does dx8 do if multiple D3DPRESENT_INTERVAL flags are set? + } + } + + params.SwapEffect = d3d9::D3DSWAPEFFECT(SwapEffect); + params.hDeviceWindow = pParams->hDeviceWindow; + params.Windowed = pParams->Windowed; + params.EnableAutoDepthStencil = pParams->EnableAutoDepthStencil; + params.AutoDepthStencilFormat = d3d9::D3DFORMAT(pParams->AutoDepthStencilFormat); + params.Flags = pParams->Flags; + + params.FullScreen_RefreshRateInHz = pParams->FullScreen_RefreshRateInHz; + + // FullScreen_PresentationInterval -> PresentationInterval + params.PresentationInterval = PresentationInterval; + + return params; + } + + // Get bytes per pixel + inline UINT GetFormatStride(const D3DFORMAT fmt) { + // TODO: Get texture size based on format + return 4; + } + + // (8<-9) Convert D3DSURFACE_DESC + inline void ConvertSurfaceDesc8(const d3d9::D3DSURFACE_DESC* pSurf9, D3DSURFACE_DESC* pSurf8) { + pSurf8->Format = D3DFORMAT(pSurf9->Format); + pSurf8->Type = D3DRESOURCETYPE(pSurf9->Type); + pSurf8->Usage = pSurf9->Usage; + pSurf8->Pool = D3DPOOL(pSurf9->Pool); + pSurf8->Size = pSurf9->Width * pSurf9->Height * GetFormatStride(pSurf8->Format); + + pSurf8->MultiSampleType = D3DMULTISAMPLE_TYPE(pSurf9->MultiSampleType); + // DX8: No multisample quality + pSurf8->Width = pSurf9->Width; + pSurf8->Height = pSurf9->Height; + } + + // (8<-9) Convert D3DVOLUME_DESC + inline void ConvertVolumeDesc8(const d3d9::D3DVOLUME_DESC* pVol9, D3DVOLUME_DESC* pVol8) { + pVol8->Format = D3DFORMAT(pVol9->Format); + pVol8->Type = D3DRESOURCETYPE(pVol9->Type); + pVol8->Usage = pVol9->Usage; + pVol8->Pool = D3DPOOL(pVol9->Pool); + pVol8->Size = pVol9->Width * pVol9->Height * pVol9->Depth * GetFormatStride(pVol8->Format); + pVol8->Width = pVol9->Width; + pVol8->Height = pVol9->Height; + pVol8->Depth = pVol9->Depth; + } + + // If this D3DTEXTURESTAGESTATETYPE has been remapped to a d3d9::D3DSAMPLERSTATETYPE + // it will be returned, otherwise returns -1 + inline d3d9::D3DSAMPLERSTATETYPE GetSamplerStateType9(const D3DTEXTURESTAGESTATETYPE StageType) { + switch (StageType) { + // 13-21: + case D3DTSS_ADDRESSU: return d3d9::D3DSAMP_ADDRESSU; + case D3DTSS_ADDRESSV: return d3d9::D3DSAMP_ADDRESSV; + case D3DTSS_BORDERCOLOR: return d3d9::D3DSAMP_BORDERCOLOR; + case D3DTSS_MAGFILTER: return d3d9::D3DSAMP_MAGFILTER; + case D3DTSS_MINFILTER: return d3d9::D3DSAMP_MINFILTER; + case D3DTSS_MIPFILTER: return d3d9::D3DSAMP_MIPFILTER; + case D3DTSS_MIPMAPLODBIAS: return d3d9::D3DSAMP_MIPMAPLODBIAS; + case D3DTSS_MAXMIPLEVEL: return d3d9::D3DSAMP_MIPFILTER; + case D3DTSS_MAXANISOTROPY: return d3d9::D3DSAMP_MAXANISOTROPY; + // 25: + case D3DTSS_ADDRESSW: return d3d9::D3DSAMP_ADDRESSW; + default: return d3d9::D3DSAMPLERSTATETYPE(-1); + } + } +} + diff --git a/src/d3d8/d3d8_device.cpp b/src/d3d8/d3d8_device.cpp new file mode 100644 index 00000000..7c12fc7c --- /dev/null +++ b/src/d3d8/d3d8_device.cpp @@ -0,0 +1,553 @@ +#include "d3d8_device.h" + +#include "d3d8_interface.h" +#include "d3d8_shader.h" + +#include "../../version.h" + +#ifdef MSC_VER +#pragma fenv_access (on) +#endif + +namespace dxvk { + + constexpr DWORD isFVF(DWORD Handle) { + return (Handle & D3DFVF_RESERVED0) == 0; + } + + constexpr DWORD getShaderHandle(DWORD Index) { + return (Index << 1) | D3DFVF_RESERVED0; + } + + constexpr DWORD getShaderIndex(DWORD Handle) { + if ((Handle & D3DFVF_RESERVED0) != 0) { + return (Handle & ~(D3DFVF_RESERVED0)) >> 1; + } else { + return Handle; + } + } + + struct D3D8VertexShaderInfo { + // Vertex Shader + d3d9::IDirect3DVertexDeclaration9* pVertexDecl = nullptr; + d3d9::IDirect3DVertexShader9* pVertexShader = nullptr; + std::vector declaration; + std::vector function; + }; + + D3D8DeviceEx::D3D8DeviceEx( + D3D8InterfaceEx* pParent, + Com&& pDevice, + //D3D8Adapter* pAdapter, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS* pParams) + : D3D8DeviceBase(std::move(pDevice)) + , m_bridge(GetD3D9Bridge(GetD3D9())) + , m_d3d8Options(pParent->GetOptions()) + , m_parent(pParent) + , m_presentParams(*pParams) + , m_deviceType(DeviceType) + , m_window(hFocusWindow) + , m_behaviorFlags(BehaviorFlags) { + + if constexpr (DXVK_VERSION[sizeof(DXVK_VERSION)-2] == '+') { + m_bridge->SetAPIName("D3D8 (" __DATE__ ", " __TIME__ ")"); + } else { + m_bridge->SetAPIName("D3D8"); + } + + // Shadow buffers are implemented by scaling + // depth test reference values and applying a 2x2 PCF. + m_bridge->SetShadowBuffersEnabled(m_d3d8Options.useShadowBuffers); + + // D3D8 Render states that aren't remapped + // but should still be recorded by D3D9 + m_bridge->AddSupportedRenderStates( + D3DRS_LINEPATTERN, + D3DRS_ZVISIBLE, + D3DRS_PATCHSEGMENTS + ); + + // Mirrors how D3D9 handles the BackBufferCount + m_presentParams.BackBufferCount = std::max(m_presentParams.BackBufferCount, 1u); + + m_textures.fill(nullptr); + m_streams.fill(D3D8VBO()); + + ResetState(); + } + + + D3D8DeviceEx::~D3D8DeviceEx() { + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::GetInfo(DWORD DevInfoID, void* pDevInfoStruct, DWORD DevInfoStructSize) { + Logger::debug(str::format("D3D8DeviceEx::GetInfo: ", DevInfoID)); + + if (unlikely(pDevInfoStruct == nullptr || DevInfoStructSize == 0)) + return D3DERR_INVALIDCALL; + + HRESULT res; + d3d9::IDirect3DQuery9* pQuery = nullptr; + + switch (DevInfoID) { + // pre-DX8 queries + case 0: + case D3DDEVINFOID_TEXTUREMANAGER: + case D3DDEVINFOID_D3DTEXTUREMANAGER: + case D3DDEVINFOID_TEXTURING: + return E_FAIL; + + case D3DDEVINFOID_VCACHE: + // Docs say response should be S_FALSE, but we'll let D9VK + // decide based on the value of supportVCache. D3D8Ex calls this. + res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_VCACHE, &pQuery); + break; + case D3DDEVINFOID_RESOURCEMANAGER: + // May not be implemented by D9VK. + res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_RESOURCEMANAGER, &pQuery); + break; + case D3DDEVINFOID_VERTEXSTATS: + res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_VERTEXSTATS, &pQuery); + break; + + default: + Logger::warn(str::format("D3D8DeviceEx::GetInfo: Unsupported device info ID: ", DevInfoID)); + return E_FAIL; + } + + if (unlikely(FAILED(res))) + goto done; + + // Immediately issue the query. + // D3D9 will begin it automatically before ending. + res = pQuery->Issue(D3DISSUE_END); + if (unlikely(FAILED(res))) { + goto done; + } + + // TODO: Will immediately issuing the query without doing any API calls + // actually yield meaingful results? And should we flush or let it mellow? + res = pQuery->GetData(pDevInfoStruct, DevInfoStructSize, D3DGETDATA_FLUSH); + + done: + if (pQuery != nullptr) + pQuery->Release(); + + if (unlikely(FAILED(res))) { + if (res == D3DERR_NOTAVAILABLE) // unsupported + return E_FAIL; + else // any unknown error + return S_FALSE; + } + return res; + } + + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::TestCooperativeLevel() { + // Equivelant of D3D11/DXGI present tests. We can always present. + return D3D_OK; + } + + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::GetDirect3D(IDirect3D8** ppD3D8) { + if (ppD3D8 == nullptr) + return D3DERR_INVALIDCALL; + + *ppD3D8 = m_parent.ref(); + return D3D_OK; + } + + // Render States // + + // ZBIAS can be an integer from 0 to 1 and needs to be remapped to float + static constexpr float ZBIAS_SCALE = -0.000005f; + static constexpr float ZBIAS_SCALE_INV = 1 / ZBIAS_SCALE; + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) { + d3d9::D3DRENDERSTATETYPE State9 = (d3d9::D3DRENDERSTATETYPE)State; + + switch (State) { + // Most render states translate 1:1 to D3D9 + default: + break; + + // TODO: D3DRS_LINEPATTERN - vkCmdSetLineRasterizationModeEXT + case D3DRS_LINEPATTERN: { + [[maybe_unused]] + D3DLINEPATTERN pattern = bit::cast(Value); + m_bridge->RenderStateNotSupported(D3DRS_LINEPATTERN); + } break; + + // Not supported by D3D8. + case D3DRS_ZVISIBLE: + break; + + // TODO: Not implemented by D9VK. Try anyway. + case D3DRS_EDGEANTIALIAS: + State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE; + break; + + case D3DRS_ZBIAS: + State9 = d3d9::D3DRS_DEPTHBIAS; + Value = bit::cast(float(Value) * ZBIAS_SCALE); + break; + + case D3DRS_SOFTWAREVERTEXPROCESSING: + // This was a very easy footgun for D3D8 applications. + if (unlikely(ShouldRecord())) + return m_recorder->SetSoftwareVertexProcessing(Value); + + return GetD3D9()->SetSoftwareVertexProcessing(Value); + + // TODO: D3DRS_PATCHSEGMENTS + case D3DRS_PATCHSEGMENTS: + m_bridge->RenderStateNotSupported(D3DRS_PATCHSEGMENTS); + break; + + } + + return GetD3D9()->SetRenderState(State9, Value); + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue) { + d3d9::D3DRENDERSTATETYPE State9 = (d3d9::D3DRENDERSTATETYPE)State; + + switch (State) { + // Most render states translate 1:1 to D3D9 + default: + break; + + // TODO: D3DRS_LINEPATTERN + case D3DRS_LINEPATTERN: + break; + + // Not supported by D3D8. + case D3DRS_ZVISIBLE: + break; + + case D3DRS_EDGEANTIALIAS: + State9 = d3d9::D3DRS_ANTIALIASEDLINEENABLE; + break; + + case D3DRS_ZBIAS: { + float bias = 0; + HRESULT res = GetD3D9()->GetRenderState(d3d9::D3DRS_DEPTHBIAS, (DWORD*)&bias); + *pValue = bit::cast(bias * ZBIAS_SCALE_INV); + return res; + } break; + + case D3DRS_SOFTWAREVERTEXPROCESSING: + return GetD3D9()->GetSoftwareVertexProcessing(); + + // TODO: D3DRS_PATCHSEGMENTS + case D3DRS_PATCHSEGMENTS: + break; + } + + return GetD3D9()->GetRenderState(State9, pValue); + } + + // Vertex Shaders // + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::CreateVertexShader( + const DWORD* pDeclaration, + const DWORD* pFunction, + DWORD* pHandle, + DWORD Usage ) { + + + D3D8VertexShaderInfo& info = m_vertexShaders.emplace_back(); + + // Store D3D8 bytecodes in the shader info + if (pDeclaration != nullptr) + for (UINT i = 0; pDeclaration[i+1] != D3DVSD_END(); i++) + info.declaration.push_back(pDeclaration[i]); + + if (pFunction != nullptr) + for (UINT i = 0; pFunction[i+1] != D3DVS_END(); i++) + info.function.push_back(pFunction[i]); + + D3D9VertexShaderCode result = translateVertexShader8(pDeclaration, pFunction); + + // Create vertex declaration + HRESULT res = GetD3D9()->CreateVertexDeclaration(result.declaration, &(info.pVertexDecl)); + if (FAILED(res)) + return res; + + if (pFunction != nullptr) { + res = GetD3D9()->CreateVertexShader(result.function.data(), &(info.pVertexShader)); + } else { + // pFunction is NULL: fixed function pipeline + info.pVertexShader = nullptr; + } + + // Set bit to indicate this is not an FVF + *pHandle = getShaderHandle(m_vertexShaders.size() - 1); + + return res; + } + + inline D3D8VertexShaderInfo* getVertexShaderInfo(D3D8DeviceEx* device, DWORD Handle) { + + Handle = getShaderIndex(Handle); + + if (unlikely(Handle >= device->m_vertexShaders.size())) { + Logger::err(str::format("getVertexShaderInfo: Invalid vertex shader index ", std::hex, Handle)); + return nullptr; + } + + D3D8VertexShaderInfo& info = device->m_vertexShaders[Handle]; + + if (unlikely(!info.pVertexDecl && !info.pVertexShader)) { + Logger::err(str::format("getVertexShaderInfo: Application provided deleted vertex shader ", std::hex, Handle)); + return nullptr; + } + + return &info; + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::SetVertexShader( DWORD Handle ) { + + if (unlikely(ShouldRecord())) { + return m_recorder->SetVertexShader(Handle); + } + + // Check for extra bit that indicates this is not an FVF + if (!isFVF(Handle)) { + + D3D8VertexShaderInfo* info = getVertexShaderInfo(this, Handle); + + if (!info) + return D3DERR_INVALIDCALL; + + // Cache current shader + m_currentVertexShader = Handle; + + GetD3D9()->SetVertexDeclaration(info->pVertexDecl); + return GetD3D9()->SetVertexShader(info->pVertexShader); + + } else { + + // Cache current FVF + m_currentVertexShader = Handle; + + //GetD3D9()->SetVertexDeclaration(nullptr); + GetD3D9()->SetVertexShader(nullptr); + return GetD3D9()->SetFVF( Handle ); + } + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::GetVertexShader(DWORD* pHandle) { + + // Return cached shader + *pHandle = m_currentVertexShader; + + return D3D_OK; + + /* + // Slow path. Use to debug cached shader validation. // + + d3d9::IDirect3DVertexShader9* pVertexShader; + HRESULT res = GetD3D9()->GetVertexShader(&pVertexShader); + + if (FAILED(res) || pVertexShader == nullptr) { + return GetD3D9()->GetFVF(pHandle); + } + + for (unsigned int i = 0; i < m_vertexShaders.size(); i++) { + D3D8VertexShaderInfo& info = m_vertexShaders[i]; + + if (info.pVertexShader == pVertexShader) { + *pHandle = getShaderHandle(DWORD(i)); + return res; + } + } + + return res; + */ + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::DeleteVertexShader(DWORD Handle) { + + if (!isFVF(Handle)) { + + D3D8VertexShaderInfo* info = getVertexShaderInfo(this, Handle); + + if (!info) + return D3DERR_INVALIDCALL; + + SAFE_RELEASE(info->pVertexDecl); + SAFE_RELEASE(info->pVertexShader); + + info->declaration.clear(); + info->function.clear(); + } + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData) { + D3D8VertexShaderInfo* pInfo = getVertexShaderInfo(this, Handle); + + if (unlikely(!pInfo)) + return D3DERR_INVALIDCALL; + + UINT SizeOfData = *pSizeOfData; + + // Get actual size + UINT ActualSize = pInfo->declaration.size() * sizeof(DWORD); + + if (pData == nullptr) { + *pSizeOfData = ActualSize; + return D3D_OK; + } + + // D3D8-specific behavior + if (SizeOfData < ActualSize) { + *pSizeOfData = ActualSize; + return D3DERR_MOREDATA; + } + + memcpy(pData, pInfo->declaration.data(), ActualSize); + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) { + D3D8VertexShaderInfo* pInfo = getVertexShaderInfo(this, Handle); + + if (unlikely(!pInfo)) + return D3DERR_INVALIDCALL; + + UINT SizeOfData = *pSizeOfData; + + // Get actual size + UINT ActualSize = pInfo->function.size() * sizeof(DWORD); + + if (pData == nullptr) { + *pSizeOfData = ActualSize; + return D3D_OK; + } + + // D3D8-specific behavior + if (SizeOfData < ActualSize) { + *pSizeOfData = ActualSize; + return D3DERR_MOREDATA; + } + + memcpy(pData, pInfo->function.data(), ActualSize); + return D3D_OK; + + } + + // Pixel Shaders // + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::CreatePixelShader( + const DWORD* pFunction, + DWORD* pHandle) { + + d3d9::IDirect3DPixelShader9* pPixelShader; + + HRESULT res = GetD3D9()->CreatePixelShader(pFunction, &pPixelShader); + + m_pixelShaders.push_back(pPixelShader); + + // Still set the shader bit, to prevent conflicts with NULL. + *pHandle = getShaderHandle(m_pixelShaders.size() - 1); + + return res; + } + + inline d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8DeviceEx* device, DWORD Handle) { + + Handle = getShaderIndex(Handle); + + if (unlikely(Handle >= device->m_pixelShaders.size())) { + Logger::err(str::format("getPixelShaderPtr: Invalid pixel shader index ", std::hex, Handle)); + return nullptr; + } + + d3d9::IDirect3DPixelShader9* pPixelShader = device->m_pixelShaders[Handle]; + + if (unlikely(pPixelShader == nullptr)) { + Logger::err(str::format("getPixelShaderPtr: Application provided deleted pixel shader ", std::hex, Handle)); + return nullptr; + } + + return pPixelShader; + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::SetPixelShader(DWORD Handle) { + + if (unlikely(ShouldRecord())) { + return m_recorder->SetPixelShader(Handle); + } + + if (Handle == DWORD(NULL)) { + return GetD3D9()->SetPixelShader(nullptr); + } + + d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle); + + if (unlikely(!pPixelShader)) { + return D3DERR_INVALIDCALL; + } + + // Cache current pixel shader + m_currentPixelShader = Handle; + + return GetD3D9()->SetPixelShader(pPixelShader); + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::GetPixelShader(DWORD* pHandle) { + // Return cached shader + *pHandle = m_currentPixelShader; + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::DeletePixelShader(DWORD Handle) { + + d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle); + + if (unlikely(!pPixelShader)) { + return D3DERR_INVALIDCALL; + } + + SAFE_RELEASE(pPixelShader); + + m_pixelShaders[getShaderIndex(Handle)] = nullptr; + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE D3D8DeviceEx::GetPixelShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData) { + + d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle); + + if (unlikely(!pPixelShader)) + return D3DERR_INVALIDCALL; + + UINT SizeOfData = *pSizeOfData; + + // Get actual size + UINT ActualSize = 0; + pPixelShader->GetFunction(nullptr, &ActualSize); + + if (pData == nullptr) { + *pSizeOfData = ActualSize; + return D3D_OK; + } + + // D3D8-specific behavior + if (SizeOfData < ActualSize) { + *pSizeOfData = ActualSize; + return D3DERR_MOREDATA; + } + + return pPixelShader->GetFunction(pData, &SizeOfData); + } + +} // namespace dxvk diff --git a/src/d3d8/d3d8_device.h b/src/d3d8/d3d8_device.h new file mode 100644 index 00000000..57a3880c --- /dev/null +++ b/src/d3d8/d3d8_device.h @@ -0,0 +1,877 @@ +#pragma once + +// Implements IDirect3DDevice8 + +#include "d3d8_include.h" +#include "d3d8_texture.h" +#include "d3d8_buffer.h" +#include "d3d8_swapchain.h" +#include "d3d8_state_block.h" +#include "d3d8_d3d9_util.h" +#include "d3d8_caps.h" + +#include "../d3d9/d3d9_bridge.h" + +#include +#include +#include +#include + +namespace dxvk { + + class D3D8InterfaceEx; + class D3D8SwapChainEx; + + struct D3D8Options; + struct D3D8VertexShaderInfo; + + using D3D8DeviceBase = D3D8WrappedObject; + class D3D8DeviceEx final : public D3D8DeviceBase { + + friend class D3D8SwapChainEx; + friend class D3D8StateBlock; + public: + + D3D8DeviceEx( + D3D8InterfaceEx* pParent, + Com&& pDevice, + //D3D8Adapter* pAdapter, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS* pParams); + + ~D3D8DeviceEx(); + + /* Direct3D 8 Exclusive Methods */ + + HRESULT STDMETHODCALLTYPE CopyRects( + IDirect3DSurface8* pSourceSurface, + CONST RECT* pSourceRectsArray, + UINT cRects, + IDirect3DSurface8* pDestinationSurface, + CONST POINT* pDestPointsArray); + + HRESULT STDMETHODCALLTYPE GetPixelShaderConstant (DWORD Register, void* pConstantData, DWORD ConstantCount) { + return GetD3D9()->GetPixelShaderConstantF(Register, (float*)pConstantData, ConstantCount); + } + + HRESULT STDMETHODCALLTYPE GetVertexShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount) { + return GetD3D9()->GetVertexShaderConstantF(Register, (float*)pConstantData, ConstantCount); + } + + HRESULT STDMETHODCALLTYPE GetPixelShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData); + HRESULT STDMETHODCALLTYPE GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData); + HRESULT STDMETHODCALLTYPE GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData); + + HRESULT STDMETHODCALLTYPE GetInfo(DWORD DevInfoID, void* pDevInfoStruct, DWORD DevInfoStructSize); + + HRESULT STDMETHODCALLTYPE TestCooperativeLevel(); + + UINT STDMETHODCALLTYPE GetAvailableTextureMem() { return GetD3D9()->GetAvailableTextureMem(); } + + HRESULT STDMETHODCALLTYPE ResourceManagerDiscardBytes(DWORD bytes) { + return GetD3D9()->EvictManagedResources(); + } + + HRESULT STDMETHODCALLTYPE GetDirect3D(IDirect3D8** ppD3D8); + + HRESULT STDMETHODCALLTYPE GetDeviceCaps(D3DCAPS8* pCaps) { + d3d9::D3DCAPS9 caps9; + HRESULT res = GetD3D9()->GetDeviceCaps(&caps9); + dxvk::ConvertCaps8(caps9, pCaps); + return res; + } + + HRESULT STDMETHODCALLTYPE GetDisplayMode(D3DDISPLAYMODE* pMode) { + // swap chain 0 + return GetD3D9()->GetDisplayMode(0, (d3d9::D3DDISPLAYMODE*)pMode); + } + + HRESULT STDMETHODCALLTYPE GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters) { + return GetD3D9()->GetCreationParameters((d3d9::D3DDEVICE_CREATION_PARAMETERS*)pParameters); + } + + HRESULT STDMETHODCALLTYPE SetCursorProperties( + UINT XHotSpot, + UINT YHotSpot, + IDirect3DSurface8* pCursorBitmap) { + + D3D8Surface* surf = static_cast(pCursorBitmap); + return GetD3D9()->SetCursorProperties(XHotSpot, YHotSpot, D3D8Surface::GetD3D9Nullable(surf)); + } + + void STDMETHODCALLTYPE SetCursorPosition(UINT XScreenSpace, UINT YScreenSpace, DWORD Flags) { + // TODO: do we need to convert from screenspace? + //GetD3D9()->SetCursorPosition(XScreenSpace, YScreenSpace, Flags); + } + + // Microsoft d3d8.h in the DirectX 9 SDK uses a different function signature... + void STDMETHODCALLTYPE SetCursorPosition(int X, int Y, DWORD Flags) { + // TODO: do we need to convert from screenspace? + //GetD3D9()->SetCursorPosition(X, Y, Flags); + } + + BOOL STDMETHODCALLTYPE ShowCursor(BOOL bShow) { return GetD3D9()->ShowCursor(bShow); } + + HRESULT STDMETHODCALLTYPE CreateAdditionalSwapChain( + D3DPRESENT_PARAMETERS* pPresentationParameters, + IDirect3DSwapChain8** ppSwapChain) { + + Com pSwapChain9; + d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters); + HRESULT res = GetD3D9()->CreateAdditionalSwapChain( + ¶ms, + &pSwapChain9 + ); + + *ppSwapChain = ref(new D3D8SwapChain(this, std::move(pSwapChain9))); + + return res; + } + + + HRESULT STDMETHODCALLTYPE Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) { + m_presentParams = *pPresentationParameters; + ResetState(); + + d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters); + return GetD3D9()->Reset(¶ms); + } + + HRESULT STDMETHODCALLTYPE Present( + const RECT* pSourceRect, + const RECT* pDestRect, + HWND hDestWindowOverride, + const RGNDATA* pDirtyRegion) { + return GetD3D9()->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); + } + + HRESULT STDMETHODCALLTYPE GetBackBuffer( + UINT iBackBuffer, + D3DBACKBUFFER_TYPE Type, + IDirect3DSurface8** ppBackBuffer) { + + if (iBackBuffer < m_backBuffers.size() || m_backBuffers[iBackBuffer] == nullptr) { + Com pSurface9; + HRESULT res = GetD3D9()->GetBackBuffer(0, iBackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9); + + if (FAILED(res)) return res; + + m_backBuffers[iBackBuffer] = ref(new D3D8Surface(this, std::move(pSurface9))); + *ppBackBuffer = m_backBuffers[iBackBuffer].ref(); + + return res; + } + + *ppBackBuffer = m_backBuffers[iBackBuffer].ref(); + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE GetRasterStatus(D3DRASTER_STATUS* pRasterStatus) { + return GetD3D9()->GetRasterStatus(0, (d3d9::D3DRASTER_STATUS*)pRasterStatus); + } + + void STDMETHODCALLTYPE SetGammaRamp(DWORD Flags, const D3DGAMMARAMP* pRamp) { + // For swap chain 0 + GetD3D9()->SetGammaRamp(0, Flags, reinterpret_cast(pRamp)); + } + + void STDMETHODCALLTYPE GetGammaRamp(D3DGAMMARAMP* pRamp) { + // For swap chain 0 + GetD3D9()->GetGammaRamp(0, reinterpret_cast(pRamp)); + } + + HRESULT STDMETHODCALLTYPE CreateTexture( + UINT Width, + UINT Height, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DTexture8** ppTexture) { + InitReturnPtr(ppTexture); + + Com pTex9 = nullptr; + HRESULT res = GetD3D9()->CreateTexture( + Width, + Height, + Levels, + Usage, + d3d9::D3DFORMAT(Format), + d3d9::D3DPOOL(Pool), + &pTex9, + NULL); + + if (FAILED(res)) + return res; + + *ppTexture = ref(new D3D8Texture2D(this, std::move(pTex9))); + + return res; + } + + HRESULT STDMETHODCALLTYPE CreateVolumeTexture( + UINT Width, + UINT Height, + UINT Depth, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DVolumeTexture8** ppVolumeTexture) { + Com pVolume9 = nullptr; + HRESULT res = GetD3D9()->CreateVolumeTexture( + Width, Height, Depth, Levels, + Usage, + d3d9::D3DFORMAT(Format), + d3d9::D3DPOOL(Pool), + &pVolume9, + NULL); + + *ppVolumeTexture = ref(new D3D8Texture3D(this, std::move(pVolume9))); + + return res; + + } + + HRESULT STDMETHODCALLTYPE CreateCubeTexture( + UINT EdgeLength, + UINT Levels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DCubeTexture8** ppCubeTexture) { + Com pCube9 = nullptr; + HRESULT res = GetD3D9()->CreateCubeTexture( + EdgeLength, + Levels, + Usage, + d3d9::D3DFORMAT(Format), + d3d9::D3DPOOL(Pool), + &pCube9, + NULL); + + *ppCubeTexture = ref(new D3D8TextureCube(this, std::move(pCube9))); + + return res; + } + + HRESULT STDMETHODCALLTYPE CreateVertexBuffer( + UINT Length, + DWORD Usage, + DWORD FVF, + D3DPOOL Pool, + IDirect3DVertexBuffer8** ppVertexBuffer) { + + Com pVertexBuffer9 = nullptr; + HRESULT res = GetD3D9()->CreateVertexBuffer(Length, Usage, FVF, d3d9::D3DPOOL(Pool), &pVertexBuffer9, NULL); + *ppVertexBuffer = ref(new D3D8VertexBuffer(this, std::move(pVertexBuffer9))); + return res; + } + + HRESULT STDMETHODCALLTYPE CreateIndexBuffer( + UINT Length, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + IDirect3DIndexBuffer8** ppIndexBuffer) { + Com pIndexBuffer9 = nullptr; + HRESULT res = GetD3D9()->CreateIndexBuffer(Length, Usage, d3d9::D3DFORMAT(Format), d3d9::D3DPOOL(Pool), &pIndexBuffer9, NULL); + *ppIndexBuffer = ref(new D3D8IndexBuffer(this, std::move(pIndexBuffer9))); + return res; + + } + + HRESULT STDMETHODCALLTYPE CreateRenderTarget( + UINT Width, + UINT Height, + D3DFORMAT Format, + D3DMULTISAMPLE_TYPE MultiSample, + BOOL Lockable, + IDirect3DSurface8** ppSurface) { + Com pSurf9 = nullptr; + HRESULT res = GetD3D9()->CreateRenderTarget( + Width, + Height, + d3d9::D3DFORMAT(Format), + d3d9::D3DMULTISAMPLE_TYPE(MultiSample), + 0, // TODO: CreateRenderTarget MultisampleQuality + Lockable, + &pSurf9, + NULL); + + *ppSurface = ref(new D3D8Surface(this, std::move(pSurf9))); + + return res; + } + + HRESULT STDMETHODCALLTYPE CreateDepthStencilSurface( + UINT Width, + UINT Height, + D3DFORMAT Format, + D3DMULTISAMPLE_TYPE MultiSample, + IDirect3DSurface8** ppSurface) { + Com pSurf9 = nullptr; + HRESULT res = GetD3D9()->CreateDepthStencilSurface( + Width, + Height, + d3d9::D3DFORMAT(Format), + d3d9::D3DMULTISAMPLE_TYPE(MultiSample), + 0, // TODO: CreateDepthStencilSurface MultisampleQuality + true, // TODO: CreateDepthStencilSurface Discard + &pSurf9, + NULL); + + *ppSurface = ref(new D3D8Surface(this, std::move(pSurf9))); + + return res; + } + + HRESULT STDMETHODCALLTYPE UpdateTexture( + IDirect3DBaseTexture8* pSourceTexture, + IDirect3DBaseTexture8* pDestinationTexture) { + D3D8Texture2D* src = static_cast(pSourceTexture); + D3D8Texture2D* dst = static_cast(pDestinationTexture); + + return GetD3D9()->UpdateTexture(D3D8Texture2D::GetD3D9Nullable(src), D3D8Texture2D::GetD3D9Nullable(dst)); + } + + HRESULT STDMETHODCALLTYPE GetFrontBuffer(IDirect3DSurface8* pDestSurface) { + if (unlikely(pDestSurface == nullptr)) + return D3DERR_INVALIDCALL; + Com surf = static_cast(pDestSurface); + // This actually gets a copy of the front buffer and writes it to pDestSurface + return GetD3D9()->GetFrontBufferData(0, D3D8Surface::GetD3D9Nullable(surf)); + } + + // CreateImageSurface -> CreateOffscreenPlainSurface + HRESULT STDMETHODCALLTYPE CreateImageSurface(UINT Width, UINT Height, D3DFORMAT Format, IDirect3DSurface8** ppSurface) { + + Com pSurf = nullptr; + HRESULT res = GetD3D9()->CreateOffscreenPlainSurface( + Width, + Height, + d3d9::D3DFORMAT(Format), + // FIXME: D3DPOOL_SCRATCH is said to be dx8 compatible, but currently won't work with CopyRects + d3d9::D3DPOOL_SYSTEMMEM, + &pSurf, + NULL); + + *ppSurface = ref(new D3D8Surface(this, std::move(pSurf))); + + return res; + } + + HRESULT STDMETHODCALLTYPE SetRenderTarget(IDirect3DSurface8* pRenderTarget, IDirect3DSurface8* pNewZStencil) { + HRESULT res; + + if (pRenderTarget != NULL) { + D3D8Surface* surf = static_cast(pRenderTarget); + res = GetD3D9()->SetRenderTarget(0, surf->GetD3D9()); + + if (FAILED(res)) + return res; + + // FIXME: Doing this crashes a GTA Vice City trace + // and possibly other things too. Investigate. + //m_renderTarget = ref(surf); + } + + // SetDepthStencilSurface is a separate call + D3D8Surface* zStencil = static_cast(pNewZStencil); + res = GetD3D9()->SetDepthStencilSurface(D3D8Surface::GetD3D9Nullable(zStencil)); + + return res; + } + + HRESULT STDMETHODCALLTYPE GetRenderTarget(IDirect3DSurface8** ppRenderTarget) { + + if (unlikely(m_renderTarget == nullptr)) { + Com pRT9 = nullptr; + HRESULT res = GetD3D9()->GetRenderTarget(0, &pRT9); // use RT index 0 + + m_renderTarget = new D3D8Surface(this, std::move(pRT9)); + + *ppRenderTarget = m_renderTarget.ref(); + return res; + } + + *ppRenderTarget = m_renderTarget.ref(); + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface) { + + if (unlikely(m_depthStencil == nullptr)) { + Com pStencil9 = nullptr; + HRESULT res = GetD3D9()->GetDepthStencilSurface(&pStencil9); + + m_depthStencil = new D3D8Surface(this, std::move(pStencil9)); + + *ppZStencilSurface = m_depthStencil.ref(); + return res; + } + + *ppZStencilSurface = m_depthStencil.ref(); + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE BeginScene() { return GetD3D9()->BeginScene(); } + + HRESULT STDMETHODCALLTYPE EndScene() { return GetD3D9()->EndScene(); } + + HRESULT STDMETHODCALLTYPE Clear( + DWORD Count, + const D3DRECT* pRects, + DWORD Flags, + D3DCOLOR Color, + float Z, + DWORD Stencil) { + return GetD3D9()->Clear(Count, pRects, Flags, Color, Z, Stencil); + } + + HRESULT STDMETHODCALLTYPE SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix) { + return GetD3D9()->SetTransform(d3d9::D3DTRANSFORMSTATETYPE(State), pMatrix); + } + + HRESULT STDMETHODCALLTYPE GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) { + return GetD3D9()->GetTransform(d3d9::D3DTRANSFORMSTATETYPE(State), pMatrix); + } + + HRESULT STDMETHODCALLTYPE MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix) { + return GetD3D9()->MultiplyTransform(d3d9::D3DTRANSFORMSTATETYPE(TransformState), pMatrix); + } + + HRESULT STDMETHODCALLTYPE SetViewport(const D3DVIEWPORT8* pViewport) { + return GetD3D9()->SetViewport(reinterpret_cast(pViewport)); + } + + HRESULT STDMETHODCALLTYPE GetViewport(D3DVIEWPORT8* pViewport) { + return GetD3D9()->GetViewport(reinterpret_cast(pViewport)); + } + + HRESULT STDMETHODCALLTYPE SetMaterial(const D3DMATERIAL8* pMaterial) { + return GetD3D9()->SetMaterial((const d3d9::D3DMATERIAL9*)pMaterial); + } + + HRESULT STDMETHODCALLTYPE GetMaterial(D3DMATERIAL8* pMaterial) { + return GetD3D9()->GetMaterial((d3d9::D3DMATERIAL9*)pMaterial); + } + + HRESULT STDMETHODCALLTYPE SetLight(DWORD Index, const D3DLIGHT8* pLight) { + return GetD3D9()->SetLight(Index, (const d3d9::D3DLIGHT9*)pLight); + } + + HRESULT STDMETHODCALLTYPE GetLight(DWORD Index, D3DLIGHT8* pLight) { + return GetD3D9()->GetLight(Index, (d3d9::D3DLIGHT9*)pLight); + } + + HRESULT STDMETHODCALLTYPE LightEnable(DWORD Index, BOOL Enable) { + return GetD3D9()->LightEnable(Index, Enable); + } + + HRESULT STDMETHODCALLTYPE GetLightEnable(DWORD Index, BOOL* pEnable) { + return GetD3D9()->GetLightEnable(Index, pEnable); + } + + HRESULT STDMETHODCALLTYPE SetClipPlane(DWORD Index, const float* pPlane) { + return GetD3D9()->SetClipPlane(Index, pPlane); + } + + HRESULT STDMETHODCALLTYPE GetClipPlane(DWORD Index, float* pPlane) { + return GetD3D9()->GetClipPlane(Index, pPlane); + } + + HRESULT STDMETHODCALLTYPE SetRenderState(D3DRENDERSTATETYPE State, DWORD Value); + + HRESULT STDMETHODCALLTYPE GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue); + + HRESULT STDMETHODCALLTYPE CreateStateBlock( + D3DSTATEBLOCKTYPE Type, + DWORD* pToken) { + + Com pStateBlock9; + HRESULT res = GetD3D9()->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(Type), &pStateBlock9); + + D3D8StateBlock* pStateBlock = new D3D8StateBlock(this, pStateBlock9.ref()); + + *pToken = DWORD(reinterpret_cast(pStateBlock)); + + return res; + } + + HRESULT STDMETHODCALLTYPE CaptureStateBlock(DWORD Token) { + return reinterpret_cast(Token)->Capture(); + } + + HRESULT STDMETHODCALLTYPE ApplyStateBlock(DWORD Token) { + return reinterpret_cast(Token)->Apply(); + } + + HRESULT STDMETHODCALLTYPE DeleteStateBlock(DWORD Token) { + delete reinterpret_cast(Token); + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE BeginStateBlock() { + + if (unlikely(m_recorder != nullptr)) + return D3DERR_INVALIDCALL; + + m_recorder = new D3D8StateBlock(this); + + return GetD3D9()->BeginStateBlock(); + } + + HRESULT STDMETHODCALLTYPE EndStateBlock(DWORD* pToken) { + + if (unlikely(pToken == nullptr || m_recorder == nullptr)) + return D3DERR_INVALIDCALL; + + Com pStateBlock; + HRESULT res = GetD3D9()->EndStateBlock(&pStateBlock); + + m_recorder->SetD3D9(std::move(pStateBlock)); + + *pToken = DWORD(reinterpret_cast(m_recorder)); + + m_recorder = nullptr; + + return res; + } + + HRESULT STDMETHODCALLTYPE SetClipStatus(const D3DCLIPSTATUS8* pClipStatus) { + return GetD3D9()->SetClipStatus(reinterpret_cast(pClipStatus)); + } + + HRESULT STDMETHODCALLTYPE GetClipStatus(D3DCLIPSTATUS8* pClipStatus) { + return GetD3D9()->GetClipStatus(reinterpret_cast(pClipStatus)); + } + + HRESULT STDMETHODCALLTYPE GetTexture(DWORD Stage, IDirect3DBaseTexture8** ppTexture) { + InitReturnPtr(ppTexture); + + *ppTexture = m_textures[Stage]; + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) { + + if (unlikely(Stage >= d8caps::MAX_TEXTURE_STAGES)) + return D3DERR_INVALIDCALL; + + if (unlikely(ShouldRecord())) + return m_recorder->SetTexture(Stage, pTexture); + + D3D8Texture2D* tex = static_cast(pTexture); + + m_textures[Stage] = tex; + + return GetD3D9()->SetTexture(Stage, D3D8Texture2D::GetD3D9Nullable(tex)); + } + + HRESULT STDMETHODCALLTYPE GetTextureStageState( + DWORD Stage, + D3DTEXTURESTAGESTATETYPE Type, + DWORD* pValue) { + d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type); + + if (stateType != -1) { + // if the type has been remapped to a sampler state type: + return GetD3D9()->GetSamplerState(Stage, stateType, pValue); + } + else { + return GetD3D9()->GetTextureStageState(Stage, d3d9::D3DTEXTURESTAGESTATETYPE(Type), pValue); + } + } + + HRESULT STDMETHODCALLTYPE SetTextureStageState( + DWORD Stage, + D3DTEXTURESTAGESTATETYPE Type, + DWORD Value) { + + d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type); + + if (stateType != -1) { + // if the type has been remapped to a sampler state type: + return GetD3D9()->SetSamplerState(Stage, stateType, Value); + } else { + return GetD3D9()->SetTextureStageState(Stage, d3d9::D3DTEXTURESTAGESTATETYPE(Type), Value); + } + } + + HRESULT STDMETHODCALLTYPE ValidateDevice(DWORD* pNumPasses) { + return GetD3D9()->ValidateDevice(pNumPasses); + } + + // Palettes not supported by d9vk, but we pass the values through anyway. + + HRESULT STDMETHODCALLTYPE SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries) { + return GetD3D9()->SetPaletteEntries(PaletteNumber, pEntries); + } + + HRESULT STDMETHODCALLTYPE GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries) { + return GetD3D9()->GetPaletteEntries(PaletteNumber, pEntries); + } + + HRESULT STDMETHODCALLTYPE SetCurrentTexturePalette(UINT PaletteNumber) { + return GetD3D9()->SetCurrentTexturePalette(PaletteNumber); + } + + HRESULT STDMETHODCALLTYPE GetCurrentTexturePalette(UINT* PaletteNumber) { + return GetD3D9()->GetCurrentTexturePalette(PaletteNumber); + } + + HRESULT STDMETHODCALLTYPE DrawPrimitive( + D3DPRIMITIVETYPE PrimitiveType, + UINT StartVertex, + UINT PrimitiveCount) { + return GetD3D9()->DrawPrimitive(d3d9::D3DPRIMITIVETYPE(PrimitiveType), StartVertex, PrimitiveCount); + } + + HRESULT STDMETHODCALLTYPE DrawIndexedPrimitive( + D3DPRIMITIVETYPE PrimitiveType, + UINT MinVertexIndex, + UINT NumVertices, + UINT StartIndex, + UINT PrimitiveCount) { + return GetD3D9()->DrawIndexedPrimitive( + d3d9::D3DPRIMITIVETYPE(PrimitiveType), + m_baseVertexIndex, // set by SetIndices + MinVertexIndex, + NumVertices, + StartIndex, + PrimitiveCount); + } + + HRESULT STDMETHODCALLTYPE DrawPrimitiveUP( + D3DPRIMITIVETYPE PrimitiveType, + UINT PrimitiveCount, + const void* pVertexStreamZeroData, + UINT VertexStreamZeroStride) { + return GetD3D9()->DrawPrimitiveUP(d3d9::D3DPRIMITIVETYPE(PrimitiveType), PrimitiveCount, pVertexStreamZeroData, VertexStreamZeroStride); + } + + HRESULT STDMETHODCALLTYPE DrawIndexedPrimitiveUP( + D3DPRIMITIVETYPE PrimitiveType, + UINT MinVertexIndex, + UINT NumVertices, + UINT PrimitiveCount, + const void* pIndexData, + D3DFORMAT IndexDataFormat, + const void* pVertexStreamZeroData, + UINT VertexStreamZeroStride) { + return GetD3D9()->DrawIndexedPrimitiveUP( + d3d9::D3DPRIMITIVETYPE(PrimitiveType), + MinVertexIndex, + NumVertices, + PrimitiveCount, + pIndexData, + d3d9::D3DFORMAT(IndexDataFormat), + pVertexStreamZeroData, + VertexStreamZeroStride); + } + + HRESULT STDMETHODCALLTYPE ProcessVertices( + UINT SrcStartIndex, + UINT DestIndex, + UINT VertexCount, + IDirect3DVertexBuffer8* pDestBuffer, + DWORD Flags) { + if (unlikely(!pDestBuffer)) + return D3DERR_INVALIDCALL; + D3D8VertexBuffer* buffer = static_cast(pDestBuffer); + return GetD3D9()->ProcessVertices( + SrcStartIndex, + DestIndex, + VertexCount, + buffer->GetD3D9(), + nullptr, + Flags + ); + } + + + HRESULT STDMETHODCALLTYPE CreateVertexShader( + const DWORD* pDeclaration, + const DWORD* pFunction, + DWORD* pHandle, + DWORD Usage); + + HRESULT STDMETHODCALLTYPE SetVertexShader(DWORD Handle); + + HRESULT STDMETHODCALLTYPE GetVertexShader(DWORD* pHandle); + + HRESULT STDMETHODCALLTYPE DeleteVertexShader(DWORD Handle); + + HRESULT STDMETHODCALLTYPE SetVertexShaderConstant( + DWORD StartRegister, + const void* pConstantData, + DWORD ConstantCount) { + // ConstantCount is actually the same as Vector4fCount + return GetD3D9()->SetVertexShaderConstantF(StartRegister, reinterpret_cast(pConstantData), ConstantCount); + } + + HRESULT STDMETHODCALLTYPE SetStreamSource( + UINT StreamNumber, + IDirect3DVertexBuffer8* pStreamData, + UINT Stride) { + if (unlikely(StreamNumber >= d8caps::MAX_STREAMS)) + return D3DERR_INVALIDCALL; + + D3D8VertexBuffer* buffer = static_cast(pStreamData); + + m_streams[StreamNumber] = D3D8VBO {buffer, Stride}; + + return GetD3D9()->SetStreamSource(StreamNumber, D3D8VertexBuffer::GetD3D9Nullable(buffer), 0, Stride); + } + + HRESULT STDMETHODCALLTYPE GetStreamSource( + UINT StreamNumber, + IDirect3DVertexBuffer8** ppStreamData, + UINT* pStride) { + InitReturnPtr(ppStreamData); + + if (likely(pStride != nullptr)) + *pStride = 0; + + if (unlikely(ppStreamData == nullptr || pStride == nullptr)) + return D3DERR_INVALIDCALL; + + if (unlikely(StreamNumber >= d8caps::MAX_STREAMS)) + return D3DERR_INVALIDCALL; + + const D3D8VBO& vbo = m_streams[StreamNumber]; + + *ppStreamData = vbo.buffer.ref(); + *pStride = vbo.stride; + + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) { + + if (unlikely(ShouldRecord())) + return m_recorder->SetIndices(pIndexData, BaseVertexIndex); + + // used by DrawIndexedPrimitive + m_baseVertexIndex = static_cast(BaseVertexIndex); + + D3D8IndexBuffer* buffer = static_cast(pIndexData); + + m_indices = buffer; + + return GetD3D9()->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(buffer)); + } + + HRESULT STDMETHODCALLTYPE GetIndices( + IDirect3DIndexBuffer8** ppIndexData, + UINT* pBaseVertexIndex) { + *ppIndexData = m_indices.ptr(); + *pBaseVertexIndex = m_baseVertexIndex; + return D3D_OK; + } + + HRESULT STDMETHODCALLTYPE CreatePixelShader( + const DWORD* pFunction, + DWORD* pHandle); + + HRESULT STDMETHODCALLTYPE SetPixelShader(DWORD Handle); + + HRESULT STDMETHODCALLTYPE GetPixelShader(DWORD* pHandle); + + HRESULT STDMETHODCALLTYPE DeletePixelShader(THIS_ DWORD Handle); + + HRESULT STDMETHODCALLTYPE SetPixelShaderConstant( + DWORD StartRegister, + const void* pConstantData, + DWORD ConstantCount) { + // ConstantCount is actually the same as Vector4fCount + return GetD3D9()->SetPixelShaderConstantF(StartRegister, reinterpret_cast(pConstantData), ConstantCount); + } + + // Patches not supported by d9vk but pass the values through anyway. + + HRESULT STDMETHODCALLTYPE DrawRectPatch( + UINT Handle, + const float* pNumSegs, + const D3DRECTPATCH_INFO* pRectPatchInfo) { + return GetD3D9()->DrawRectPatch(Handle, pNumSegs, reinterpret_cast(pRectPatchInfo)); + } + + HRESULT STDMETHODCALLTYPE DrawTriPatch( + UINT Handle, + const float* pNumSegs, + const D3DTRIPATCH_INFO* pTriPatchInfo) { + return GetD3D9()->DrawTriPatch(Handle, pNumSegs, reinterpret_cast(pTriPatchInfo)); + } + + HRESULT STDMETHODCALLTYPE DeletePatch(UINT Handle) { + return GetD3D9()->DeletePatch(Handle); + } + + public: // Internal Methods // + + inline bool ShouldRecord() { return m_recorder != nullptr; } + + inline void ResetState() { + // Purge cached objects + m_backBuffers.clear(); + m_textures.fill(nullptr); + m_streams.fill(D3D8VBO()); + m_indices = nullptr; + for (UINT i = 0; i < m_presentParams.BackBufferCount; i++) { + m_backBuffers.push_back(nullptr); + } + m_frontBuffer = nullptr; + m_renderTarget = nullptr; + m_depthStencil = nullptr; + } + + friend d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8DeviceEx* device, DWORD Handle); + friend D3D8VertexShaderInfo* getVertexShaderInfo(D3D8DeviceEx* device, DWORD Handle); + + private: + + D3D9Bridge* m_bridge; + const D3D8Options& m_d3d8Options; + + Com m_parent; + + D3DPRESENT_PARAMETERS m_presentParams; + + D3D8StateBlock* m_recorder = nullptr; + + struct D3D8VBO { + Com buffer = nullptr; + UINT stride = 0; + }; + + // Remember to fill() these in the constructor! + std::array m_textures; + std::array m_streams; + + Com m_indices; + INT m_baseVertexIndex = 0; + + std::vector> m_backBuffers; + Com m_frontBuffer; + + Com m_renderTarget; + Com m_depthStencil; + + std::vector m_vertexShaders; + std::vector m_pixelShaders; + DWORD m_currentVertexShader = 0; // can be FVF or vs index, can have DXVK_D3D8_SHADER_BIT + DWORD m_currentPixelShader = 0; // will have DXVK_D3D8_SHADER_BIT + + D3DDEVTYPE m_deviceType; + HWND m_window; + + DWORD m_behaviorFlags; + + }; + +} diff --git a/src/d3d8/d3d8_device_child.h b/src/d3d8/d3d8_device_child.h new file mode 100644 index 00000000..8ab23efe --- /dev/null +++ b/src/d3d8/d3d8_device_child.h @@ -0,0 +1,68 @@ +#pragma once + +/** Common methods for device-tied objects. +* - AddRef, Release from IUnknown +* - GetDevice from various classes including IDirect3DResource8 +*/ + +#include "d3d8_include.h" +#include "d3d8_wrapped_object.h" + +namespace dxvk { + + class D3D8DeviceEx; + + template + class D3D8DeviceChild : public D3D8WrappedObject { + + public: + + D3D8DeviceChild(D3D8DeviceEx* pDevice, Com&& Object) + : D3D8WrappedObject(std::move(Object)) + , 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(IDirect3DDevice8** ppDevice) { + InitReturnPtr(ppDevice); + + if (ppDevice == nullptr) + return D3DERR_INVALIDCALL; + + *ppDevice = ref(GetDevice()); + return D3D_OK; + } + + IDirect3DDevice8* GetDevice() { + return reinterpret_cast(m_parent); + } + + D3D8DeviceEx* GetParent() { + return m_parent; + } + + protected: + + D3D8DeviceEx* m_parent; + + }; + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_include.h b/src/d3d8/d3d8_include.h new file mode 100644 index 00000000..7ca9aee0 --- /dev/null +++ b/src/d3d8/d3d8_include.h @@ -0,0 +1,226 @@ +#pragma once + +#ifndef _MSC_VER +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0A00 +#endif + +#include + +// Used for some refcounted COM objects that need to be released. +#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p) = nullptr; } } + +#include + +// Declare __uuidof for D3D8 interfaces +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDirect3D8, 0x1DD9E8DA,0x1C77,0x4D40,0xB0,0xCF,0x98,0xFE,0xFD,0xFF,0x95,0x12); +__CRT_UUID_DECL(IDirect3DDevice8, 0x7385E5DF,0x8FE8,0x41D5,0x86,0xB6,0xD7,0xB4,0x85,0x47,0xB6,0xCF); +__CRT_UUID_DECL(IDirect3DResource8, 0x1B36BB7B,0x09B7,0x410A,0xB4,0x45,0x7D,0x14,0x30,0xD7,0xB3,0x3F); +__CRT_UUID_DECL(IDirect3DVertexBuffer8, 0x8AEEEAC7,0x05F9,0x44D4,0xB5,0x91,0x00,0x0B,0x0D,0xF1,0xCB,0x95); +__CRT_UUID_DECL(IDirect3DVolume8, 0xBD7349F5,0x14F1,0x42E4,0x9C,0x79,0x97,0x23,0x80,0xDB,0x40,0xC0); +__CRT_UUID_DECL(IDirect3DSwapChain8, 0x928C088B,0x76B9,0x4C6B,0xA5,0x36,0xA5,0x90,0x85,0x38,0x76,0xCD); +__CRT_UUID_DECL(IDirect3DSurface8, 0xB96EEBCA,0xB326,0x4EA5,0x88,0x2F,0x2F,0xF5,0xBA,0xE0,0x21,0xDD); +__CRT_UUID_DECL(IDirect3DIndexBuffer8, 0x0E689C9A,0x053D,0x44A0,0x9D,0x92,0xDB,0x0E,0x3D,0x75,0x0F,0x86); +__CRT_UUID_DECL(IDirect3DBaseTexture8, 0xB4211CFA,0x51B9,0x4A9F,0xAB,0x78,0xDB,0x99,0xB2,0xBB,0x67,0x8E); +__CRT_UUID_DECL(IDirect3DTexture8, 0xE4CDD575,0x2866,0x4F01,0xB1,0x2E,0x7E,0xEC,0xE1,0xEC,0x93,0x58); +__CRT_UUID_DECL(IDirect3DCubeTexture8, 0x3EE5B968,0x2ACA,0x4C34,0x8B,0xB5,0x7E,0x0C,0x3D,0x19,0xB7,0x50); +__CRT_UUID_DECL(IDirect3DVolumeTexture8, 0x4B8AAAFA,0x140F,0x42BA,0x91,0x31,0x59,0x7E,0xAF,0xAA,0x2E,0xAD); +#elif defined(_MSC_VER) +interface DECLSPEC_UUID("1DD9E8DA-1C77-4D40-B0CF-98FEFDFF9512") IDirect3D8; +interface DECLSPEC_UUID("7385E5DF-8FE8-41D5-86B6-D7B48547B6CF") IDirect3DDevice8; +interface DECLSPEC_UUID("1B36BB7B-09B7-410A-B445-7D1430D7B33F") IDirect3DResource8; +interface DECLSPEC_UUID("8AEEEAC7-05F9-44D4-B591-000B0DF1CB95") IDirect3DVertexBuffer8; +interface DECLSPEC_UUID("BD7349F5-14F1-42E4-9C79-972380DB40C0") IDirect3DVolume8; +interface DECLSPEC_UUID("928C088B-76B9-4C6B-A536-A590853876CD") IDirect3DSwapChain8; +interface DECLSPEC_UUID("B96EEBCA-B326-4EA5-882F-2FF5BAE021DD") IDirect3DSurface8; +interface DECLSPEC_UUID("0E689C9A-053D-44A0-9D92-DB0E3D750F86") IDirect3DIndexBuffer8; +interface DECLSPEC_UUID("B4211CFA-51B9-4A9F-AB78-DB99B2BB678E") IDirect3DBaseTexture8; +interface DECLSPEC_UUID("E4CDD575-2866-4F01-B12E-7EECE1EC9358") IDirect3DTexture8; +interface DECLSPEC_UUID("3EE5B968-2ACA-4C34-8BB5-7E0C3D19B750") IDirect3DCubeTexture8; +interface DECLSPEC_UUID("4B8AAAFA-140F-42BA-9131-597EAFAA2EAD") IDirect3DVolumeTexture8; +#endif + +// Undefine D3D8 macros // +#undef DIRECT3D_VERSION +#undef D3D_SDK_VERSION + +#undef D3DCS_ALL // parentheses added in DX9 +#undef D3DFVF_POSITION_MASK // changed from 0x00E to 0x400E in DX9 +#undef D3DFVF_RESERVED2 // reduced from 4 to 2 in DX9 + +#undef D3DSP_REGNUM_MASK // changed from 0x00000FFF to 0x000007FF in DX9 + + +#if defined(__MINGW32__) || defined(__GNUC__) + +// Avoid redundant definitions (add D3D*_DEFINED macros here) // +#define D3DRECT_DEFINED +#define D3DMATRIX_DEFINED + +// Temporarily override __CRT_UUID_DECL to allow usage in d3d9 namespace +#pragma push_macro("__CRT_UUID_DECL") +#ifdef __CRT_UUID_DECL +#undef __CRT_UUID_DECL +#endif + +#ifdef __MINGW32__ +#define __CRT_UUID_DECL(type,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ +} \ + extern "C++" { \ + template<> struct __mingw_uuidof_s { \ + static constexpr IID __uuid_inst = { \ + l,w1,w2, {b1,b2,b3,b4,b5,b6,b7,b8} \ + }; \ + }; \ + template<> constexpr const GUID &__mingw_uuidof() { \ + return __mingw_uuidof_s::__uuid_inst; \ + } \ + template<> constexpr const GUID &__mingw_uuidof() { \ + return __mingw_uuidof_s::__uuid_inst; \ + } \ + } \ +namespace d3d9 { + +#elif defined(__GNUC__) +#define __CRT_UUID_DECL(type, a, b, c, d, e, f, g, h, i, j, k) \ +} \ + extern "C++" { template <> constexpr GUID __uuidof_helper() { return GUID{a,b,c,{d,e,f,g,h,i,j,k}}; } } \ + extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ + extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ + extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ + extern "C++" { template <> constexpr GUID __uuidof_helper() { return __uuidof_helper(); } } \ +namespace d3d9 { +#endif + +#endif // defined(__MINGW32__) || defined(__GNUC__) + + +/** +* \brief Direct3D 9 +* +* All D3D9 interfaces are included within +* a namespace, so as not to collide with +* D3D8 interfaces. +*/ +namespace d3d9 { +#include +} + +// Indicates d3d9:: namespace is in-use. +#define DXVK_D3D9_NAMESPACE + +#if defined(__MINGW32__) || defined(__GNUC__) +#pragma pop_macro("__CRT_UUID_DECL") +#endif + +//for some reason we need to specify __declspec(dllexport) for MinGW +#if defined(__WINE__) +#define DLLEXPORT __attribute__((visibility("default"))) +#else +#define 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/sync/sync_recursive.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_misc.h" +#include "../util/util_string.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 + +// From d3dtypes.h + +#ifndef D3DDEVINFOID_TEXTUREMANAGER +#define D3DDEVINFOID_TEXTUREMANAGER 1 +#endif + +#ifndef D3DDEVINFOID_D3DTEXTUREMANAGER +#define D3DDEVINFOID_D3DTEXTUREMANAGER 2 +#endif + +#ifndef D3DDEVINFOID_TEXTURING +#define D3DDEVINFOID_TEXTURING 3 +#endif + +// From d3dhal.h + +#ifndef D3DDEVINFOID_VCACHE +#define D3DDEVINFOID_VCACHE 4 +#endif + +// MinGW headers are broken. Who'dve guessed? +#ifndef _MSC_VER + +// Missing from d3d8types.h +#ifndef D3DDEVINFOID_RESOURCEMANAGER +#define D3DDEVINFOID_RESOURCEMANAGER 5 +#endif + +#ifndef D3DDEVINFOID_VERTEXSTATS +#define D3DDEVINFOID_VERTEXSTATS 6 // Aka D3DDEVINFOID_D3DVERTEXSTATS +#endif + +#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; diff --git a/src/d3d8/d3d8_interface.cpp b/src/d3d8/d3d8_interface.cpp new file mode 100644 index 00000000..01e58cdc --- /dev/null +++ b/src/d3d8/d3d8_interface.cpp @@ -0,0 +1,143 @@ +#include "d3d8_interface.h" + +#include "d3d8_device.h" +#include "d3d8_texture.h" + +#include + +namespace dxvk +{ + D3D8InterfaceEx::D3D8InterfaceEx(UINT SDKVersion) + { + d3d9::Direct3DCreate9Ex(D3D_SDK_VERSION, &m_d3d9ex); + + m_bridge = GetD3D9Bridge(m_d3d9ex); + m_d3d8Options = m_bridge->GetConfig(); + + m_adapterCount = m_d3d9ex->GetAdapterCount(); + + m_adapterModeCounts.resize(m_adapterCount); + m_adapterModes.reserve(m_adapterCount); + + for (UINT adapter = 0; adapter < m_adapterCount; adapter++) { + + m_adapterModes.emplace_back(); + + // cache adapter modes and mode counts for each d3d9 format + for (d3d9::D3DFORMAT fmt : ADAPTER_FORMATS) { + + const UINT modeCount = m_d3d9ex->GetAdapterModeCount(adapter, fmt); + + for (UINT mode = 0; mode < modeCount; mode++) { + + m_adapterModes[adapter].emplace_back(); + + m_d3d9ex->EnumAdapterModes(adapter, fmt, mode, &(m_adapterModes[adapter].back())); + + // can't use modeCount as it's only for one fmt + m_adapterModeCounts[adapter]++; + } + } + } + } + + HRESULT STDMETHODCALLTYPE D3D8InterfaceEx::QueryInterface(REFIID riid, void** ppvObject) { + if (ppvObject == nullptr) + return E_POINTER; + + *ppvObject = nullptr; + + if (riid == __uuidof(IUnknown) + || riid == __uuidof(IDirect3D8)) { + *ppvObject = ref(this); + return S_OK; + } + + Logger::warn("D3D8InterfaceEx::QueryInterface: Unknown interface query"); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + + HRESULT STDMETHODCALLTYPE D3D8InterfaceEx::GetAdapterIdentifier( + UINT Adapter, + DWORD Flags, + D3DADAPTER_IDENTIFIER8* pIdentifier) { + + // This flag now has the opposite effect. + // Either way, WHQLevel will be 1 with Direct3D9Ex + if (Flags & D3DENUM_NO_WHQL_LEVEL) + Flags &= ~D3DENUM_WHQL_LEVEL; + else + Flags |= D3DENUM_WHQL_LEVEL; + + d3d9::D3DADAPTER_IDENTIFIER9 identifier9; + HRESULT res = m_d3d9ex->GetAdapterIdentifier(Adapter, Flags, &identifier9); + + strncpy(pIdentifier->Driver, identifier9.Driver, MAX_DEVICE_IDENTIFIER_STRING); + strncpy(pIdentifier->Description, identifier9.Description, MAX_DEVICE_IDENTIFIER_STRING); + + pIdentifier->DriverVersion = identifier9.DriverVersion; + pIdentifier->VendorId = identifier9.VendorId; + pIdentifier->DeviceId = identifier9.DeviceId; + pIdentifier->SubSysId = identifier9.SubSysId; + pIdentifier->Revision = identifier9.Revision; + + // copy + pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier; + + pIdentifier->WHQLLevel = identifier9.WHQLLevel; + + return res; + } + + HRESULT __stdcall D3D8InterfaceEx::EnumAdapterModes( + UINT Adapter, + UINT Mode, + D3DDISPLAYMODE* pMode) { + + if (Adapter >= m_adapterCount || Mode >= m_adapterModeCounts[Adapter] || pMode == nullptr) { + return D3DERR_INVALIDCALL; + } + + pMode->Width = m_adapterModes[Adapter][Mode].Width; + pMode->Height = m_adapterModes[Adapter][Mode].Height; + pMode->RefreshRate = m_adapterModes[Adapter][Mode].RefreshRate; + pMode->Format = (D3DFORMAT)m_adapterModes[Adapter][Mode].Format; + return D3D_OK; + } + + HRESULT __stdcall D3D8InterfaceEx::CreateDevice( + UINT Adapter, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS* pPresentationParameters, + IDirect3DDevice8** ppReturnedDeviceInterface) { + + Com pDevice9 = nullptr; + d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters); + HRESULT res = m_d3d9ex->CreateDevice( + Adapter, + (d3d9::D3DDEVTYPE)DeviceType, + hFocusWindow, + BehaviorFlags, + ¶ms, + &pDevice9 + ); + + if (FAILED(res)) { + return res; + } + + *ppReturnedDeviceInterface = ref(new D3D8DeviceEx( + this, std::move(pDevice9), + DeviceType, hFocusWindow, BehaviorFlags, + pPresentationParameters + )); + + return res; + } + + + +} // namespace dxvk \ No newline at end of file diff --git a/src/d3d8/d3d8_interface.h b/src/d3d8/d3d8_interface.h new file mode 100644 index 00000000..790c71a0 --- /dev/null +++ b/src/d3d8/d3d8_interface.h @@ -0,0 +1,178 @@ +#pragma once + +// Implements IDirect3D8 + +#include "d3d8_include.h" +#include "d3d8_d3d9_util.h" +#include "d3d8_options.h" +#include "../d3d9/d3d9_bridge.h" + +//#include "../dxvk/dxvk_instance.h" + +namespace dxvk { + + /** + * \brief D3D8 interface implementation + * + * Implements the IDirect3DDevice8 interfaces + * which provides the way to get adapters and create other objects such as \ref IDirect3DDevice8. + * similar to \ref DxgiFactory but for D3D8. + */ + class D3D8InterfaceEx final : public ComObjectClamp { + + static constexpr d3d9::D3DFORMAT ADAPTER_FORMATS[] = { + d3d9::D3DFMT_A1R5G5B5, + //d3d9::D3DFMT_A2R10G10B10, (not in D3D8) + d3d9::D3DFMT_A8R8G8B8, + d3d9::D3DFMT_R5G6B5, + d3d9::D3DFMT_X1R5G5B5, + d3d9::D3DFMT_X8R8G8B8 + }; + + public: + D3D8InterfaceEx(UINT SDKVersion); + + // IUnknown methods // + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + + // IDirect3D8 methods // + + // TODO: RegisterSoftwareDevice // + HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction) { + dxvk::Logger::warn("D3D8InterfaceEx::RegisterSoftwareDevice: stub"); + return D3DERR_INVALIDCALL; + } + + UINT STDMETHODCALLTYPE GetAdapterCount() { + return m_d3d9ex->GetAdapterCount(); + } + + HRESULT STDMETHODCALLTYPE GetAdapterIdentifier( + UINT Adapter, + DWORD Flags, + D3DADAPTER_IDENTIFIER8* pIdentifier); + + UINT STDMETHODCALLTYPE GetAdapterModeCount(UINT Adapter) { + return m_adapterModeCounts[Adapter]; + } + + HRESULT STDMETHODCALLTYPE EnumAdapterModes( + UINT Adapter, + UINT Mode, + D3DDISPLAYMODE* pMode); + + HRESULT STDMETHODCALLTYPE GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) { + return m_d3d9ex->GetAdapterDisplayMode(Adapter, (d3d9::D3DDISPLAYMODE*)pMode); + } + + HRESULT STDMETHODCALLTYPE CheckDeviceType( + UINT Adapter, + D3DDEVTYPE DevType, + D3DFORMAT AdapterFormat, + D3DFORMAT BackBufferFormat, + BOOL bWindowed) { + return m_d3d9ex->CheckDeviceType( + Adapter, + (d3d9::D3DDEVTYPE)DevType, + (d3d9::D3DFORMAT)AdapterFormat, + (d3d9::D3DFORMAT)BackBufferFormat, + bWindowed + ); + } + + HRESULT STDMETHODCALLTYPE CheckDeviceFormat( + UINT Adapter, + D3DDEVTYPE DeviceType, + D3DFORMAT AdapterFormat, + DWORD Usage, + D3DRESOURCETYPE RType, + D3DFORMAT CheckFormat) { + return m_d3d9ex->CheckDeviceFormat( + Adapter, + (d3d9::D3DDEVTYPE)DeviceType, + (d3d9::D3DFORMAT)AdapterFormat, + Usage, + (d3d9::D3DRESOURCETYPE)RType, + (d3d9::D3DFORMAT)CheckFormat + ); + } + + HRESULT STDMETHODCALLTYPE CheckDeviceMultiSampleType( + UINT Adapter, + D3DDEVTYPE DeviceType, + D3DFORMAT SurfaceFormat, + BOOL Windowed, + D3DMULTISAMPLE_TYPE MultiSampleType) { + + DWORD* pQualityLevels = nullptr; + return m_d3d9ex->CheckDeviceMultiSampleType( + Adapter, + (d3d9::D3DDEVTYPE)DeviceType, + (d3d9::D3DFORMAT)SurfaceFormat, + Windowed, + (d3d9::D3DMULTISAMPLE_TYPE)MultiSampleType, + pQualityLevels + ); + } + + HRESULT STDMETHODCALLTYPE CheckDepthStencilMatch( + UINT Adapter, + D3DDEVTYPE DeviceType, + D3DFORMAT AdapterFormat, + D3DFORMAT RenderTargetFormat, + D3DFORMAT DepthStencilFormat) { + return m_d3d9ex->CheckDepthStencilMatch( + Adapter, + (d3d9::D3DDEVTYPE)DeviceType, + (d3d9::D3DFORMAT)AdapterFormat, + (d3d9::D3DFORMAT)RenderTargetFormat, + (d3d9::D3DFORMAT)DepthStencilFormat + ); + } + + HRESULT STDMETHODCALLTYPE GetDeviceCaps( + UINT Adapter, + D3DDEVTYPE DeviceType, + D3DCAPS8* pCaps) { + d3d9::D3DCAPS9 caps9; + HRESULT res = m_d3d9ex->GetDeviceCaps(Adapter, (d3d9::D3DDEVTYPE)DeviceType, &caps9); + dxvk::ConvertCaps8(caps9, pCaps); + return res; + } + + HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter) { + return m_d3d9ex->GetAdapterMonitor(Adapter); + } + + HRESULT STDMETHODCALLTYPE CreateDevice( + UINT Adapter, + D3DDEVTYPE DeviceType, + HWND hFocusWindow, + DWORD BehaviorFlags, + D3DPRESENT_PARAMETERS* pPresentationParameters, + IDirect3DDevice8** ppReturnedDeviceInterface); + + + const D3D8Options& GetOptions() { return m_d3d8Options; } + + private: + + UINT m_adapterCount; + std::vector m_adapterModeCounts; + std::vector> m_adapterModes; + + //void CacheModes(D3D9Format Format); + + //static const char* GetDriverDllName(DxvkGpuVendor vendor); + + d3d9::IDirect3D9Ex* m_d3d9ex; + + bool m_extended; + + D3D9InterfaceBridge* m_bridge; + D3D8Options m_d3d8Options; + + //std::vector m_adapters; + }; + +} // namespace dxvk \ No newline at end of file diff --git a/src/d3d8/d3d8_main.cpp b/src/d3d8/d3d8_main.cpp new file mode 100644 index 00000000..6565ec66 --- /dev/null +++ b/src/d3d8/d3d8_main.cpp @@ -0,0 +1,37 @@ + +// Main: the location of Direct3DCreate8 and other core exported functions + +#include "d3d8_interface.h" +//#include "d3d9_shader_validator.h" + +class D3DFE_PROCESSVERTICES; +using PSGPERRORID = UINT; + +namespace dxvk { + Logger Logger::s_instance("d3d8.log"); + + HRESULT CreateD3D8( + UINT SDKVersion, + IDirect3D8** ppDirect3D8) { + if (!ppDirect3D8) + return D3DERR_INVALIDCALL; + + *ppDirect3D8 = ref(new D3D8InterfaceEx(SDKVersion)); + return D3D_OK; + } +} + +extern "C" { + + + DLLEXPORT IDirect3D8* __stdcall Direct3DCreate8(UINT nSDKVersion) { + //dxvk::CreateD3D9(false, &pDirect3D); + + dxvk::Logger::trace("Direct3DCreate8 called"); + + IDirect3D8* pDirect3D = nullptr; + dxvk::CreateD3D8(false, &pDirect3D); + + return pDirect3D; + } +} diff --git a/src/d3d8/d3d8_options.h b/src/d3d8/d3d8_options.h new file mode 100644 index 00000000..110229bf --- /dev/null +++ b/src/d3d8/d3d8_options.h @@ -0,0 +1,21 @@ +#pragma once + +#include "d3d8_include.h" +#include "../d3d9/d3d9_bridge.h" +#include "../util/config/config.h" + +namespace dxvk { + struct D3D8Options { + /// Treat D24S8 and D16 as early NVIDIA shadow buffers that test + /// reference values in the range [0..2^N - 1] where N is bit depth. + /// Also emulates hardware shadow filtering using a bilinear 2x2 PCF. + bool useShadowBuffers = false; + + D3D8Options() {} + D3D8Options(const Config& config) { + useShadowBuffers = config.getOption("d3d8.useShadowBuffers", useShadowBuffers); + } + + }; + +} diff --git a/src/d3d8/d3d8_resource.h b/src/d3d8/d3d8_resource.h new file mode 100644 index 00000000..6155b7ea --- /dev/null +++ b/src/d3d8/d3d8_resource.h @@ -0,0 +1,101 @@ +#pragma once + +/** Implements IDirect3DResource8 +* +* - SetPrivateData, GetPrivateData, FreePrivateData +* - SetPriority, GetPriority +* +* - Subclasses must provide: PreLoad, GetType +*/ + +#include "d3d8_device_child.h" + +#include "../util/com/com_private_data.h" + +namespace dxvk { + + template + class D3D8Resource : public D3D8DeviceChild { + + public: + + D3D8Resource(D3D8DeviceEx* pDevice, Com&& Object) + : D3D8DeviceChild(pDevice, std::move(Object)) + , 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( + reinterpret_cast(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(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; + } + + virtual IUnknown* GetInterface(REFIID riid) override try { + return D3D8DeviceChild::GetInterface(riid); + } catch (HRESULT err) { + if (riid == __uuidof(IDirect3DResource8)) + return this; + + throw err; + } + + protected: + + DWORD m_priority; + + private: + + ComPrivateData m_privateData; + + }; + + +} // namespace dxvk \ No newline at end of file diff --git a/src/d3d8/d3d8_shader.cpp b/src/d3d8/d3d8_shader.cpp new file mode 100644 index 00000000..80b82565 --- /dev/null +++ b/src/d3d8/d3d8_shader.cpp @@ -0,0 +1,329 @@ + +#include "d3d8_shader.h" + +#define VSD_SHIFT_MASK(token, field) ((token & field ## MASK) >> field ## SHIFT) + +#define VS_SHIFT_MASK(token, field) ((token << field ## _SHIFT) & field ## _MASK) + +// Magic number from D3DVSD_REG() +#define VSD_SKIP_FLAG 0x10000000 + +#define VS_BIT_PARAM 0x80000000 + +namespace dxvk { + + // v0-v16, used by fixed function vs + static constexpr int D3D8_NUM_VERTEX_INPUT_REGISTERS = 17; + + // standard mapping of vertx input v0-v16 to d3d9 usages and usage indices + // (See D3DVSDE_ values in d3d8types.h or DirectX 8 docs for vertex shader input registers vn) + static const BYTE D3D8_VERTEX_INPUT_REGISTERS[D3D8_NUM_VERTEX_INPUT_REGISTERS][2] = { + {d3d9::D3DDECLUSAGE_POSITION, 0}, // dcl_position v0 + {d3d9::D3DDECLUSAGE_BLENDWEIGHT, 0}, // dcl_blendweight v1 + {d3d9::D3DDECLUSAGE_BLENDINDICES, 0}, // dcl_blendindices v2 + {d3d9::D3DDECLUSAGE_NORMAL, 0}, // dcl_normal v3 + {d3d9::D3DDECLUSAGE_PSIZE, 0}, // dcl_psize v4 + {d3d9::D3DDECLUSAGE_COLOR, 0}, // dcl_color v5 ; diffuse + {d3d9::D3DDECLUSAGE_COLOR, 1}, // dcl_color1 v6 ; specular + {d3d9::D3DDECLUSAGE_TEXCOORD, 0}, // dcl_texcoord0 v7 + {d3d9::D3DDECLUSAGE_TEXCOORD, 1}, // dcl_texcoord1 v8 + {d3d9::D3DDECLUSAGE_TEXCOORD, 2}, // dcl_texcoord2 v9 + {d3d9::D3DDECLUSAGE_TEXCOORD, 3}, // dcl_texcoord3 v10 + {d3d9::D3DDECLUSAGE_TEXCOORD, 4}, // dcl_texcoord4 v11 + {d3d9::D3DDECLUSAGE_TEXCOORD, 5}, // dcl_texcoord5 v12 + {d3d9::D3DDECLUSAGE_TEXCOORD, 6}, // dcl_texcoord6 v13 + {d3d9::D3DDECLUSAGE_TEXCOORD, 7}, // dcl_texcoord7 v14 + {d3d9::D3DDECLUSAGE_POSITION, 1}, // dcl_position1 v15 ; position 2 + {d3d9::D3DDECLUSAGE_NORMAL, 1}, // dcl_normal1 v16 ; normal 2 + }; + + // width in bytes of each D3DDECLTYPE (dx9) or D3DVSDT (dx8) + static const BYTE D3D9_DECL_TYPE_SIZES[d3d9::MAXD3DDECLTYPE + 1] = { + 4, // FLOAT1 + 8, // FLOAT2 + 12, // FLOAT3 + 16, // FLOAT4 + 4, // D3DCOLOR + + 4, // UBYTE4 + 4, // SHORT2 + 8, // SHORT4 + + // The following are for vs2.0+ // + 4, // UBYTE4N + 4, // SHORT2N + 8, // SHORT4N + 4, // USHORT2N + 8, // USHORT4N + 6, // UDEC3 + 6, // DEC3N + 8, // FLOAT16_2 + 16, // FLOAT16_4 + + 0 // UNUSED + }; + + /** + * Encodes a \ref DxsoShaderInstruction + * + * \param [in] opcode DxsoOpcode + * \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/instruction-token + */ + constexpr DWORD encodeInstruction(d3d9::D3DSHADER_INSTRUCTION_OPCODE_TYPE opcode) { + DWORD token = 0; + token |= opcode & 0xFFFF; // bits 0:15 + return token; + } + + /** + * Encodes a \ref DxsoRegister + * + * \param [in] regType DxsoRegisterType + * \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/destination-parameter-token + */ + constexpr DWORD encodeDestRegister(d3d9::D3DSHADER_PARAM_REGISTER_TYPE type, UINT reg) { + DWORD token = 0; + token |= reg & 0x7FF; // bits 0:10 num + token |= ((type & 0x07) << 28); // bits 28:30 type[0:2] + token |= ((type & 0x18) >> 3) << 11; // bits 11:12 type[3:4] + // UINT addrMode : 1; // bit 13 hasRelative + token |= 0b1111 << 16; // bits 16:19 DxsoRegMask + // UINT resultModifier : 3; // bits 20:23 + // UINT resultShift : 3; // bits 24:27 + token |= 1 << 31; // bit 31 always 1 + return token; + } + + /** + * Encodes a \ref DxsoDeclaration + * + * \param [in] regType DxsoRegisterType + * \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/dcl-instruction + */ + constexpr DWORD encodeDeclaration(d3d9::D3DDECLUSAGE usage, DWORD index) { + DWORD token = 0; + token |= VS_SHIFT_MASK(usage, D3DSP_DCL_USAGE); // bits 0:4 DxsoUsage (TODO: missing MSB) + token |= VS_SHIFT_MASK(index, D3DSP_DCL_USAGEINDEX); // bits 16:19 usageIndex + token |= 1 << 31; // bit 31 always 1 + return token; + } + + D3D9VertexShaderCode translateVertexShader8(const DWORD* pDeclaration, const DWORD* pFunction) { + using d3d9::D3DDECLTYPE; + D3D9VertexShaderCode result; + + std::vector& tokens = result.function; + std::vector defs; // Constant definitions + + // shaderInputRegisters: + // set bit N to enable input register vN + DWORD shaderInputRegisters = 0; + + d3d9::D3DVERTEXELEMENT9* vertexElements = result.declaration; + unsigned int elementIdx = 0; + + // these are used for pDeclaration and pFunction + int i = 0; + DWORD token; + + std::stringstream dbg; + dbg << "Vertex Declaration Tokens:\n\t"; + + WORD currentStream = 0; + WORD currentOffset = 0; + + // remap d3d8 tokens to d3d9 vertex elements + // and enable specific shaderInputRegisters for each + do { + token = pDeclaration[i++]; + + D3DVSD_TOKENTYPE tokenType = D3DVSD_TOKENTYPE(VSD_SHIFT_MASK(token, D3DVSD_TOKENTYPE)); + + switch (tokenType) { + case D3DVSD_TOKEN_NOP: + dbg << "NOP"; + break; + case D3DVSD_TOKEN_STREAM: { + dbg << "STREAM "; + + // TODO: D3DVSD_STREAM_TESS + if (token & D3DVSD_STREAMTESSMASK) { + dbg << "TESS"; + } + + DWORD streamNum = VSD_SHIFT_MASK(token, D3DVSD_STREAMNUMBER); + + currentStream = streamNum; + currentOffset = 0; // reset offset + + dbg << ", num=" << streamNum; + break; + } + case D3DVSD_TOKEN_STREAMDATA: { + + dbg << "STREAMDATA "; + + // D3DVSD_SKIP + if (token & VSD_SKIP_FLAG) { + auto skipCount = VSD_SHIFT_MASK(token, D3DVSD_SKIPCOUNT); + dbg << "SKIP " << " count=" << skipCount; + currentOffset += skipCount * sizeof(DWORD); + break; + } + + // D3DVSD_REG + DWORD dataLoadType = VSD_SHIFT_MASK(token, D3DVSD_DATALOADTYPE); + + if ( dataLoadType == 0 ) { // vertex + + vertexElements[elementIdx].Stream = currentStream; + vertexElements[elementIdx].Offset = currentOffset; + + // Read and set data type + D3DDECLTYPE dataType = D3DDECLTYPE(VSD_SHIFT_MASK(token, D3DVSD_DATATYPE)); + vertexElements[elementIdx].Type = dataType; // (D3DVSDT values map directly to D3DDECLTYPE) + + // Increase stream offset + currentOffset += D3D9_DECL_TYPE_SIZES[dataType]; + + vertexElements[elementIdx].Method = d3d9::D3DDECLMETHOD_DEFAULT; + + DWORD dataReg = VSD_SHIFT_MASK(token, D3DVSD_VERTEXREG); + + // Map D3DVSDE register num to Usage and UsageIndex + vertexElements[elementIdx].Usage = D3D8_VERTEX_INPUT_REGISTERS[dataReg][0]; + vertexElements[elementIdx].UsageIndex = D3D8_VERTEX_INPUT_REGISTERS[dataReg][1]; + + // Enable register vn + shaderInputRegisters |= 1 << dataReg; + + // Finished with this element + elementIdx++; + + dbg << "type=" << dataType << ", register=" << dataReg; + } + break; + } + case D3DVSD_TOKEN_TESSELLATOR: + dbg << "TESSELLATOR " << std::hex << token; + // TODO: D3DVSD_TOKEN_TESSELLATOR + break; + case D3DVSD_TOKEN_CONSTMEM: { + dbg << "CONSTMEM "; + DWORD count = VSD_SHIFT_MASK(token, D3DVSD_CONSTCOUNT); + DWORD regCount = count * 4; + DWORD addr = VSD_SHIFT_MASK(token, D3DVSD_CONSTADDRESS); + DWORD rs = VSD_SHIFT_MASK(token, D3DVSD_CONSTRS); + + dbg << "count=" << count << ", addr=" << addr << ", rs=" << rs; + + // Add a DEF instruction for each constant + for (DWORD j = 0; j < regCount; j += 4) { + defs.push_back(encodeInstruction(d3d9::D3DSIO_DEF)); + defs.push_back(encodeDestRegister(d3d9::D3DSPR_CONST2, addr)); + defs.push_back(pDeclaration[i+j+0]); + defs.push_back(pDeclaration[i+j+1]); + defs.push_back(pDeclaration[i+j+2]); + defs.push_back(pDeclaration[i+j+3]); + addr++; + } + i += regCount; + break; + } + case D3DVSD_TOKEN_EXT: { + dbg << "EXT " << std::hex << token << " "; + DWORD extInfo = VSD_SHIFT_MASK(token, D3DVSD_EXTINFO); + DWORD extCount = VSD_SHIFT_MASK(token, D3DVSD_EXTCOUNT); + dbg << "info=" << extInfo << ", count=" << extCount; + // TODO: D3DVSD_TOKEN_EXT + break; + } + case D3DVSD_TOKEN_END: { + using d3d9::D3DDECLTYPE_UNUSED; + + vertexElements[elementIdx] = D3DDECL_END(); + + // Finished with this element + elementIdx++; + + dbg << "END"; + break; + } + default: + dbg << "UNKNOWN TYPE"; + break; + } + dbg << "\n\t"; + + //dbg << std::hex << token << " "; + + } while (token != D3DVSD_END()); + Logger::debug(dbg.str()); + + + if (pFunction != nullptr) { + // copy first token + // TODO: ensure first token is always only one dword + tokens.push_back(pFunction[0]); + + DWORD vsMajor = D3DSHADER_VERSION_MAJOR(pFunction[0]); + DWORD vsMinor = D3DSHADER_VERSION_MINOR(pFunction[0]); + Logger::debug(str::format("VS version: ", vsMajor, ".", vsMinor)); + + // insert dcl instructions + for (int vn = 0; vn < D3D8_NUM_VERTEX_INPUT_REGISTERS; vn++) { + + // if bit N is set then we need to dcl register vN + if ((shaderInputRegisters & (1 << vn)) != 0) { + + Logger::debug(str::format("\tShader Input Regsiter: v", vn)); + + DWORD usage = D3D8_VERTEX_INPUT_REGISTERS[vn][0]; + DWORD index = D3D8_VERTEX_INPUT_REGISTERS[vn][1]; + + tokens.push_back(encodeInstruction(d3d9::D3DSIO_DCL)); // dcl opcode + tokens.push_back(encodeDeclaration(d3d9::D3DDECLUSAGE(usage), index)); // usage token + tokens.push_back(encodeDestRegister(d3d9::D3DSPR_INPUT, vn)); // dest register num + } + } + + // copy constant defs + for (DWORD def : defs) { + tokens.push_back(def); + } + + // copy shader tokens from input, + // skip first token (we already copied it) + i = 1; + do { + token = pFunction[i++]; + + DWORD opcode = token & D3DSI_OPCODE_MASK; + + // Instructions + if ((token & VS_BIT_PARAM) == 0) { + + // RSQ swizzle fixup + if (opcode == D3DSIO_RSQ) { + tokens.push_back(token); // instr + tokens.push_back(token = pFunction[i++]); // dest + token = pFunction[i++]; // src0 + + // If no swizzling is done, then use the w-component. + // See d8vk#43 for more information as this may need to change in some cases. + if (((token & D3DVS_NOSWIZZLE) == D3DVS_NOSWIZZLE)) { + token &= ~D3DVS_SWIZZLE_MASK; + token |= (D3DVS_X_W | D3DVS_Y_W | D3DVS_Z_W | D3DVS_W_W); + } + } + } + tokens.push_back(token); + + //Logger::debug(str::format(std::hex, token)); + } while (token != D3DVS_END()); + } + + return result; + } + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_shader.h b/src/d3d8/d3d8_shader.h new file mode 100644 index 00000000..b4dac9ae --- /dev/null +++ b/src/d3d8/d3d8_shader.h @@ -0,0 +1,14 @@ +#pragma once + +#include "d3d8_include.h" + +namespace dxvk { + + struct D3D9VertexShaderCode { + d3d9::D3DVERTEXELEMENT9 declaration[MAXD3DDECLLENGTH + 1]; + std::vector function; + }; + + D3D9VertexShaderCode translateVertexShader8(const DWORD* pDeclaration, const DWORD* pFunction); + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_state_block.cpp b/src/d3d8/d3d8_state_block.cpp new file mode 100644 index 00000000..be2bcd24 --- /dev/null +++ b/src/d3d8/d3d8_state_block.cpp @@ -0,0 +1,44 @@ +#include "d3d8_device.h" +#include "d3d8_state_block.h" + +HRESULT dxvk::D3D8StateBlock::Capture() { + + if (unlikely(m_stateBlock == nullptr)) return D3DERR_INVALIDCALL; + + if (m_capture.vs) m_device->GetVertexShader(&m_vertexShader); + if (m_capture.ps) m_device->GetPixelShader(&m_pixelShader); + + for (DWORD stage = 0; stage < m_textures.size(); stage++) + if (m_capture.textures.get(stage)) + m_device->GetTexture(stage, &m_textures[stage]); + + if (m_capture.indices) m_device->GetIndices(&m_indices, &m_baseVertexIndex); + + if (m_capture.swvp) + m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, (DWORD*)&m_isSWVP); + + return m_stateBlock->Capture(); +} + +HRESULT dxvk::D3D8StateBlock::Apply() { + + if (unlikely(m_stateBlock == nullptr)) return D3DERR_INVALIDCALL; + + + HRESULT res = m_stateBlock->Apply(); + + if (m_capture.vs) m_device->SetVertexShader(m_vertexShader); + if (m_capture.ps) m_device->SetPixelShader(m_pixelShader); + + for (DWORD stage = 0; stage < m_textures.size(); stage++) + if (m_capture.textures.get(stage)) + m_device->SetTexture(stage, m_textures[stage] ); + + if (m_capture.indices) m_device->SetIndices(m_indices, m_baseVertexIndex); + + // This was a very easy footgun for D3D8 applications. + if (m_capture.swvp) + m_device->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, m_isSWVP); + + return res; +} diff --git a/src/d3d8/d3d8_state_block.h b/src/d3d8/d3d8_state_block.h new file mode 100644 index 00000000..e9cc7bd6 --- /dev/null +++ b/src/d3d8/d3d8_state_block.h @@ -0,0 +1,116 @@ +#pragma once + +#include "d3d8_caps.h" +#include "d3d8_include.h" +#include "d3d8_device.h" +#include "d3d8_device_child.h" + +#include + +namespace dxvk { + + struct D3D8StateCapture { + bool vs : 1; + bool ps : 1; + bool indices : 1; + bool swvp : 1; + + bit::bitset textures; + + D3D8StateCapture() + : vs(false) + , ps(false) + , indices(false) + , swvp(false) { + // Ensure all bits are initialized to false + textures.clearAll(); + } + }; + + // Wrapper class for D3D9 state blocks. Captures D3D8-specific state. + class D3D8StateBlock { + + public: + + D3D8StateBlock( + D3D8DeviceEx* pDevice, + Com&& pStateBlock) + : m_device(pDevice) + , m_stateBlock(std::move(pStateBlock)) { + + + m_textures.fill(nullptr); + } + + ~D3D8StateBlock() {} + + // Construct a state block without a D3D9 object + D3D8StateBlock(D3D8DeviceEx* pDevice) + : D3D8StateBlock(pDevice, nullptr) { + } + + // Attach a D3D9 object to a state block that doesn't have one yet + void SetD3D9(Com&& pStateBlock) { + if (likely(m_stateBlock == nullptr)) { + m_stateBlock = std::move(pStateBlock); + } else { + Logger::err("D3D8StateBlock::SetD3D9 called when m_stateBlock has already been initialized"); + } + } + + HRESULT Capture(); + + HRESULT Apply(); + + inline HRESULT SetVertexShader(DWORD Handle) { + m_vertexShader = Handle; + m_capture.vs = true; + return D3D_OK; + } + + inline HRESULT SetPixelShader(DWORD Handle) { + m_pixelShader = Handle; + m_capture.ps = true; + return D3D_OK; + } + + inline HRESULT SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) { + m_textures[Stage] = pTexture; + m_capture.textures.set(Stage, true); + return D3D_OK; + } + + inline HRESULT SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) { + m_indices = pIndexData; + m_baseVertexIndex = BaseVertexIndex; + m_capture.indices = true; + return D3D_OK; + } + + inline HRESULT SetSoftwareVertexProcessing(bool value) { + m_isSWVP = value; + m_capture.swvp = true; + return D3D_OK; + } + + private: + D3D8DeviceEx* m_device; + Com m_stateBlock; + + // State Data // + + D3D8StateCapture m_capture; + + DWORD m_vertexShader; // vs + DWORD m_pixelShader; // ps + + std::array m_textures; // textures + + IDirect3DIndexBuffer8* m_indices = nullptr; // indices + UINT m_baseVertexIndex; // indices + + bool m_isSWVP; // D3DRS_SOFTWAREVERTEXPROCESSING + }; + + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_subresource.h b/src/d3d8/d3d8_subresource.h new file mode 100644 index 00000000..6abbf704 --- /dev/null +++ b/src/d3d8/d3d8_subresource.h @@ -0,0 +1,61 @@ +#pragma once + +#include "d3d8_resource.h" + +namespace dxvk { + + // Base class for Surfaces and Volumes, + // which can be attached to Textures. + + template + class D3D8Subresource : public D3D8Resource { + + using Resource = D3D8Resource; + + public: + + D3D8Subresource( + D3D8DeviceEx* pDevice, + Com&& Object, + IDirect3DBaseTexture8* pBaseTexture) + : Resource(pDevice, std::move(Object)), + m_container(pBaseTexture) { + } + + ~D3D8Subresource() { + } + + // Refing subresources implicitly refs the container texture, + ULONG STDMETHODCALLTYPE AddRef() final { + if (m_container != nullptr) + return m_container->AddRef(); + + return Resource::AddRef(); + } + + // and releasing them implicitly releases the texture. + ULONG STDMETHODCALLTYPE Release() final { + if (m_container != nullptr) + return m_container->Release(); + + return Resource::Release(); + } + + // Clients can grab the container if they want + HRESULT STDMETHODCALLTYPE GetContainer(REFIID riid, void** ppContainer) final { + if (m_container != nullptr) + return m_container->QueryInterface(riid, ppContainer); + + return this->GetDevice()->QueryInterface(riid, ppContainer); + } + + inline IDirect3DBaseTexture8* GetBaseTexture() { + return m_container; + } + + protected: + + IDirect3DBaseTexture8* m_container; + }; + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_surface.cpp b/src/d3d8/d3d8_surface.cpp new file mode 100644 index 00000000..b3823914 --- /dev/null +++ b/src/d3d8/d3d8_surface.cpp @@ -0,0 +1,25 @@ + +#include "d3d8_surface.h" +#include "d3d8_device.h" + +namespace dxvk { + + Com D3D8Surface::CreateBlitImage() { + d3d9::D3DSURFACE_DESC desc; + GetD3D9()->GetDesc(&desc); + + Com image = nullptr; + HRESULT res = GetParent()->GetD3D9()->CreateRenderTarget( + desc.Width, desc.Height, desc.Format, + d3d9::D3DMULTISAMPLE_NONE, 0, + FALSE, + &image, + NULL); + + if (FAILED(res)) + throw new DxvkError("D3D8: Failed to create blit image"); + + image.ref(); + return image; + } +} \ No newline at end of file diff --git a/src/d3d8/d3d8_surface.h b/src/d3d8/d3d8_surface.h new file mode 100644 index 00000000..35f9f38a --- /dev/null +++ b/src/d3d8/d3d8_surface.h @@ -0,0 +1,84 @@ +#pragma once + + +// Implements IDirect3DSurface8 + +#include "d3d8_include.h" +#include "d3d8_subresource.h" +#include "d3d8_d3d9_util.h" + +namespace dxvk { + + // TODO: all inherited methods in D3D8Surface should be final like in d9vk + + using D3D8SurfaceBase = D3D8Subresource; + class D3D8Surface final : public D3D8SurfaceBase { + + public: + + D3D8Surface( + D3D8DeviceEx* pDevice, + IDirect3DBaseTexture8* pTexture, + Com&& pSurface) + : D3D8SurfaceBase (pDevice, std::move(pSurface), pTexture) { + } + + // A surface does not need to be attached to a texture + D3D8Surface( + D3D8DeviceEx* pDevice, + Com&& pSurface) + : D3D8Surface (pDevice, nullptr, std::move(pSurface)) { + } + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() { + return D3DRESOURCETYPE(GetD3D9()->GetType()); + } + + HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC* pDesc) { + d3d9::D3DSURFACE_DESC desc; + HRESULT res = GetD3D9()->GetDesc(&desc); + ConvertSurfaceDesc8(&desc, pDesc); + return res; + } + + HRESULT STDMETHODCALLTYPE LockRect(D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) { + return GetD3D9()->LockRect((d3d9::D3DLOCKED_RECT*)pLockedRect, pRect, Flags); + } + + HRESULT STDMETHODCALLTYPE UnlockRect() { + return GetD3D9()->UnlockRect(); + } + + HRESULT STDMETHODCALLTYPE GetDC(HDC* phDC) { + return GetD3D9()->GetDC(phDC); + } + + HRESULT STDMETHODCALLTYPE ReleaseDC(HDC hDC) { + return GetD3D9()->ReleaseDC(hDC); + } + + public: + + /** + * \brief Allocate or reuse an image of the same size + * as this texture for performing blit into system mem. + * + * TODO: Consider creating only one texture to + * encompass all surface levels of a texture. + */ + Com GetBlitImage() { + if (unlikely(m_blitImage == nullptr)) { + m_blitImage = CreateBlitImage(); + } + + return m_blitImage; + } + + + private: + Com CreateBlitImage(); + + Com m_blitImage = nullptr; + + }; +} \ No newline at end of file diff --git a/src/d3d8/d3d8_swapchain.h b/src/d3d8/d3d8_swapchain.h new file mode 100644 index 00000000..277df86d --- /dev/null +++ b/src/d3d8/d3d8_swapchain.h @@ -0,0 +1,42 @@ +#pragma once + +#include "d3d8_device_child.h" +#include "d3d8_surface.h" +#include "d3d8_d3d9_util.h" + +namespace dxvk { + + using D3D8SwapChainBase = D3D8DeviceChild; + class D3D8SwapChain final : public D3D8SwapChainBase { + + public: + + D3D8SwapChain( + D3D8DeviceEx* pDevice, + Com&& pSwapChain) + : D3D8SwapChainBase(pDevice, std::move(pSwapChain)) {} + + HRESULT STDMETHODCALLTYPE Present(const RECT *src, const RECT *dst, HWND hWnd, const RGNDATA *dirtyRegion) final { + return GetD3D9()->Present(src, dst, hWnd, dirtyRegion, 0); + } + + HRESULT STDMETHODCALLTYPE GetBackBuffer(UINT BackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface8** ppBackBuffer) final { + // Same logic as in D3D8DeviceEx::GetBackBuffer + if (unlikely(m_backBuffer == nullptr)) { + Com pSurface9; + HRESULT res = GetD3D9()->GetBackBuffer(BackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9); + + m_backBuffer = new D3D8Surface(GetParent(), std::move(pSurface9)); + *ppBackBuffer = m_backBuffer.ref(); + return res; + } + + *ppBackBuffer = m_backBuffer.ref(); + return D3D_OK; + } + private: + Com m_backBuffer = nullptr; + + }; + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_texture.h b/src/d3d8/d3d8_texture.h new file mode 100644 index 00000000..97570f32 --- /dev/null +++ b/src/d3d8/d3d8_texture.h @@ -0,0 +1,243 @@ +#pragma once + +#include "d3d8_resource.h" +#include "d3d8_surface.h" +#include "d3d8_volume.h" + +#include "d3d8_d3d9_util.h" + +#include +#include + +namespace dxvk { + + + // Implements IDirect3DBaseTexture8 (Except GetType) // + + template + class D3D8BaseTexture : public D3D8Resource { + + public: + + constexpr static UINT CUBE_FACES = 6; + + using SubresourceType8 = typename SubresourceType::D3D8; + using SubresourceType9 = typename SubresourceType::D3D9; + + D3D8BaseTexture( + D3D8DeviceEx* pDevice, + Com&& pBaseTexture, + UINT SubresourceCount) + : D3D8Resource ( pDevice, std::move(pBaseTexture) ) { + m_subresources.resize(SubresourceCount, nullptr); + } + + ~D3D8BaseTexture() { + for (size_t i = 0; i < m_subresources.size(); i++) + if (m_subresources[i] != nullptr) + m_subresources[i] = nullptr; + } + + virtual IUnknown* GetInterface(REFIID riid) final override try { + return D3D8Resource::GetInterface(riid); + } catch (HRESULT err) { + if (riid == __uuidof(IDirect3DBaseTexture8)) + return this; + + throw err; + } + + void STDMETHODCALLTYPE PreLoad() final { + this->GetD3D9()->PreLoad(); + } + + DWORD STDMETHODCALLTYPE SetLOD(DWORD LODNew) final { + return this->GetD3D9()->SetLOD(LODNew); + } + + DWORD STDMETHODCALLTYPE GetLOD() final { + return this->GetD3D9()->GetLOD(); + } + + DWORD STDMETHODCALLTYPE GetLevelCount() final { + return this->GetD3D9()->GetLevelCount(); + } + + protected: + + HRESULT STDMETHODCALLTYPE GetSubresource(UINT Index, SubresourceType8** ppSubresource) { + InitReturnPtr(ppSubresource); + + if (unlikely(Index >= m_subresources.size())) + return D3DERR_INVALIDCALL; + + if (m_subresources[Index] == nullptr) { + try { + Com subresource = LookupSubresource(Index); + + // Cache the subresource + m_subresources[Index] = new SubresourceType(this->m_parent, this, std::move(subresource)); + } catch (HRESULT res) { + return res; + } + } + + *ppSubresource = m_subresources[Index].ref(); + return D3D_OK; + } + + private: + + SubresourceType9* LookupSubresource(UINT Index) { + SubresourceType9* ptr = nullptr; + HRESULT res = D3DERR_INVALIDCALL; + if constexpr (std::is_same_v) { + res = this->GetD3D9()->GetSurfaceLevel(Index, &ptr); + } else if constexpr (std::is_same_v) { + res = this->GetD3D9()->GetVolumeLevel(Index, &ptr); + } else if constexpr (std::is_same_v) { + res = this->GetD3D9()->GetCubeMapSurface(d3d9::D3DCUBEMAP_FACES(Index % CUBE_FACES), Index / CUBE_FACES, &ptr); + } + if (FAILED(res)) + throw res; + return ptr; + } + + std::vector> m_subresources; + + }; + + // Implements IDirect3DTexture8 // + + using D3D8Texture2DBase = D3D8BaseTexture; + class D3D8Texture2D final : public D3D8Texture2DBase { + + public: + + D3D8Texture2D( + D3D8DeviceEx* pDevice, + Com&& pTexture) + : D3D8Texture2DBase(pDevice, std::move(pTexture), pTexture->GetLevelCount()) { + } + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_TEXTURE; } + + HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) { + d3d9::D3DSURFACE_DESC surf; + HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf); + ConvertSurfaceDesc8(&surf, pDesc); + return res; + } + + HRESULT STDMETHODCALLTYPE GetSurfaceLevel(UINT Level, IDirect3DSurface8** ppSurfaceLevel) { + return GetSubresource(Level, ppSurfaceLevel); + } + + HRESULT STDMETHODCALLTYPE LockRect(UINT Level, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) { + return GetD3D9()->LockRect(Level, reinterpret_cast(pLockedRect), pRect, Flags); + } + + HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level) { + return GetD3D9()->UnlockRect(Level); + } + + HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect) { + return GetD3D9()->AddDirtyRect(pDirtyRect); + } + + }; + + // Implements IDirect3DVolumeTexture8 // + + using D3D8Texture3DBase = D3D8BaseTexture; + class D3D8Texture3D final : public D3D8Texture3DBase { + + public: + + D3D8Texture3D( + D3D8DeviceEx* pDevice, + Com&& pVolumeTexture) + : D3D8Texture3DBase(pDevice, std::move(pVolumeTexture), pVolumeTexture->GetLevelCount()) {} + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_VOLUMETEXTURE; } + + HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DVOLUME_DESC *pDesc) { + d3d9::D3DVOLUME_DESC vol; + HRESULT res = GetD3D9()->GetLevelDesc(Level, &vol); + ConvertVolumeDesc8(&vol, pDesc); + return res; + } + + HRESULT STDMETHODCALLTYPE GetVolumeLevel(UINT Level, IDirect3DVolume8** ppVolumeLevel) { + return GetSubresource(Level, ppVolumeLevel); + } + + HRESULT STDMETHODCALLTYPE LockBox(UINT Level, D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) { + return GetD3D9()->LockBox( + Level, + reinterpret_cast(pLockedBox), + reinterpret_cast(pBox), + Flags + ); + } + + HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level) { + return GetD3D9()->UnlockBox(Level); + } + + HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox) { + return GetD3D9()->AddDirtyBox(reinterpret_cast(pDirtyBox)); + } + + }; + + + // Implements IDirect3DCubeTexture8 // + + using D3D8TextureCubeBase = D3D8BaseTexture; + class D3D8TextureCube final : public D3D8TextureCubeBase { + + public: + + D3D8TextureCube( + D3D8DeviceEx* pDevice, + Com&& pTexture) + : D3D8TextureCubeBase(pDevice, std::move(pTexture), pTexture->GetLevelCount() * CUBE_FACES) { + } + + D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_CUBETEXTURE; } + + HRESULT STDMETHODCALLTYPE GetLevelDesc(UINT Level, D3DSURFACE_DESC* pDesc) { + d3d9::D3DSURFACE_DESC surf; + HRESULT res = GetD3D9()->GetLevelDesc(Level, &surf); + ConvertSurfaceDesc8(&surf, pDesc); + return res; + } + + HRESULT STDMETHODCALLTYPE GetCubeMapSurface(D3DCUBEMAP_FACES Face, UINT Level, IDirect3DSurface8** ppSurfaceLevel) { + return GetSubresource((Level * CUBE_FACES) + Face, ppSurfaceLevel); + } + + HRESULT STDMETHODCALLTYPE LockRect( + D3DCUBEMAP_FACES Face, + UINT Level, + D3DLOCKED_RECT* pLockedRect, + const RECT* pRect, + DWORD Flags) { + return GetD3D9()->LockRect( + d3d9::D3DCUBEMAP_FACES(Face), + Level, + reinterpret_cast(pLockedRect), + pRect, + Flags); + } + + HRESULT STDMETHODCALLTYPE UnlockRect(D3DCUBEMAP_FACES Face, UINT Level) { + return GetD3D9()->UnlockRect(d3d9::D3DCUBEMAP_FACES(Face), Level); + } + + HRESULT STDMETHODCALLTYPE AddDirtyRect(D3DCUBEMAP_FACES Face, const RECT* pDirtyRect) { + return GetD3D9()->AddDirtyRect(d3d9::D3DCUBEMAP_FACES(Face), pDirtyRect); + } + }; +} \ No newline at end of file diff --git a/src/d3d8/d3d8_volume.h b/src/d3d8/d3d8_volume.h new file mode 100644 index 00000000..8b45ad83 --- /dev/null +++ b/src/d3d8/d3d8_volume.h @@ -0,0 +1,40 @@ +#pragma once + +#include "d3d8_subresource.h" +#include "d3d8_d3d9_util.h" + +namespace dxvk { + + using D3D8VolumeBase = D3D8Subresource; + class D3D8Volume final : public D3D8VolumeBase { + + public: + + D3D8Volume( + D3D8DeviceEx* pDevice, + IDirect3DVolumeTexture8* pTexture, + Com&& pVolume) + : D3D8VolumeBase(pDevice, std::move(pVolume), pTexture) {} + + HRESULT STDMETHODCALLTYPE GetDesc(D3DVOLUME_DESC* pDesc) { + d3d9::D3DVOLUME_DESC desc; + HRESULT res = GetD3D9()->GetDesc(&desc); + ConvertVolumeDesc8(&desc, pDesc); + return res; + } + + HRESULT STDMETHODCALLTYPE LockBox(D3DLOCKED_BOX* pLockedBox, CONST D3DBOX* pBox, DWORD Flags) final { + return GetD3D9()->LockBox( + reinterpret_cast(pLockedBox), + reinterpret_cast(pBox), + Flags + ); + } + + HRESULT STDMETHODCALLTYPE UnlockBox() final { + return GetD3D9()->UnlockBox(); + } + + }; + +} \ No newline at end of file diff --git a/src/d3d8/d3d8_wrapped_object.h b/src/d3d8/d3d8_wrapped_object.h new file mode 100644 index 00000000..e79fecf7 --- /dev/null +++ b/src/d3d8/d3d8_wrapped_object.h @@ -0,0 +1,68 @@ +#pragma once + +#include "d3d8_include.h" + +namespace dxvk { + + template + class D3D8WrappedObject : public ComObjectClamp { + + public: + + using D3D9 = D3D9Type; + using D3D8 = D3D8Type; + + D3D8WrappedObject(Com&& object) + : m_d3d9(std::move(object)) { + } + + D3D9* GetD3D9() { + return m_d3d9.ptr(); + } + + // For cases where the object may be null. + static D3D9* GetD3D9Nullable(D3D8WrappedObject* self) { + if (unlikely(self == NULL)) { + return NULL; + } + return self->m_d3d9.ptr(); + } + + template + static D3D9* GetD3D9Nullable(Com& self) { + return GetD3D9Nullable(self.ptr()); + } + + virtual IUnknown* GetInterface(REFIID riid) { + if (riid == __uuidof(IUnknown)) + return this; + if (riid == __uuidof(D3D8)) + return this; + + throw E_NOINTERFACE; + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) final { + if (ppvObject == nullptr) + return E_POINTER; + + *ppvObject = nullptr; + + try { + *ppvObject = ref(this->GetInterface(riid)); + return S_OK; + } catch (HRESULT err) { + Logger::warn("D3D8WrappedObject::QueryInterface: Unknown interface query"); + Logger::warn(str::format(riid)); + return err; + } + } + + + private: + + Com m_d3d9; + + }; + +} \ No newline at end of file diff --git a/src/d3d8/meson.build b/src/d3d8/meson.build new file mode 100644 index 00000000..07de59ef --- /dev/null +++ b/src/d3d8/meson.build @@ -0,0 +1,25 @@ +d3d8_res = wrc_generator.process('version.rc') + +d3d8_src = [ + 'd3d8_main.cpp', + 'd3d8_interface.cpp', + 'd3d8_device.cpp', + 'd3d8_surface.cpp', + 'd3d8_blit.cpp', + 'd3d8_state_block.cpp', + 'd3d8_shader.cpp' +] + +if platform != 'windows' + lib_d3d9 = d3d9_dep +endif + +d3d8_dll = shared_library('d3d8'+dll_ext, d3d8_src, d3d8_res, + name_prefix : '', + dependencies : [ lib_d3d9, util_dep, dxso_dep, dxvk_dep ], + include_directories : dxvk_include_path, + install : true, + objects : (not dxvk_is_msvc and platform == 'windows') ? 'd3d8'+def_spec_ext : [], + vs_module_defs : 'd3d8'+def_spec_ext +) + diff --git a/src/d3d8/version.rc b/src/d3d8/version.rc new file mode 100644 index 00000000..9e2b099f --- /dev/null +++ b/src/d3d8/version.rc @@ -0,0 +1,31 @@ +#include + +// 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 8 Runtime" + VALUE "FileVersion", "10.0.17763.1 (WinBuild.160101.0800)" + VALUE "InternalName", "D3D8.dll" + VALUE "LegalCopyright", "zlib/libpng license" + VALUE "OriginalFilename", "D3D8.dll" + VALUE "ProductName", "DXVK" + VALUE "ProductVersion", "10.0.17763.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0809, 1200 + END +END diff --git a/src/d3d9/d3d9_bridge.cpp b/src/d3d9/d3d9_bridge.cpp new file mode 100644 index 00000000..ca8a022a --- /dev/null +++ b/src/d3d9/d3d9_bridge.cpp @@ -0,0 +1,87 @@ + +#include "d3d9_device.h" +#include "d3d9_interface.h" +#include "d3d9_bridge.h" +#include "d3d9_swapchain.h" +#include "d3d9_surface.h" + +namespace dxvk { + + ULONG D3D9Bridge::AddRef() { + return m_device->AddRef(); + } + + ULONG D3D9Bridge::Release() { + return m_device->Release(); + } + + void D3D9Bridge::SetAPIName(const char* name) { + m_device->m_implicitSwapchain->SetApiName(name); + } + + void D3D9Bridge::SetShadowBuffersEnabled(bool enabled) { + m_device->m_dxsoOptions.drefScaling = enabled; + m_device->m_dxsoOptions.shadowFilter = enabled; + } + + HRESULT D3D9Bridge::UpdateTextureFromBuffer( + IDirect3DSurface9* pDestSurface, + IDirect3DSurface9* pSrcSurface, + const RECT* pSrcRect, + const POINT* pDestPoint) { + auto lock = m_device->LockDevice(); + + D3D9Surface* dst = static_cast(pDestSurface); + D3D9Surface* src = static_cast(pSrcSurface); + + if (unlikely(dst == nullptr || src == nullptr)) + return D3DERR_INVALIDCALL; + + D3D9CommonTexture* srcTextureInfo = src->GetCommonTexture(); + D3D9CommonTexture* dstTextureInfo = dst->GetCommonTexture(); + + VkOffset3D srcOffset = { 0u, 0u, 0u }; + VkOffset3D dstOffset = { 0u, 0u, 0u }; + VkExtent3D texLevelExtent = srcTextureInfo->GetExtentMip(src->GetSubresource()); + VkExtent3D extent = texLevelExtent; + + srcOffset = { pSrcRect->left, + pSrcRect->top, + 0u }; + + extent = { uint32_t(pSrcRect->right - pSrcRect->left), uint32_t(pSrcRect->bottom - pSrcRect->top), 1 }; + + // TODO: Validate extents like in D3D9DeviceEx::UpdateSurface + + dstOffset = { pDestPoint->x, + pDestPoint->y, + 0u }; + + + m_device->UpdateTextureFromBuffer( + srcTextureInfo, dstTextureInfo, + src->GetSubresource(), dst->GetSubresource(), + srcOffset, extent, dstOffset + ); + + dstTextureInfo->SetNeedsReadback(dst->GetSubresource(), true); + + if (dstTextureInfo->IsAutomaticMip()) + m_device->MarkTextureMipsDirty(dstTextureInfo); + + return D3D_OK; + } + + + ULONG D3D9InterfaceBridge::AddRef() { + return m_interface->AddRef(); + } + + ULONG D3D9InterfaceBridge::Release() { + return m_interface->Release(); + } + + const Config& D3D9InterfaceBridge::GetConfig() const { + return m_interface->GetInstance()->config(); + } +} \ No newline at end of file diff --git a/src/d3d9/d3d9_bridge.h b/src/d3d9/d3d9_bridge.h new file mode 100644 index 00000000..83ff4546 --- /dev/null +++ b/src/d3d9/d3d9_bridge.h @@ -0,0 +1,111 @@ +#pragma once + +#include +#include "../util/config/config.h" +#include "../util/util_error.h" +#include "../util/log/log.h" + +#include "../vulkan/vulkan_loader.h" + +#include + +// NOTE: You must include "d3d9_include.h" or "d3d8_include.h" before this header. + +/** + * The D3D9 bridge allows D3D8 to access DXVK internals. + * For Vulkan interop without needing DXVK internals, see d3d9_interop.h. + */ + +namespace dxvk { + + class D3D9DeviceEx; + class D3D9InterfaceEx; + +#if defined(_MSC_VER) + class DECLSPEC_UUID("D3D9ACAB-42A9-4C1E-AA97-DEADFADED420") D3D9Bridge; + class DECLSPEC_UUID("D3D9ACAB-A407-773E-18E9-CAFEBEEF2000") D3D9InterfaceBridge; +#endif + + class D3D9Bridge { + + // D3D8 keeps D3D9 objects contained in a namespace. + #ifdef DXVK_D3D9_NAMESPACE + using IDirect3DSurface9 = d3d9::IDirect3DSurface9; + #endif + + public: + using Type = D3D9DeviceEx; + + D3D9Bridge(D3D9DeviceEx* pDevice) : m_device(pDevice) {} + virtual ~D3D9Bridge() = default; + + ULONG AddRef(); + ULONG Release(); + + virtual void SetAPIName(const char* name); + + virtual void SetShadowBuffersEnabled(bool enabled); + + virtual HRESULT UpdateTextureFromBuffer( + IDirect3DSurface9* pDestSurface, + IDirect3DSurface9* pSrcSurface, + const RECT* pSrcRect, + const POINT* pDestPoint); + + + // Indicate that D3D9 shouldn't complain about an unhandled + // D3DRENDERSTATE because it's implemented elsewhere. + template + inline void AddSupportedRenderStates(Args... state) { + ((m_supportedRenderStates[state] = true), ...); + } + + inline void RenderStateNotSupported(uint8_t renderState) { + m_supportedRenderStates[renderState] = false; + } + + constexpr bool IsRenderStateSupported(uint8_t renderState) const { + return m_supportedRenderStates[renderState]; + } + + private: + D3D9DeviceEx* m_device; + std::bitset m_supportedRenderStates; + }; + + class D3D9InterfaceBridge { + + public: + using Type = D3D9InterfaceEx; + + D3D9InterfaceBridge(D3D9InterfaceEx* pObject) : m_interface(pObject) {} + virtual ~D3D9InterfaceBridge() = default; + + ULONG AddRef(); + ULONG Release(); + + virtual const Config& GetConfig() const; + + protected: + D3D9InterfaceEx* m_interface; + }; + + // Get a bridge interface to D3D9 (D3D9Bridge, D3D9InterfaceBridge). + // Pass this the relevant D3D9 object. + template + B* GetD3D9Bridge(T* object) { + B* pointer; + if (FAILED(object->QueryInterface(__uuidof(B), (void**)&pointer))) { + Logger::err("GetD3D9Bridge: ERROR! Failed to get D3D9 Bridge. d3d9.dll might be DXVK, but not a version built for D8VK!"); + Logger::err("Please install the d3d9.dll that came with d3d8.dll"); + throw DxvkError("GetD3D9Bridge: ERROR! Failed to get D3D9 Bridge. d3d9.dll might be DXVK, but not a version built for D8VK!"); + } + return pointer; + } + +} + +#if !defined(_MSC_VER) +__CRT_UUID_DECL(dxvk::D3D9Bridge, 0xD3D9ACAB, 0x42A9, 0x4C1E, 0xAA, 0x97, 0xDE, 0xAD, 0xFA, 0xDE, 0xD4, 0x20); +__CRT_UUID_DECL(dxvk::D3D9InterfaceBridge, 0xD3D9ACAB, 0xA407, 0x773E, 0x18, 0xE9, 0xCA, 0xFE, 0xBE, 0xEF, 0x20, 0x00); +#endif diff --git a/src/d3d9/d3d9_common_texture.cpp b/src/d3d9/d3d9_common_texture.cpp index 8cb723d2..bc988853 100644 --- a/src/d3d9/d3d9_common_texture.cpp +++ b/src/d3d9/d3d9_common_texture.cpp @@ -356,7 +356,7 @@ namespace dxvk { if (!CheckImageSupport(&imageInfo, imageInfo.tiling)) { throw DxvkError(str::format( "D3D9: Cannot create texture:", - "\n Type: ", std::hex, ResourceType, + "\n Type: 0x", std::hex, ResourceType, std::dec, "\n Format: ", m_desc.Format, "\n Extent: ", m_desc.Width, "x", m_desc.Height, @@ -364,8 +364,8 @@ namespace dxvk { "\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)); + "\n Usage: 0x", std::hex, m_desc.Usage, std::dec, + "\n Pool: 0x", std::hex, m_desc.Pool, std::dec)); } return m_device->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index e8ef3012..2a5259a4 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -55,7 +55,8 @@ namespace dxvk { , m_isSWVP ( (BehaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) ? true : false ) , m_csThread ( dxvkDevice, dxvkDevice->createContext(DxvkContextType::Primary) ) , m_csChunk ( AllocCsChunk() ) - , m_d3d9Interop ( this ) { + , m_d3d9Interop ( this ) + , m_bridge ( this ) { // If we can SWVP, then we use an extended constant set // as SWVP has many more slots available than HWVP. bool canSWVP = CanSWVP(); @@ -191,6 +192,11 @@ namespace dxvk { *ppvObject = ref(this); return S_OK; } + + if (riid == __uuidof(D3D9Bridge)) { + *ppvObject = ref(&m_bridge); + return S_OK; + } if (riid == __uuidof(ID3D9VkInteropDevice)) { *ppvObject = ref(&m_d3d9Interop); @@ -2173,7 +2179,7 @@ namespace dxvk { default: static bool s_errorShown[256]; - if (!std::exchange(s_errorShown[State], true)) + if (!std::exchange(s_errorShown[State], true) && !m_bridge.IsRenderStateSupported(State)) Logger::warn(str::format("D3D9DeviceEx::SetRenderState: Unhandled render state ", State)); break; } @@ -3834,6 +3840,11 @@ namespace dxvk { m_drefClamp &= ~(1u << StateSampler); m_drefClamp |= uint32_t(newTexture->IsUpgradedToD32f()) << StateSampler; + const uint32_t offset = StateSampler * 2; + m_drefScale &= ~(0b11u << offset); + if (m_dxsoOptions.drefScaling || m_dxsoOptions.shadowFilter) + m_drefScale |= GetDepthBufferDrefScale(newTexture->Desc()->Format) << offset; + const bool oldCube = m_cubeTextures & (1u << StateSampler); const bool newCube = newTexture->GetType() == D3DRTYPE_CUBETEXTURE; if (oldCube != newCube) { @@ -6298,7 +6309,7 @@ namespace dxvk { const uint32_t nullTextureMask = usedSamplerMask & ~usedTextureMask; const uint32_t depthTextureMask = m_depthTextures & usedTextureMask; const uint32_t drefClampMask = m_drefClamp & depthTextureMask; - UpdateCommonSamplerSpec(nullTextureMask, depthTextureMask, drefClampMask); + UpdateCommonSamplerSpec(nullTextureMask, depthTextureMask, drefClampMask, m_drefScale); if (m_flags.test(D3D9DeviceFlag::DirtySharedPixelShaderData)) { m_flags.clr(D3D9DeviceFlag::DirtySharedPixelShaderData); @@ -6877,6 +6888,11 @@ namespace dxvk { stage.Projected = (ttff & D3DTTFF_PROJECTED) ? 1 : 0; stage.ProjectedCount = (ttff & D3DTTFF_PROJECTED) ? count : 0; + + if (m_dxsoOptions.drefScaling) + stage.DrefScale = D3D9DrefScale((m_drefScale >> samplerOffset) & 0b11u); + if (m_dxsoOptions.shadowFilter) + stage.ShadowFilter = (m_depthTextures & (1 << idx)) != 0; } auto& stage0 = key.Stages[0].Contents; @@ -7298,7 +7314,7 @@ namespace dxvk { UpdatePixelShaderSamplerSpec(0u, 0u, 0u); UpdateVertexBoolSpec(0u); UpdatePixelBoolSpec(0u); - UpdateCommonSamplerSpec(0u, 0u, 0u); + UpdateCommonSamplerSpec(0u, 0u, 0u, 0u); return D3D_OK; } @@ -7494,10 +7510,11 @@ namespace dxvk { } - void D3D9DeviceEx::UpdateCommonSamplerSpec(uint32_t nullMask, uint32_t depthMask, uint32_t drefMask) { + void D3D9DeviceEx::UpdateCommonSamplerSpec(uint32_t nullMask, uint32_t depthMask, uint32_t drefMask, uint32_t drefScale) { bool dirty = m_specInfo.set(depthMask); dirty |= m_specInfo.set(nullMask); dirty |= m_specInfo.set(drefMask); + dirty |= m_specInfo.set(drefScale); if (dirty) m_flags.set(D3D9DeviceFlag::DirtySpecializationEntries); diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index 63e9fd47..6dba545c 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -30,6 +30,8 @@ #include "d3d9_interop.h" #include +#include "d3d9_bridge.h" + #include #include #include @@ -119,6 +121,7 @@ namespace dxvk { friend class D3D9SwapChainEx; friend class D3D9ConstantBuffer; friend class D3D9UserDefinedAnnotation; + friend class D3D9Bridge; friend D3D9VkInteropDevice; public: @@ -1145,7 +1148,7 @@ namespace dxvk { void UpdateVertexBoolSpec(uint32_t value); void UpdatePixelBoolSpec(uint32_t value); void UpdatePixelShaderSamplerSpec(uint32_t types, uint32_t projections, uint32_t fetch4); - void UpdateCommonSamplerSpec(uint32_t boundMask, uint32_t depthMask, uint32_t drefMask); + void UpdateCommonSamplerSpec(uint32_t boundMask, uint32_t depthMask, uint32_t drefMask, uint32_t drefScale); void UpdatePointModeSpec(uint32_t mode); void UpdateFogModeSpec(bool fogEnabled, D3DFOGMODE vertexFogMode, D3DFOGMODE pixelFogMode); @@ -1242,6 +1245,8 @@ namespace dxvk { uint32_t m_textureTypes = 0; uint32_t m_projectionBitfield = 0; + uint32_t m_drefScale = 0; + uint32_t m_dirtySamplerStates = 0; uint32_t m_dirtyTextures = 0; @@ -1322,6 +1327,8 @@ namespace dxvk { #endif D3D9VkInteropDevice m_d3d9Interop; + + D3D9Bridge m_bridge; }; } diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index 7b6b379b..fa279f1d 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -335,6 +335,49 @@ namespace dxvk { spvModule.opLabel(atestSkipLabel); } + uint32_t DoFixedFunctionShadowFilter( + SpirvModule& module, + uint32_t inSample, + uint32_t sampledImage, + uint32_t coordinates, + uint32_t reference, + const SpirvImageOperands& operands) { + + SpirvImageOperands imageOperands = operands; + imageOperands.flags |= spv::ImageOperandsConstOffsetMask; + + uint32_t f32 = module.defFloatType(32); + uint32_t vec4 = module.defVectorType(f32, 4); + uint32_t val = module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f); + + uint32_t index = 0; + auto Tap = [&](int du, int dv) { + imageOperands.sConstOffset = module.constvec4i32(du, dv, 0, 0); + uint32_t sample = module.opImageSampleDrefImplicitLod(f32, sampledImage, coordinates, reference, imageOperands); + val = module.opCompositeInsert(vec4, sample, val, 1, &index); + + if (index < 3) index++; else index = 0; + }; + + Tap(0, 1); + Tap(-1, 0); + Tap(1, 0); + Tap(0, -1); + + uint32_t denom = inSample == 0 + ? module.constvec4f32(0.25, 0.25, 0.25, 0.25) + : module.constvec4f32(0.20, 0.20, 0.20, 0.20); + + // Average the 4 samples together + val = module.opDot(f32, val, denom); + + // Average the 4 samples with the center sample, if any + if (inSample != 0) { + val = module.opFFma(f32, inSample, module.constf32(0.20), val); + } + + return val; + } uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count) { uint32_t floatType = spvModule.defFloatType(32); @@ -1778,6 +1821,11 @@ namespace dxvk { return coords; }; + auto ScalarReplicate = [&](uint32_t reg) { + std::array replicant = { reg, reg, reg, reg }; + return m_module.opCompositeConstruct(m_vec4Type, replicant.size(), replicant.data()); + }; + auto GetTexture = [&]() { if (!processedTexture) { SpirvImageOperands imageOperands; @@ -1785,9 +1833,13 @@ namespace dxvk { uint32_t texcoordCnt = m_ps.samplers[i].texcoordCnt; + D3D9DrefScale drefScale = D3D9DrefScale(stage.DrefScale); + bool drefScaled = drefScale != DrefScale_None; + bool shadowFilter = stage.ShadowFilter; + // Add one for the texcoord count // if we need to include the divider - if (m_fsKey.Stages[i].Contents.Projected) + if (m_fsKey.Stages[i].Contents.Projected || drefScaled || shadowFilter) texcoordCnt++; std::array indices = { 0, 1, 2, 3 }; @@ -1826,10 +1878,28 @@ namespace dxvk { shouldProject = false; } - if (shouldProject) - texture = m_module.opImageSampleProjImplicitLod(m_vec4Type, imageVarId, texcoord, imageOperands); - else - texture = m_module.opImageSampleImplicitLod(m_vec4Type, imageVarId, texcoord, imageOperands); + if (unlikely(shadowFilter || drefScaled)) { + uint32_t component = 2; + uint32_t reference = m_module.opCompositeExtract(m_floatType, texcoord, 1, &component); + + if (drefScaled) { + uint32_t maxDref = m_module.constf32(GetDrefScaleFactor(drefScale)); + reference = m_module.opFMul(m_floatType, reference, maxDref); + } + + texture = m_module.opImageSampleDrefImplicitLod(m_floatType, imageVarId, texcoord, reference, imageOperands); + + if (shadowFilter) { + texture = DoFixedFunctionShadowFilter(m_module, texture, imageVarId, texcoord, reference, imageOperands); + } + + texture = ScalarReplicate(texture); + } else { + if (shouldProject) + texture = m_module.opImageSampleProjImplicitLod(m_vec4Type, imageVarId, texcoord, imageOperands); + else + texture = m_module.opImageSampleImplicitLod(m_vec4Type, imageVarId, texcoord, imageOperands); + } if (i != 0 && m_fsKey.Stages[i - 1].Contents.ColorOp == D3DTOP_BUMPENVMAPLUMINANCE) { uint32_t index = m_module.constu32(D3D9SharedPSStages_Count * (i - 1) + D3D9SharedPSStages_BumpEnvLScale); @@ -1857,11 +1927,6 @@ namespace dxvk { return texture; }; - auto ScalarReplicate = [&](uint32_t reg) { - std::array replicant = { reg, reg, reg, reg }; - return m_module.opCompositeConstruct(m_vec4Type, replicant.size(), replicant.data()); - }; - auto AlphaReplicate = [&](uint32_t reg) { uint32_t alphaComponentId = 3; uint32_t alpha = m_module.opCompositeExtract(m_floatType, reg, 1, &alphaComponentId); diff --git a/src/d3d9/d3d9_fixed_function.h b/src/d3d9/d3d9_fixed_function.h index 104f044c..9d64d14b 100644 --- a/src/d3d9/d3d9_fixed_function.h +++ b/src/d3d9/d3d9_fixed_function.h @@ -3,6 +3,7 @@ #include "d3d9_include.h" #include "d3d9_caps.h" +#include "d3d9_format.h" #include "../dxvk/dxvk_shader.h" @@ -58,6 +59,14 @@ namespace dxvk { void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx); + uint32_t DoFixedFunctionShadowFilter( + SpirvModule& module, + uint32_t inSample, + uint32_t sampledImage, + uint32_t coordinates, + uint32_t reference, + const SpirvImageOperands& operands); + // Returns a render state block uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count); @@ -143,6 +152,29 @@ namespace dxvk { D3D9FFShaderKeyVSData Data; }; + enum D3D9DrefScale : uint32_t { + DrefScale_None = 0, + DrefScale_D16 = 1, + DrefScale_D24S8 = 2, + }; + + constexpr float GetDrefScaleFactor(const D3D9DrefScale scale) { + switch (scale) { + default: return 1.0f; + case DrefScale_D16: return 1.0f / (float(1 << 16) - 1.0f); + case DrefScale_D24S8: return 1.0f / (float(1 << 24) - 1.0f); + } + } + + constexpr D3D9DrefScale GetDepthBufferDrefScale(D3D9Format format) { + switch (format) { + default: return DrefScale_None; + case D3D9Format::D16: return DrefScale_D16; + case D3D9Format::D24S8: return DrefScale_D24S8; + } + } + + constexpr uint32_t TextureArgCount = 3; struct D3D9FFShaderStage { @@ -166,6 +198,9 @@ namespace dxvk { uint32_t TextureBound : 1; + uint32_t DrefScale : 2; + uint32_t ShadowFilter : 1; + // Included in here, read from Stage 0 for packing reasons // Affects all stages. uint32_t GlobalSpecularEnable : 1; diff --git a/src/d3d9/d3d9_interface.cpp b/src/d3d9/d3d9_interface.cpp index 10113e57..be156d5d 100644 --- a/src/d3d9/d3d9_interface.cpp +++ b/src/d3d9/d3d9_interface.cpp @@ -3,6 +3,7 @@ #include "d3d9_monitor.h" #include "d3d9_caps.h" #include "d3d9_device.h" +#include "d3d9_bridge.h" #include "../util/util_singleton.h" @@ -14,6 +15,7 @@ namespace dxvk { D3D9InterfaceEx::D3D9InterfaceEx(bool bExtended) : m_instance ( g_dxvkInstance.acquire() ) + , m_bridge ( this ) , m_extended ( bExtended ) , m_d3d9Options ( nullptr, m_instance->config() ) , m_d3d9Interop ( this ) { @@ -86,6 +88,11 @@ namespace dxvk { return S_OK; } + if (riid == __uuidof(D3D9InterfaceBridge)) { + *ppvObject = ref(&m_bridge); + return S_OK; + } + if (riid == __uuidof(ID3D9VkInteropInterface)) { *ppvObject = ref(&m_d3d9Interop); return S_OK; diff --git a/src/d3d9/d3d9_interface.h b/src/d3d9/d3d9_interface.h index 55c9be91..d0ee287f 100644 --- a/src/d3d9/d3d9_interface.h +++ b/src/d3d9/d3d9_interface.h @@ -1,6 +1,7 @@ #pragma once #include "d3d9_adapter.h" +#include "d3d9_bridge.h" #include "d3d9_interop.h" #include "../dxvk/dxvk_instance.h" @@ -140,6 +141,8 @@ namespace dxvk { Rc m_instance; + D3D9InterfaceBridge m_bridge; + bool m_extended; D3D9Options m_d3d9Options; diff --git a/src/d3d9/d3d9_spec_constants.h b/src/d3d9/d3d9_spec_constants.h index cfe84473..a47f9053 100644 --- a/src/d3d9/d3d9_spec_constants.h +++ b/src/d3d9/d3d9_spec_constants.h @@ -30,6 +30,8 @@ namespace dxvk { SpecDrefClamp, // 1 bit for 16 PS samplers | Bits: 16 SpecFetch4, // 1 bit for 16 PS samplers | Bits: 16 + SpecDrefScale, // 2 bits for 16 PS samplers | Bits: 32 + SpecConstantCount, }; @@ -44,7 +46,7 @@ namespace dxvk { }; struct D3D9SpecializationInfo { - static constexpr uint32_t MaxSpecDwords = 5; + static constexpr uint32_t MaxSpecDwords = 6; static constexpr std::array Layout{{ { 0, 0, 32 }, // SamplerType @@ -65,6 +67,8 @@ namespace dxvk { { 4, 0, 16 }, // DrefClamp { 4, 16, 16 }, // Fetch4 + + { 5, 0, 32 }, // DrefScale }}; template diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index e01c8041..24951dca 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -961,9 +961,13 @@ namespace dxvk { void D3D9SwapChainEx::SyncFrameLatency() { // Wait for the sync event so that we respect the maximum frame latency m_frameLatencySignal->wait(m_frameId - GetActualFrameLatency()); + } + + void D3D9SwapChainEx::SetApiName(const char* name) { + m_apiName = name; + CreateHud(); } - uint32_t D3D9SwapChainEx::GetActualFrameLatency() { uint32_t maxFrameLatency = m_parent->GetFrameLatency(); @@ -1190,7 +1194,11 @@ namespace dxvk { std::string D3D9SwapChainEx::GetApiName() { - return this->GetParent()->IsExtended() ? "D3D9Ex" : "D3D9"; + if (m_apiName == nullptr) { + return this->GetParent()->IsExtended() ? "D3D9Ex" : "D3D9"; + } else { + return m_apiName; + } } } diff --git a/src/d3d9/d3d9_swapchain.h b/src/d3d9/d3d9_swapchain.h index 2e44a272..f9b439cb 100644 --- a/src/d3d9/d3d9_swapchain.h +++ b/src/d3d9/d3d9_swapchain.h @@ -81,6 +81,8 @@ namespace dxvk { void SyncFrameLatency(); + void SetApiName(const char* name); + private: enum BindingIds : uint32_t { @@ -126,6 +128,8 @@ namespace dxvk { double m_displayRefreshRate = 0.0; + const char* m_apiName = nullptr; + void PresentImage(UINT PresentInterval); void SubmitPresent(const vk::PresenterSync& Sync, uint32_t FrameId); diff --git a/src/d3d9/meson.build b/src/d3d9/meson.build index 9e27c6e8..625e77af 100644 --- a/src/d3d9/meson.build +++ b/src/d3d9/meson.build @@ -44,7 +44,8 @@ d3d9_src = [ 'd3d9_annotation.cpp', 'd3d9_mem.cpp', 'd3d9_window.cpp', - 'd3d9_interop.cpp' + 'd3d9_interop.cpp', + 'd3d9_bridge.cpp' ] d3d9_ld_args = [] @@ -69,3 +70,4 @@ d3d9_dep = declare_dependency( link_with : [ d3d9_dll ], include_directories : [ dxvk_include_path ], ) + diff --git a/src/dxso/dxso_compiler.cpp b/src/dxso/dxso_compiler.cpp index 00e73424..b4bb1dcd 100644 --- a/src/dxso/dxso_compiler.cpp +++ b/src/dxso/dxso_compiler.cpp @@ -2873,10 +2873,28 @@ void DxsoCompiler::emitControlFlowGenericLoop( uint32_t component = sampler.dimensions; reference = m_module.opCompositeExtract( fType, texcoordVar.id, 1, &component); + + // Clamp Dref to [0..1] for D32F emulating UNORM textures uint32_t clampDref = m_spec.get(m_module, m_specUbo, SpecDrefClamp, samplerIdx, 1); clampDref = m_module.opINotEqual(bool_t, clampDref, m_module.constu32(0)); uint32_t clampedDref = m_module.opFClamp(fType, reference, m_module.constf32(0.0f), m_module.constf32(1.0f)); reference = m_module.opSelect(fType, clampDref, clampedDref, reference); + + // Scale Dref to [0..(2^N - 1)] for D24S8 and D16 + if (m_moduleInfo.options.drefScaling) { + uint32_t scaleDref = m_spec.get(m_module, m_specUbo, SpecDrefScale, samplerIdx * 2, 2); + uint32_t maxDref = m_module.constf32(1.0f); + + uint32_t isD16 = m_module.opIEqual(bool_t, scaleDref, m_module.constu32(DrefScale_D16)); + uint32_t maxDref16 = m_module.constf32(GetDrefScaleFactor(DrefScale_D16)); + maxDref = m_module.opSelect(fType, isD16, maxDref16, maxDref); + + uint32_t isD24 = m_module.opIEqual(bool_t, scaleDref, m_module.constu32(DrefScale_D24S8)); + uint32_t maxDref24 = m_module.constf32(GetDrefScaleFactor(DrefScale_D24S8)); + maxDref = m_module.opSelect(fType, isD24, maxDref24, maxDref); + + reference = m_module.opFMul(fType, reference, maxDref); + } } uint32_t fetch4 = 0; @@ -2898,6 +2916,12 @@ void DxsoCompiler::emitControlFlowGenericLoop( fetch4, imageOperands); + // Emulate hardware shadow filtering for 2D depth texture lookups. + if (depth && m_moduleInfo.options.shadowFilter && samplerType == SamplerTypeTexture2D) { + const uint32_t sampledImage = m_module.opLoad(sampler.typeId, sampler.varId); + result.id = DoFixedFunctionShadowFilter(m_module, result.id, sampledImage, texcoordVar.id, reference, imageOperands); + } + // If we are sampling depth we've already specc'ed this! // This path is always size 4 because it only hits on color. if (isNull != 0) { diff --git a/src/dxso/dxso_options.h b/src/dxso/dxso_options.h index cd0ab8e1..29f88891 100644 --- a/src/dxso/dxso_options.h +++ b/src/dxso/dxso_options.h @@ -49,6 +49,16 @@ namespace dxvk { /// Whether or not we can rely on robustness2 to handle oob constant access bool robustness2Supported; + + /// Whether we should check SpecDrefScale at runtime to apply Dref scaling for depth + /// textures (D24S8 and D16). This allows compatability with games that expect a + /// different depth test range, which was typically a D3D8 quirk on early NVIDIA hardware. + bool drefScaling = false; + + /// Whether to perform 2x2 PCF when linearly sampling certain depth texture formats, + /// as done by early NVIDIA GPUs. The possibility of this behavior is also implied by + /// the spec for GL_ARB_shadow and various NVIDIA publications. + bool shadowFilter = false; }; } \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 779d9d46..18d4e6fc 100644 --- a/src/meson.build +++ b/src/meson.build @@ -31,7 +31,14 @@ if get_option('enable_d3d9') subdir('d3d9') endif +if get_option('enable_d3d8') + if not get_option('enable_d3d9') + error('D3D9 is required for D3D8.') + endif + subdir('d3d8') +endif + # Nothing selected -if not get_option('enable_d3d9') and not get_option('enable_dxgi') +if not get_option('enable_d3d8') and not get_option('enable_d3d9') and not get_option('enable_dxgi') warning('Nothing selected to be built.?') endif diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index 68a3c728..5479f0f7 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -720,6 +720,30 @@ namespace dxvk { * Speedup above 60fps */ { R"(\\bf10\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, + }} }, + + /**********************************************/ + /* D3D8 GAMES */ + /**********************************************/ + + /* Duke Nukem Forever (2001) */ + { R"(\\DukeForever\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, + /* Indiana Jones and the Emperor's Tomb * + * Fixes intro window being stuck on screen */ + { R"(\\indy\.exe$)", {{ + { "d3d9.enableDialogMode", "True" }, + }} }, + /* Tom Clancy's Splinter Cell * + * Supports shadow buffers */ + { R"(\\splintercell\.exe$)", {{ + { "d3d8.useShadowBuffers", "True" }, + }} }, + /* Anito: Defend a Land Enraged */ + { R"(\\Anito\.exe$)", {{ + { "d3d9.memoryTrackTest", "True" }, + { "d3d9.maxAvailableMemory", "1024" }, }} }, }}; diff --git a/src/util/util_vector.h b/src/util/util_vector.h index ace6cde7..72797441 100644 --- a/src/util/util_vector.h +++ b/src/util/util_vector.h @@ -153,7 +153,7 @@ namespace dxvk { inline Vector4 replaceNaN(Vector4 a) { #ifdef DXVK_ARCH_X86 Vector4 result; - __m128 value = _mm_load_ps(a.data); + __m128 value = _mm_loadu_ps(a.data); __m128 mask = _mm_cmpeq_ps(value, value); value = _mm_and_ps(value, mask); _mm_store_ps(result.data, value); diff --git a/src/vulkan/vulkan_util.h b/src/vulkan/vulkan_util.h index 7fa064dd..588c9224 100644 --- a/src/vulkan/vulkan_util.h +++ b/src/vulkan/vulkan_util.h @@ -4,6 +4,11 @@ #include "vulkan_loader.h" +#if defined(_MSC_VER) +// Unary minus on unsigned type +#pragma warning( disable : 4146 ) +#endif + namespace dxvk::vk { inline VkImageSubresourceRange makeSubresourceRange( -- 2.39.2