This commit is contained in:
Jeff 2023-08-13 22:19:57 +01:00 committed by GitHub
commit 380ea0a537
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 5031 additions and 10 deletions

View File

@ -32,6 +32,13 @@ jobs:
Write-Output "VSDEVCMD=${installationPath}\Common7\Tools\VsDevCmd.bat" `
| Out-File -FilePath "${Env:GITHUB_ENV}" -Append
- name: Download D3D8 SDK Headers
shell: pwsh
run: |
Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8.h -OutFile include/d3d8.h
Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8types.h -OutFile include/d3d8types.h
Invoke-WebRequest -URI https://raw.githubusercontent.com/NovaRain/DXSDK_Collection/master/DXSDK_Aug2007/Include/d3d8caps.h -OutFile include/d3d8caps.h
- name: Build MSVC x86
shell: pwsh
run: |

View File

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

View File

@ -1,6 +1,6 @@
# 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).

View File

@ -606,3 +606,30 @@
# 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.
#
# Supported values:
# - True/False
# d3d8.useShadowBuffers = False
# MANAGED Buffer Placement
#
# Remap some DEFAULT pool vertex and index buffers to the MANAGED pool to improve
# performance by avoiding waiting for games that frequently lock (large) buffers.
#
# This implicitly disables direct buffer mapping. Some applications may need this option
# disabled for certain (smaller) buffers to keep from overwriting in-use buffer regions.
#
# Supported values:
# - True/False
# - Any non-negative integer - sets the minimum size in bytes for a buffer to be MANAGED
# d3d8.managedBufferPlacement = True

View File

@ -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')

3
src/d3d8/d3d8.def Normal file
View File

@ -0,0 +1,3 @@
LIBRARY D3D8.DLL
EXPORTS
Direct3DCreate8 @ 5

7
src/d3d8/d3d8.sym Normal file
View File

@ -0,0 +1,7 @@
{
global:
Direct3DCreate8;
local:
*;
};

246
src/d3d8/d3d8_batch.h Normal file
View File

@ -0,0 +1,246 @@
#pragma once
#include "d3d8_include.h"
#include "d3d8_buffer.h"
#include "d3d8_format.h"
#include <vector>
#include <cstdint>
#include <climits>
namespace dxvk {
constexpr size_t D3DPT_COUNT = size_t(D3DPT_TRIANGLEFAN) + 1;
constexpr D3DPRIMITIVETYPE D3DPT_INVALID = D3DPRIMITIVETYPE(0);
/**
* Vertex buffer that can handle many tiny locks while
* still maintaing the lock ordering of direct-mapped buffers.
*/
class D3D8BatchBuffer final : public D3D8VertexBuffer {
public:
D3D8BatchBuffer(
D3D8Device* pDevice,
D3DPOOL Pool,
DWORD Usage,
UINT Length,
DWORD FVF)
: D3D8VertexBuffer(pDevice, nullptr, Pool, Usage)
, m_data(Length)
, m_fvf(FVF) {
}
HRESULT STDMETHODCALLTYPE Lock(
UINT OffsetToLock,
UINT SizeToLock,
BYTE** ppbData,
DWORD Flags) {
*ppbData = m_data.data() + OffsetToLock;
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE Unlock() {
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) {
pDesc->Format = D3DFMT_VERTEXDATA;
pDesc->Type = D3DRTYPE_VERTEXBUFFER;
pDesc->Usage = m_usage;
pDesc->Pool = m_pool;
pDesc->Size = m_data.size();
pDesc->FVF = m_fvf;
return D3D_OK;
}
void STDMETHODCALLTYPE PreLoad() {
}
const void* GetPtr(UINT byteOffset = 0) const {
return m_data.data() + byteOffset;
}
UINT Size() const {
return m_data.size();
}
private:
std::vector<BYTE> m_data;
DWORD m_fvf;
};
/**
* Main handler for batching D3D8 draw calls.
*/
class D3D8Batcher {
struct Batch {
D3DPRIMITIVETYPE PrimitiveType = D3DPT_INVALID;
std::vector<uint16_t> Indices;
UINT Offset = 0;
UINT MinVertex = UINT_MAX;
UINT MaxVertex = 0;
UINT PrimitiveCount = 0;
UINT DrawCallCount = 0;
};
public:
D3D8Batcher(D3D8Device* pDevice8, Com<d3d9::IDirect3DDevice9>&& pDevice9)
: m_device8(pDevice8)
, m_device(std::move(pDevice9)) {
}
inline D3D8BatchBuffer* CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool) {
return ref(new D3D8BatchBuffer(m_device8, Pool, Usage, Length, FVF));
}
inline void StateChange() {
if (likely(m_batches.empty()))
return;
for (auto& draw : m_batches) {
if (draw.PrimitiveType == D3DPT_INVALID)
continue;
//m_largestBatch = std::max(m_largestBatch, draw.DrawCallCount);
//m_bridge->AddBatchCalls(draw.DrawCallCount);
for (auto& index : draw.Indices)
index -= draw.MinVertex;
m_device->DrawIndexedPrimitiveUP(
d3d9::D3DPRIMITIVETYPE(draw.PrimitiveType),
0,
draw.MaxVertex - draw.MinVertex,
draw.PrimitiveCount,
draw.Indices.data(),
d3d9::D3DFMT_INDEX16,
m_stream->GetPtr(draw.MinVertex * m_stride),
m_stride);
m_device->SetStreamSource(0, D3D8VertexBuffer::GetD3D9Nullable(m_stream), 0, m_stride);
m_device->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(m_indices));
draw.PrimitiveType = D3DPRIMITIVETYPE(0);
draw.Offset = 0;
draw.MinVertex = UINT_MAX;
draw.MaxVertex = 0;
draw.PrimitiveCount = 0;
draw.DrawCallCount = 0;
}
}
inline void EndFrame() {
//m_bridge->AddBatchCalls(m_largestBatch);
//m_largestBatch = 0;
}
inline HRESULT DrawPrimitive(
D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex,
UINT PrimitiveCount) {
// None of this linestrip or fan malarkey
D3DPRIMITIVETYPE batchedPrimType = PrimitiveType;
switch (PrimitiveType) {
case D3DPT_LINESTRIP: batchedPrimType = D3DPT_LINELIST; break;
case D3DPT_TRIANGLEFAN: batchedPrimType = D3DPT_TRIANGLELIST; break;
default: break;
}
Batch* batch = &m_batches[size_t(batchedPrimType)];
batch->PrimitiveType = batchedPrimType;
//UINT vertices = GetVertexCount8(PrimitiveType, PrimitiveCount);
switch (PrimitiveType) {
case D3DPT_POINTLIST:
batch->Indices.resize(batch->Offset + PrimitiveCount);
for (UINT i = 0; i < PrimitiveCount; i++)
batch->Indices[batch->Offset++] = (StartVertex + i);
break;
case D3DPT_LINELIST:
batch->Indices.resize(batch->Offset + PrimitiveCount * 2);
for (UINT i = 0; i < PrimitiveCount; i++) {
batch->Indices[batch->Offset++] = (StartVertex + i * 2 + 0);
batch->Indices[batch->Offset++] = (StartVertex + i * 2 + 1);
}
break;
case D3DPT_LINESTRIP:
batch->Indices.resize(batch->Offset + PrimitiveCount * 2);
for (UINT i = 0; i < PrimitiveCount; i++) {
batch->Indices[batch->Offset++] = (StartVertex + i + 0);
batch->Indices[batch->Offset++] = (StartVertex + i + 1);
}
break;
case D3DPT_TRIANGLELIST:
batch->Indices.resize(batch->Offset + PrimitiveCount * 3);
for (UINT i = 0; i < PrimitiveCount; i++) {
batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 0);
batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 1);
batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 2);
}
break;
case D3DPT_TRIANGLESTRIP:
// Join with degenerate triangle
// 1 2 3, 3 4, 4 5 6
batch->Indices.resize(batch->Offset + PrimitiveCount + 2);
if (batch->Offset > 0) {
batch->Indices[batch->Offset + 1] = batch->Indices[batch->Offset-2];
batch->Indices[batch->Offset += 2] = StartVertex;
}
for (UINT i = 0; i < PrimitiveCount; i++) {
batch->Indices[batch->Offset++] = (StartVertex + i + 0);
}
break;
// 1 2 3 4 5 6 7 -> 1 2 3, 1 3 4, 1 4 5, 1 5 6, 1 6 7
case D3DPT_TRIANGLEFAN:
batch->Indices.resize(batch->Offset + PrimitiveCount * 3);
for (UINT i = 0; i < PrimitiveCount; i++) {
batch->Indices[batch->Offset++] = (StartVertex + 0);
batch->Indices[batch->Offset++] = (StartVertex + i + 1);
batch->Indices[batch->Offset++] = (StartVertex + i + 2);
}
break;
default:
return D3DERR_INVALIDCALL;
}
batch->MinVertex = std::min(batch->MinVertex, StartVertex);
if (!batch->Indices.empty())
batch->MaxVertex = std::max(batch->MaxVertex, UINT(batch->Indices.back() + 1));
batch->PrimitiveCount += PrimitiveCount;
batch->DrawCallCount++;
return D3D_OK;
}
inline void SetStream(UINT num, D3D8VertexBuffer* stream, UINT stride) {
if (unlikely(num != 0)) {
StateChange();
return;
}
if (unlikely(m_stream != stream || m_stride != stride)) {
StateChange();
m_stream = static_cast<D3D8BatchBuffer*>(stream);
m_stride = stride;
}
}
inline void SetIndices(D3D8IndexBuffer* indices, INT baseVertexIndex) {
if (m_indices != indices || m_baseVertexIndex != baseVertexIndex) {
StateChange();
m_indices = indices;
m_baseVertexIndex = baseVertexIndex;
}
}
private:
D3D8Device* m_device8;
Com<d3d9::IDirect3DDevice9> m_device;
D3D8BatchBuffer* m_stream = nullptr;
UINT m_stride = 0;
D3D8IndexBuffer* m_indices = nullptr;
INT m_baseVertexIndex = 0;
std::array<Batch, D3DPT_COUNT> m_batches;
};
}

102
src/d3d8/d3d8_buffer.h Normal file
View File

@ -0,0 +1,102 @@
#pragma once
#include "d3d8_include.h"
#include "d3d8_resource.h"
namespace dxvk {
template <typename D3D9, typename D3D8>
class D3D8Buffer : public D3D8Resource<D3D9, D3D8> {
public:
D3D8Buffer(
D3D8Device* pDevice,
Com<D3D9>&& pBuffer,
D3DPOOL Pool,
DWORD Usage)
: D3D8Resource<D3D9, D3D8> (pDevice, std::move(pBuffer))
, m_pool (Pool)
, m_usage (Usage) {
}
HRESULT STDMETHODCALLTYPE Lock(
UINT OffsetToLock,
UINT SizeToLock,
BYTE** ppbData,
DWORD Flags) {
return this->GetD3D9()->Lock(
OffsetToLock,
SizeToLock,
reinterpret_cast<void**>(ppbData),
Flags);
}
HRESULT STDMETHODCALLTYPE Unlock() {
return this->GetD3D9()->Unlock();
}
void STDMETHODCALLTYPE PreLoad() {
this->GetD3D9()->PreLoad();
}
protected:
// This is the D3D8 pool, not necessarily what's given to D3D9.
const D3DPOOL m_pool;
// This is the D3D8 usage, not necessarily what's given to D3D9.
const DWORD m_usage;
};
using D3D8VertexBufferBase = D3D8Buffer<d3d9::IDirect3DVertexBuffer9, IDirect3DVertexBuffer8>;
class D3D8VertexBuffer : public D3D8VertexBufferBase {
public:
D3D8VertexBuffer(
D3D8Device* pDevice,
Com<d3d9::IDirect3DVertexBuffer9>&& pBuffer,
D3DPOOL Pool,
DWORD Usage)
: D3D8VertexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
}
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_VERTEXBUFFER; }
HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) {
HRESULT hr = GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DVERTEXBUFFER_DESC*>(pDesc));
if (!FAILED(hr)) {
pDesc->Pool = m_pool;
pDesc->Usage = m_usage;
}
return hr;
}
};
using D3D8IndexBufferBase = D3D8Buffer<d3d9::IDirect3DIndexBuffer9, IDirect3DIndexBuffer8>;
class D3D8IndexBuffer final : public D3D8IndexBufferBase {
public:
D3D8IndexBuffer(
D3D8Device* pDevice,
Com<d3d9::IDirect3DIndexBuffer9>&& pBuffer,
D3DPOOL Pool,
DWORD Usage)
: D3D8IndexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
}
D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_INDEXBUFFER; }
HRESULT STDMETHODCALLTYPE GetDesc(D3DINDEXBUFFER_DESC* pDesc) final {
HRESULT hr = GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DINDEXBUFFER_DESC*>(pDesc));
if (!FAILED(hr)) {
pDesc->Pool = m_pool;
pDesc->Usage = m_usage;
}
return hr;
}
};
}

8
src/d3d8/d3d8_caps.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
namespace dxvk::d8caps {
constexpr uint32_t MAX_TEXTURE_STAGES = 8;
constexpr uint32_t MAX_STREAMS = 16;
}

188
src/d3d8/d3d8_d3d9_util.h Normal file
View File

@ -0,0 +1,188 @@
#pragma once
/**
* Utility functions for converting
* between DirectX8 and DirectX9 types.
*/
#include "d3d8_include.h"
#include "d3d8_format.h"
#include "d3d8_options.h"
#include <utility>
namespace dxvk {
// Remap certain vertex and index buffers to different pools.
inline std::pair<DWORD, d3d9::D3DPOOL> ChooseBufferPool(DWORD Usage, D3DPOOL Pool8, UINT Length, const D3D8Options& options) {
d3d9::D3DPOOL Pool = d3d9::D3DPOOL(Pool8);
// TODO: Optimize carefully which buffers go in which pools.
// - WRITEONLY DYNAMIC buffers might get performance gains in DEFAULT if not misused
// Remap DEFAULT pool vertex buffers to MANAGED.
// - This avoids direct buffer mapping which can cause apps to misbehave
// due to differences in the behavior of NOOVERWRITE that will
// make apps write over in-use buffers that they expect to wait for.
// - D3D9DeviceEx::LockBuffer will ignored DISCARD and NOOVERWRITE for us
if (Pool8 == D3DPOOL_DEFAULT && Length >= options.managedBufferPlacement) {
Pool = d3d9::D3DPOOL_MANAGED;
Usage &= ~D3DUSAGE_DYNAMIC;
}
return {Usage, Pool};
}
// (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 PS 1.4 and VS 1.1
pCaps8->VertexShaderVersion = D3DVS_VERSION(1, 1);
pCaps8->PixelShaderVersion = D3DPS_VERSION(1, 4);
// This was removed by D3D9. We can probably render windowed.
pCaps8->Caps2 |= D3DCAPS2_CANRENDERWINDOWED;
// Replaced by D3DPRASTERCAPS_DEPTHBIAS in D3D9
pCaps8->RasterCaps |= D3DPRASTERCAPS_ZBIAS;
// Remove D3D9-specific caps:
pCaps8->Caps2 &= ~D3DCAPS2_CANAUTOGENMIPMAP;
pCaps8->Caps3 &= ~D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION
& ~D3DCAPS3_COPY_TO_VIDMEM
& ~D3DCAPS3_COPY_TO_SYSTEMMEM;
pCaps8->PrimitiveMiscCaps &= ~D3DPMISCCAPS_INDEPENDENTWRITEMASKS
& ~D3DPMISCCAPS_PERSTAGECONSTANT
& ~D3DPMISCCAPS_FOGANDSPECULARALPHA
& ~D3DPMISCCAPS_SEPARATEALPHABLEND
& ~D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS
& ~D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING
& ~D3DPMISCCAPS_FOGVERTEXCLAMPED
& ~D3DPMISCCAPS_POSTBLENDSRGBCONVERT;
pCaps8->RasterCaps &= ~D3DPRASTERCAPS_SCISSORTEST
& ~D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS
& ~D3DPRASTERCAPS_DEPTHBIAS
& ~D3DPRASTERCAPS_MULTISAMPLE_TOGGLE;
pCaps8->SrcBlendCaps &= ~D3DPBLENDCAPS_INVSRCCOLOR2
& ~D3DPBLENDCAPS_SRCCOLOR2;
pCaps8->LineCaps &= ~D3DLINECAPS_ANTIALIAS;
pCaps8->StencilCaps &= ~D3DSTENCILCAPS_TWOSIDED;
}
// (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;
}
// (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 = getSurfaceSize(pSurf8->Format, pSurf9->Width, pSurf9->Height);
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 = getSurfaceSize(pVol8->Format, pVol9->Width, pVol9->Height) * pVol9->Depth;
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);
}
}
}

1705
src/d3d8/d3d8_device.cpp Normal file

File diff suppressed because it is too large Load Diff

450
src/d3d8/d3d8_device.h Normal file
View File

@ -0,0 +1,450 @@
#pragma once
#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 "d3d8_batch.h"
#include "../d3d9/d3d9_bridge.h"
#include <array>
#include <vector>
#include <type_traits>
#include <unordered_set>
namespace dxvk {
class D3D8Interface;
struct D3D8VertexShaderInfo;
using D3D8DeviceBase = D3D8WrappedObject<d3d9::IDirect3DDevice9, IDirect3DDevice8>;
class D3D8Device final : public D3D8DeviceBase {
friend class D3D8StateBlock;
public:
D3D8Device(
D3D8Interface* pParent,
Com<d3d9::IDirect3DDevice9>&& pDevice,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS* pParams);
~D3D8Device();
HRESULT STDMETHODCALLTYPE TestCooperativeLevel();
UINT STDMETHODCALLTYPE GetAvailableTextureMem();
HRESULT STDMETHODCALLTYPE ResourceManagerDiscardBytes(DWORD bytes);
HRESULT STDMETHODCALLTYPE GetDirect3D(IDirect3D8** ppD3D8);
HRESULT STDMETHODCALLTYPE GetDeviceCaps(D3DCAPS8* pCaps);
HRESULT STDMETHODCALLTYPE GetDisplayMode(D3DDISPLAYMODE* pMode);
HRESULT STDMETHODCALLTYPE GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters);
HRESULT STDMETHODCALLTYPE SetCursorProperties(
UINT XHotSpot,
UINT YHotSpot,
IDirect3DSurface8* pCursorBitmap);
void STDMETHODCALLTYPE SetCursorPosition(UINT XScreenSpace, UINT YScreenSpace, DWORD Flags);
// Microsoft d3d8.h in the DirectX 9 SDK uses a different function signature...
void STDMETHODCALLTYPE SetCursorPosition(int X, int Y, DWORD Flags);
BOOL STDMETHODCALLTYPE ShowCursor(BOOL bShow);
HRESULT STDMETHODCALLTYPE CreateAdditionalSwapChain(
D3DPRESENT_PARAMETERS* pPresentationParameters,
IDirect3DSwapChain8** ppSwapChain);
HRESULT STDMETHODCALLTYPE Reset(D3DPRESENT_PARAMETERS* pPresentationParameters);
HRESULT STDMETHODCALLTYPE Present(
const RECT* pSourceRect,
const RECT* pDestRect,
HWND hDestWindowOverride,
const RGNDATA* pDirtyRegion);
HRESULT STDMETHODCALLTYPE GetBackBuffer(
UINT iBackBuffer,
D3DBACKBUFFER_TYPE Type,
IDirect3DSurface8** ppBackBuffer);
HRESULT STDMETHODCALLTYPE GetRasterStatus(D3DRASTER_STATUS* pRasterStatus);
void STDMETHODCALLTYPE SetGammaRamp(DWORD Flags, const D3DGAMMARAMP* pRamp);
void STDMETHODCALLTYPE GetGammaRamp(D3DGAMMARAMP* pRamp);
HRESULT STDMETHODCALLTYPE CreateTexture(
UINT Width,
UINT Height,
UINT Levels,
DWORD Usage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DTexture8** ppTexture);
HRESULT STDMETHODCALLTYPE CreateVolumeTexture(
UINT Width,
UINT Height,
UINT Depth,
UINT Levels,
DWORD Usage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DVolumeTexture8** ppVolumeTexture);
HRESULT STDMETHODCALLTYPE CreateCubeTexture(
UINT EdgeLength,
UINT Levels,
DWORD Usage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DCubeTexture8** ppCubeTexture);
HRESULT STDMETHODCALLTYPE CreateVertexBuffer(
UINT Length,
DWORD Usage,
DWORD FVF,
D3DPOOL Pool,
IDirect3DVertexBuffer8** ppVertexBuffer);
HRESULT STDMETHODCALLTYPE CreateIndexBuffer(
UINT Length,
DWORD Usage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DIndexBuffer8** ppIndexBuffer);
HRESULT STDMETHODCALLTYPE CreateRenderTarget(
UINT Width,
UINT Height,
D3DFORMAT Format,
D3DMULTISAMPLE_TYPE MultiSample,
BOOL Lockable,
IDirect3DSurface8** ppSurface);
HRESULT STDMETHODCALLTYPE CreateDepthStencilSurface(
UINT Width,
UINT Height,
D3DFORMAT Format,
D3DMULTISAMPLE_TYPE MultiSample,
IDirect3DSurface8** ppSurface);
HRESULT STDMETHODCALLTYPE CreateImageSurface(UINT Width, UINT Height, D3DFORMAT Format, IDirect3DSurface8** ppSurface);
HRESULT STDMETHODCALLTYPE CopyRects(
IDirect3DSurface8* pSourceSurface,
const RECT* pSourceRectsArray,
UINT cRects,
IDirect3DSurface8* pDestinationSurface,
const POINT* pDestPointsArray);
HRESULT STDMETHODCALLTYPE UpdateTexture(
IDirect3DBaseTexture8* pSourceTexture,
IDirect3DBaseTexture8* pDestinationTexture);
HRESULT STDMETHODCALLTYPE GetFrontBuffer(IDirect3DSurface8* pDestSurface);
HRESULT STDMETHODCALLTYPE SetRenderTarget(IDirect3DSurface8* pRenderTarget, IDirect3DSurface8* pNewZStencil);
HRESULT STDMETHODCALLTYPE GetRenderTarget(IDirect3DSurface8** ppRenderTarget);
HRESULT STDMETHODCALLTYPE GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface);
HRESULT STDMETHODCALLTYPE BeginScene();
HRESULT STDMETHODCALLTYPE EndScene();
HRESULT STDMETHODCALLTYPE Clear(
DWORD Count,
const D3DRECT* pRects,
DWORD Flags,
D3DCOLOR Color,
float Z,
DWORD Stencil);
HRESULT STDMETHODCALLTYPE SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix);
HRESULT STDMETHODCALLTYPE GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix);
HRESULT STDMETHODCALLTYPE MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix);
HRESULT STDMETHODCALLTYPE SetViewport(const D3DVIEWPORT8* pViewport);
HRESULT STDMETHODCALLTYPE GetViewport(D3DVIEWPORT8* pViewport);
HRESULT STDMETHODCALLTYPE SetMaterial(const D3DMATERIAL8* pMaterial);
HRESULT STDMETHODCALLTYPE GetMaterial(D3DMATERIAL8* pMaterial);
HRESULT STDMETHODCALLTYPE SetLight(DWORD Index, const D3DLIGHT8* pLight);
HRESULT STDMETHODCALLTYPE GetLight(DWORD Index, D3DLIGHT8* pLight);
HRESULT STDMETHODCALLTYPE LightEnable(DWORD Index, BOOL Enable);
HRESULT STDMETHODCALLTYPE GetLightEnable(DWORD Index, BOOL* pEnable);
HRESULT STDMETHODCALLTYPE SetClipPlane(DWORD Index, const float* pPlane);
HRESULT STDMETHODCALLTYPE GetClipPlane(DWORD Index, float* pPlane);
HRESULT STDMETHODCALLTYPE SetRenderState(D3DRENDERSTATETYPE State, DWORD Value);
HRESULT STDMETHODCALLTYPE GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue);
HRESULT STDMETHODCALLTYPE CreateStateBlock(
D3DSTATEBLOCKTYPE Type,
DWORD* pToken);
HRESULT STDMETHODCALLTYPE CaptureStateBlock(DWORD Token);
HRESULT STDMETHODCALLTYPE ApplyStateBlock(DWORD Token);
HRESULT STDMETHODCALLTYPE DeleteStateBlock(DWORD Token);
HRESULT STDMETHODCALLTYPE BeginStateBlock();
HRESULT STDMETHODCALLTYPE EndStateBlock(DWORD* pToken);
HRESULT STDMETHODCALLTYPE SetClipStatus(const D3DCLIPSTATUS8* pClipStatus);
HRESULT STDMETHODCALLTYPE GetClipStatus(D3DCLIPSTATUS8* pClipStatus);
HRESULT STDMETHODCALLTYPE GetTexture(DWORD Stage, IDirect3DBaseTexture8** ppTexture);
HRESULT STDMETHODCALLTYPE SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture);
HRESULT STDMETHODCALLTYPE GetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD* pValue);
HRESULT STDMETHODCALLTYPE SetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD Value);
HRESULT STDMETHODCALLTYPE ValidateDevice(DWORD* pNumPasses);
HRESULT STDMETHODCALLTYPE GetInfo(DWORD DevInfoID, void* pDevInfoStruct, DWORD DevInfoStructSize);
HRESULT STDMETHODCALLTYPE SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries);
HRESULT STDMETHODCALLTYPE GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries);
HRESULT STDMETHODCALLTYPE SetCurrentTexturePalette(UINT PaletteNumber);
HRESULT STDMETHODCALLTYPE GetCurrentTexturePalette(UINT* PaletteNumber);
HRESULT STDMETHODCALLTYPE DrawPrimitive(
D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex,
UINT PrimitiveCount);
HRESULT STDMETHODCALLTYPE DrawIndexedPrimitive(
D3DPRIMITIVETYPE PrimitiveType,
UINT MinVertexIndex,
UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount);
HRESULT STDMETHODCALLTYPE DrawPrimitiveUP(
D3DPRIMITIVETYPE PrimitiveType,
UINT PrimitiveCount,
const void* pVertexStreamZeroData,
UINT VertexStreamZeroStride);
HRESULT STDMETHODCALLTYPE DrawIndexedPrimitiveUP(
D3DPRIMITIVETYPE PrimitiveType,
UINT MinVertexIndex,
UINT NumVertices,
UINT PrimitiveCount,
const void* pIndexData,
D3DFORMAT IndexDataFormat,
const void* pVertexStreamZeroData,
UINT VertexStreamZeroStride);
HRESULT STDMETHODCALLTYPE ProcessVertices(
UINT SrcStartIndex,
UINT DestIndex,
UINT VertexCount,
IDirect3DVertexBuffer8* pDestBuffer,
DWORD 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);
HRESULT STDMETHODCALLTYPE GetVertexShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount);
HRESULT STDMETHODCALLTYPE GetVertexShaderDeclaration(DWORD Handle, void* pData, DWORD* pSizeOfData);
HRESULT STDMETHODCALLTYPE GetVertexShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData);
HRESULT STDMETHODCALLTYPE SetStreamSource(
UINT StreamNumber,
IDirect3DVertexBuffer8* pStreamData,
UINT Stride);
HRESULT STDMETHODCALLTYPE GetStreamSource(
UINT StreamNumber,
IDirect3DVertexBuffer8** ppStreamData,
UINT* pStride);
HRESULT STDMETHODCALLTYPE SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex);
HRESULT STDMETHODCALLTYPE GetIndices(
IDirect3DIndexBuffer8** ppIndexData,
UINT* pBaseVertexIndex);
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 GetPixelShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount);
HRESULT STDMETHODCALLTYPE SetPixelShaderConstant(
DWORD StartRegister,
const void* pConstantData,
DWORD ConstantCount);
HRESULT STDMETHODCALLTYPE GetPixelShaderFunction(DWORD Handle, void* pData, DWORD* pSizeOfData);
HRESULT STDMETHODCALLTYPE DrawRectPatch(
UINT Handle,
const float* pNumSegs,
const D3DRECTPATCH_INFO* pRectPatchInfo);
HRESULT STDMETHODCALLTYPE DrawTriPatch(
UINT Handle,
const float* pNumSegs,
const D3DTRIPATCH_INFO* pTriPatchInfo);
HRESULT STDMETHODCALLTYPE DeletePatch(UINT Handle);
public: // Internal Methods //
inline bool ShouldRecord() { return m_recorder != nullptr; }
inline bool ShouldBatch() { return m_batcher != nullptr; }
/**
* Marks any state change in the device, so we can signal
* the batcher to emit draw calls. StateChange should be
* called immediately before changing any D3D9 state.
*/
inline void StateChange() {
if (ShouldBatch())
m_batcher->StateChange();
}
inline void ResetState() {
// Mirrors how D3D9 handles the BackBufferCount
m_presentParams.BackBufferCount = std::max(m_presentParams.BackBufferCount, 1u);
// Purge cached objects
// TODO: Some functions may need to be called here (e.g. SetTexture, etc.)
// in case Reset can be recorded by state blocks and other things.
m_textures.fill(nullptr);
m_streams.fill(D3D8VBO());
m_indices = nullptr;
m_renderTarget = nullptr;
m_depthStencil = nullptr;
m_backBuffers.clear();
m_backBuffers.resize(m_presentParams.BackBufferCount);
for (UINT i = 0; i < m_presentParams.BackBufferCount; i++) {
Com<d3d9::IDirect3DSurface9> pSurface9;
GetD3D9()->GetBackBuffer(0, i, d3d9::D3DBACKBUFFER_TYPE_MONO, &pSurface9);
m_backBuffers[i] = new D3D8Surface(this, std::move(pSurface9));
}
m_autoDepthStencil = nullptr;
Com<d3d9::IDirect3DSurface9> pStencil9 = nullptr;
GetD3D9()->GetDepthStencilSurface(&pStencil9);
m_autoDepthStencil = new D3D8Surface(this, std::move(pStencil9));
m_renderTarget = m_backBuffers[0];
m_depthStencil = m_autoDepthStencil;
}
friend d3d9::IDirect3DPixelShader9* getPixelShaderPtr(D3D8Device* device, DWORD Handle);
friend D3D8VertexShaderInfo* getVertexShaderInfo(D3D8Device* device, DWORD Handle);
private:
Com<IDxvkD3D8Bridge> m_bridge;
const D3D8Options& m_d3d8Options;
Com<D3D8Interface> m_parent;
D3DPRESENT_PARAMETERS m_presentParams;
D3D8StateBlock* m_recorder = nullptr;
std::unordered_set<D3D8StateBlock*> m_stateBlocks;
D3D8Batcher* m_batcher = nullptr;
struct D3D8VBO {
Com<D3D8VertexBuffer, false> buffer = nullptr;
UINT stride = 0;
};
// Remember to fill() these in the constructor!
std::array<Com<D3D8Texture2D, false>, d8caps::MAX_TEXTURE_STAGES> m_textures;
std::array<D3D8VBO, d8caps::MAX_STREAMS> m_streams;
Com<D3D8IndexBuffer, false> m_indices;
INT m_baseVertexIndex = 0;
// TODO: Which of these should be a private ref
std::vector<Com<D3D8Surface, false>> m_backBuffers;
Com<D3D8Surface, false> m_autoDepthStencil;
Com<D3D8Surface, false> m_renderTarget;
Com<D3D8Surface, false> m_depthStencil;
std::vector<D3D8VertexShaderInfo> m_vertexShaders;
std::vector<d3d9::IDirect3DPixelShader9*> m_pixelShaders;
DWORD m_currentVertexShader = 0; // can be FVF or vs index (marked by D3DFVF_RESERVED0)
DWORD m_currentPixelShader = 0;
D3DDEVTYPE m_deviceType;
HWND m_window;
DWORD m_behaviorFlags;
};
}

View File

@ -0,0 +1,72 @@
#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 D3D8Device;
template <typename D3D9, typename D3D8>
class D3D8DeviceChild : public D3D8WrappedObject<D3D9, D3D8> {
public:
D3D8DeviceChild(D3D8Device* pDevice, Com<D3D9>&& Object)
: D3D8WrappedObject<D3D9, D3D8>(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() {
// ignore Release calls on objects with 0 refCount
if(unlikely(!this->m_refCount))
return this->m_refCount;
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<IDirect3DDevice8*>(m_parent);
}
D3D8Device* GetParent() {
return m_parent;
}
protected:
D3D8Device* m_parent;
};
}

199
src/d3d8/d3d8_format.h Normal file
View File

@ -0,0 +1,199 @@
#pragma once
#include "d3d8_include.h"
namespace dxvk {
inline constexpr bool isDXT(D3DFORMAT fmt) {
return fmt == D3DFMT_DXT1
|| fmt == D3DFMT_DXT2
|| fmt == D3DFMT_DXT3
|| fmt == D3DFMT_DXT4
|| fmt == D3DFMT_DXT5;
}
inline constexpr bool isDXT(d3d9::D3DFORMAT fmt) {
return isDXT(D3DFORMAT(fmt));
}
inline constexpr bool isSupportedDepthStencilFormat(D3DFORMAT fmt) {
// native d3d8 doesn't support D3DFMT_D32, D3DFMT_D15S1 or D3DFMT_D24X4S4
return fmt == D3DFMT_D16_LOCKABLE
|| fmt == D3DFMT_D16
//|| fmt == D3DFMT_D32
//|| fmt == D3DFMT_D15S1
//|| fmt == D3DFMT_D24X4S4
|| fmt == D3DFMT_D24S8
|| fmt == D3DFMT_D24X8;
}
// Get bytes per pixel (or 4x4 block for DXT)
inline constexpr UINT getFormatStride(D3DFORMAT fmt) {
switch (fmt) {
default:
case D3DFMT_UNKNOWN:
return 0;
case D3DFMT_R3G3B2:
case D3DFMT_A8:
case D3DFMT_P8:
case D3DFMT_L8:
case D3DFMT_A4L4:
return 1;
case D3DFMT_R5G6B5:
case D3DFMT_X1R5G5B5:
case D3DFMT_A1R5G5B5:
case D3DFMT_A4R4G4B4:
case D3DFMT_A8R3G3B2:
case D3DFMT_X4R4G4B4:
case D3DFMT_A8P8:
case D3DFMT_A8L8:
case D3DFMT_V8U8:
case D3DFMT_L6V5U5:
case D3DFMT_D16_LOCKABLE:
case D3DFMT_D15S1:
case D3DFMT_D16:
case D3DFMT_UYVY:
case D3DFMT_YUY2:
return 2;
case D3DFMT_R8G8B8:
return 3;
case D3DFMT_A8R8G8B8:
case D3DFMT_X8R8G8B8:
case D3DFMT_A2B10G10R10:
//case D3DFMT_A8B8G8R8:
//case D3DFMT_X8B8G8R8:
case D3DFMT_G16R16:
case D3DFMT_X8L8V8U8:
case D3DFMT_Q8W8V8U8:
case D3DFMT_V16U16:
case D3DFMT_W11V11U10:
case D3DFMT_A2W10V10U10:
case D3DFMT_D32:
case D3DFMT_D24S8:
case D3DFMT_D24X8:
case D3DFMT_D24X4S4:
return 4;
case D3DFMT_DXT1:
return 8;
case D3DFMT_DXT2:
case D3DFMT_DXT3:
case D3DFMT_DXT4:
case D3DFMT_DXT5:
return 16;
}
}
inline constexpr uint32_t GetVertexCount8(D3DPRIMITIVETYPE type, UINT count) {
switch (type) {
default:
case D3DPT_TRIANGLELIST: return count * 3;
case D3DPT_POINTLIST: return count;
case D3DPT_LINELIST: return count * 2;
case D3DPT_LINESTRIP: return count + 1;
case D3DPT_TRIANGLESTRIP: return count + 2;
case D3DPT_TRIANGLEFAN: return count + 2;
}
}
// Essentially the same logic as D3D9VertexDecl::SetFVF
inline constexpr UINT GetFVFStride(DWORD FVF) {
uint32_t texCount = 0;
uint32_t betas = 0;
uint8_t betaIdx = 0xFF;
UINT size = 0;
switch (FVF & D3DFVF_POSITION_MASK) {
case D3DFVF_XYZ:
case D3DFVF_XYZB1:
case D3DFVF_XYZB2:
case D3DFVF_XYZB3:
case D3DFVF_XYZB4:
case D3DFVF_XYZB5:
size += sizeof(float) * 3;
if ((FVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZ)
break;
betas = (((FVF & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1) + 1;
if (FVF & D3DFVF_LASTBETA_D3DCOLOR)
betaIdx = sizeof(D3DCOLOR);
else if (FVF & D3DFVF_LASTBETA_UBYTE4)
betaIdx = sizeof(BYTE) * 4;
else if ((FVF & D3DFVF_XYZB5) == D3DFVF_XYZB5)
betaIdx = sizeof(float);
if (betaIdx != 0xFF)
betas--;
if (betas > 0) {
if (betas <= 4)
size += sizeof(float) * betas;
}
if (betaIdx != 0xFF) {
size += betaIdx;
}
break;
case D3DFVF_XYZW:
case D3DFVF_XYZRHW:
size += sizeof(float) * 4;
break;
default:
break;
}
if (FVF & D3DFVF_NORMAL) {
size += sizeof(float) * 3;
}
if (FVF & D3DFVF_PSIZE) {
size += sizeof(float);
}
if (FVF & D3DFVF_DIFFUSE) {
size += sizeof(D3DCOLOR);
}
if (FVF & D3DFVF_SPECULAR) {
size += sizeof(D3DCOLOR);
}
texCount = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
texCount = std::min(texCount, 8u);
for (uint32_t i = 0; i < texCount; i++) {
switch ((FVF >> (16 + i * 2)) & 0x3) {
case D3DFVF_TEXTUREFORMAT1:
size += sizeof(float);
break;
case D3DFVF_TEXTUREFORMAT2:
size += sizeof(float) * 2;
break;
case D3DFVF_TEXTUREFORMAT3:
size += sizeof(float) * 3;
break;
case D3DFVF_TEXTUREFORMAT4:
size += sizeof(float) * 4;
break;
default:
break;
}
}
return size;
}
inline constexpr UINT getSurfaceSize(D3DFORMAT Format, UINT Width, UINT Height) {
if (isDXT(Format)) {
Width = ((Width + 3) >> 2);
Height = ((Height + 3) >> 2);
}
return Width * Height * getFormatStride(Format);
}
}

200
src/d3d8/d3d8_include.h Normal file
View File

@ -0,0 +1,200 @@
#pragma once
#ifndef _MSC_VER
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0A00
#endif
#include <stdint.h>
#include <d3d8.h>
// 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 D3D9
#undef D3DFVF_POSITION_MASK // changed from 0x00E to 0x400E in D3D9
#undef D3DFVF_RESERVED2 // reduced from 4 to 2 in DX9
#undef D3DSP_REGNUM_MASK // changed from 0x00000FFF to 0x000007FF in D3D9
#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<d3d9::type> { static constexpr IID __uuid_inst = {l,w1,w2, {b1,b2,b3,b4,b5,b6,b7,b8}}; }; \
extern "C++" template<> constexpr const GUID &__mingw_uuidof<d3d9::type>() { return __mingw_uuidof_s<d3d9::type>::__uuid_inst; } \
extern "C++" template<> constexpr const GUID &__mingw_uuidof<d3d9::type*>() { return __mingw_uuidof_s<d3d9::type>::__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<d3d9::type>() { return GUID{a,b,c,{d,e,f,g,h,i,j,k}}; } } \
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type*>() { return __uuidof_helper<d3d9::type>(); } } \
extern "C++" { template <> constexpr GUID __uuidof_helper<const d3d9::type*>() { return __uuidof_helper<d3d9::type>(); } } \
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type&>() { return __uuidof_helper<d3d9::type>(); } } \
extern "C++" { template <> constexpr GUID __uuidof_helper<const d3d9::type&>() { return __uuidof_helper<d3d9::type>(); } } \
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 <d3d9.h>
}
// 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/util_error.h"
#include "../util/util_likely.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
#else // _MSC_VER
// These are enum typedefs in the MinGW headers, but not defined by Microsoft
#define D3DVSDT_TYPE DWORD
#define D3DVSDE_REGISTER DWORD
#endif

140
src/d3d8/d3d8_interface.cpp Normal file
View File

@ -0,0 +1,140 @@
#include "d3d8_interface.h"
#include "d3d8_device.h"
#include "d3d8_texture.h"
#include <cstring>
namespace dxvk
{
D3D8Interface::D3D8Interface() {
m_d3d9 = d3d9::Direct3DCreate9(D3D_SDK_VERSION);
// Get the bridge interface to D3D9.
if (FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), (void**)&m_bridge))) {
throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
}
m_d3d8Options = D3D8Options(*m_bridge->GetConfig());
m_adapterCount = m_d3d9->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_d3d9->GetAdapterModeCount(adapter, fmt);
for (UINT mode = 0; mode < modeCount; mode++) {
m_adapterModes[adapter].emplace_back();
m_d3d9->EnumAdapterModes(adapter, fmt, mode, &(m_adapterModes[adapter].back()));
// can't use modeCount as it's only for one fmt
m_adapterModeCounts[adapter]++;
}
}
}
}
HRESULT STDMETHODCALLTYPE D3D8Interface::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("D3D8Interface::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}
HRESULT STDMETHODCALLTYPE D3D8Interface::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_d3d9->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;
pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier;
pIdentifier->WHQLLevel = identifier9.WHQLLevel;
return res;
}
HRESULT __stdcall D3D8Interface::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 D3D8Interface::CreateDevice(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS* pPresentationParameters,
IDirect3DDevice8** ppReturnedDeviceInterface) {
Com<d3d9::IDirect3DDevice9> pDevice9 = nullptr;
d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);
HRESULT res = m_d3d9->CreateDevice(
Adapter,
(d3d9::D3DDEVTYPE)DeviceType,
hFocusWindow,
BehaviorFlags,
&params,
&pDevice9
);
if (FAILED(res)) {
return res;
}
*ppReturnedDeviceInterface = ref(new D3D8Device(
this, std::move(pDevice9),
DeviceType, hFocusWindow, BehaviorFlags,
pPresentationParameters
));
return res;
}
}

164
src/d3d8/d3d8_interface.h Normal file
View File

@ -0,0 +1,164 @@
#pragma once
#include "d3d8_include.h"
#include "d3d8_d3d9_util.h"
#include "d3d8_options.h"
#include "d3d8_format.h"
#include "../d3d9/d3d9_bridge.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 D3D8Interface final : public ComObjectClamp<IDirect3D8> {
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:
D3D8Interface();
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction) {
return m_d3d9->RegisterSoftwareDevice(pInitializeFunction);
}
UINT STDMETHODCALLTYPE GetAdapterCount() {
return m_d3d9->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_d3d9->GetAdapterDisplayMode(Adapter, (d3d9::D3DDISPLAYMODE*)pMode);
}
HRESULT STDMETHODCALLTYPE CheckDeviceType(
UINT Adapter,
D3DDEVTYPE DevType,
D3DFORMAT AdapterFormat,
D3DFORMAT BackBufferFormat,
BOOL bWindowed) {
return m_d3d9->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_d3d9->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_d3d9->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) {
if (isSupportedDepthStencilFormat(DepthStencilFormat))
return m_d3d9->CheckDepthStencilMatch(
Adapter,
(d3d9::D3DDEVTYPE)DeviceType,
(d3d9::D3DFORMAT)AdapterFormat,
(d3d9::D3DFORMAT)RenderTargetFormat,
(d3d9::D3DFORMAT)DepthStencilFormat
);
return D3DERR_NOTAVAILABLE;
}
HRESULT STDMETHODCALLTYPE GetDeviceCaps(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DCAPS8* pCaps) {
d3d9::D3DCAPS9 caps9;
HRESULT res = m_d3d9->GetDeviceCaps(Adapter, (d3d9::D3DDEVTYPE)DeviceType, &caps9);
dxvk::ConvertCaps8(caps9, pCaps);
return res;
}
HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter) {
return m_d3d9->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<UINT> m_adapterModeCounts;
std::vector<std::vector<d3d9::D3DDISPLAYMODE>> m_adapterModes;
d3d9::IDirect3D9* m_d3d9;
Com<IDxvkD3D8InterfaceBridge> m_bridge;
D3D8Options m_d3d8Options;
};
}

24
src/d3d8/d3d8_main.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "d3d8_interface.h"
namespace dxvk {
Logger Logger::s_instance("d3d8.log");
HRESULT CreateD3D8(IDirect3D8** ppDirect3D8) {
if (!ppDirect3D8)
return D3DERR_INVALIDCALL;
*ppDirect3D8 = ref(new D3D8Interface());
return D3D_OK;
}
}
extern "C" {
DLLEXPORT IDirect3D8* __stdcall Direct3DCreate8(UINT nSDKVersion) {
dxvk::Logger::trace("Direct3DCreate8 called");
IDirect3D8* pDirect3D = nullptr;
dxvk::CreateD3D8(&pDirect3D);
return pDirect3D;
}
}

55
src/d3d8/d3d8_options.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "d3d8_options.h"
#include "../d3d9/d3d9_bridge.h"
#include "../util/config/config.h"
#include "../util/util_string.h"
#include <charconv>
namespace dxvk {
static inline uint32_t parseDword(std::string_view str) {
uint32_t value = UINT32_MAX;
std::from_chars(str.data(), str.data() + str.size(), value);
return value;
}
void D3D8Options::parseVsDecl(const std::string& decl) {
if (decl.empty())
return;
if (decl.find_first_of("0123456789") == std::string::npos) {
Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl));
Logger::warn("D3D8: Expected numbers.");
return;
}
if (decl.find_first_of(":,;") == std::string::npos) {
Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl));
Logger::warn("D3D8: Expected a comma-separated list of colon-separated number pairs.");
return;
}
std::vector<std::string_view> decls = str::split(decl, ":,;");
if (decls.size() % 2 != 0) {
Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl));
Logger::warn("D3D8: Expected an even number of numbers.");
return;
}
for (size_t i = 0; i < decls.size(); i += 2) {
uint32_t reg = parseDword(decls[i]);
uint32_t type = parseDword(decls[i+1]);
if (reg > D3DVSDE_NORMAL2) {
Logger::warn(str::format("D3D8: Invalid forceVsDecl register number: ", decls[i]));
return;
}
if (type > D3DVSDT_SHORT4) {
Logger::warn(str::format("D3D8: Invalid forceVsDecl type: ", decls[i+1]));
return;
}
forceVsDecl.emplace_back(D3DVSDE_REGISTER(reg), D3DVSDT_TYPE(type));
}
}
}

43
src/d3d8/d3d8_options.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "d3d8_include.h"
#include "../d3d9/d3d9_bridge.h"
#include "../util/config/config.h"
namespace dxvk {
struct D3D8Options {
/// Remap DEFAULT pool vertex and index buffers above this size to the MANAGED pool
/// to improve performance by avoiding waiting for games that frequently lock (large) buffers.
///
/// This implicitly disables direct buffer mapping. Some applications may need this option
/// disabled for certain (smaller) buffers to keep from overwriting in-use buffer regions.
uint32_t managedBufferPlacement = 0;
/// Some games rely on undefined behavior by using undeclared vertex shader inputs.
/// The simplest way to fix them is to simply modify their vertex shader decl.
///
/// This option takes a comma-separated list of colon-separated number pairs, where
/// the first number is a D3DVSDE_REGISTER value, the second is a D3DVSDT_TYPE value.
/// e.g. "0:2,3:2,7:1" for float3 position : v0, float3 normal : v3, float2 uv : v7
std::vector<std::pair<D3DVSDE_REGISTER, D3DVSDT_TYPE>> forceVsDecl;
/// Specialized drawcall batcher, typically for games that draw a lot of similar
/// geometry in separate drawcalls (sometimes even one triangle at a time).
///
/// May hurt performance outside of specifc games that benefit from it.
bool batching = false;
D3D8Options() {}
D3D8Options(const Config& config) {
int32_t minManagedSize = config.getOption<int32_t> ("d3d8.managedBufferPlacement", managedBufferPlacement);
managedBufferPlacement = config.getOption<bool> ("d3d8.managedBufferPlacement", true) ? minManagedSize : UINT32_MAX;
auto forceVsDeclStr = config.getOption<std::string>("d3d8.forceVsDecl", "");
batching = config.getOption<bool> ("d3d8.batching", batching);
parseVsDecl(forceVsDeclStr);
}
void parseVsDecl(const std::string& decl);
};
}

100
src/d3d8/d3d8_resource.h Normal file
View File

@ -0,0 +1,100 @@
#pragma once
/** Implements IDirect3DResource8
*
* - SetPrivateData, GetPrivateData, FreePrivateData
* - SetPriority, GetPriority
*
* - Subclasses provide: PreLoad, GetType
*/
#include "d3d8_device_child.h"
#include "../util/com/com_private_data.h"
namespace dxvk {
template <typename D3D9, typename D3D8>
class D3D8Resource : public D3D8DeviceChild<D3D9, D3D8> {
public:
D3D8Resource(D3D8Device* pDevice, Com<D3D9>&& Object)
: D3D8DeviceChild<D3D9, D3D8>(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<IUnknown*>(
reinterpret_cast<const IUnknown*>(pData));
hr = m_privateData.setInterface(
refguid, unknown);
}
else
hr = m_privateData.setData(
refguid, SizeOfData, pData);
if (FAILED(hr))
return D3DERR_INVALIDCALL;
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE GetPrivateData(
REFGUID refguid,
void* pData,
DWORD* pSizeOfData) final {
HRESULT hr = m_privateData.getData(
refguid, reinterpret_cast<UINT*>(pSizeOfData), pData);
if (FAILED(hr))
return D3DERR_INVALIDCALL;
return D3D_OK;
}
HRESULT STDMETHODCALLTYPE FreePrivateData(REFGUID refguid) final {
HRESULT hr = m_privateData.setData(refguid, 0, nullptr);
if (FAILED(hr))
return D3DERR_INVALIDCALL;
return D3D_OK;
}
DWORD STDMETHODCALLTYPE SetPriority(DWORD PriorityNew) {
DWORD oldPriority = m_priority;
m_priority = PriorityNew;
return oldPriority;
}
DWORD STDMETHODCALLTYPE GetPriority() {
return m_priority;
}
virtual IUnknown* GetInterface(REFIID riid) override try {
return D3D8DeviceChild<D3D9, D3D8>::GetInterface(riid);
} catch (HRESULT err) {
if (riid == __uuidof(IDirect3DResource8))
return this;
throw err;
}
protected:
DWORD m_priority;
private:
ComPrivateData m_privateData;
};
}

336
src/d3d8/d3d8_shader.cpp Normal file
View File

@ -0,0 +1,336 @@
#include "d3d8_shader.h"
#define VSD_SHIFT_MASK(token, field) ((token & field ## MASK) >> field ## SHIFT)
#define VSD_ENCODE(token, field) ((token << field ## _SHIFT) & field ## _MASK)
// Magic number from D3DVSD_SKIP(...)
#define VSD_SKIP_FLAG 0x10000000
// This bit is set on all parameter (non-instruction) tokens.
#define VS_BIT_PARAM 0x80000000
namespace dxvk {
static constexpr int D3D8_NUM_VERTEX_INPUT_REGISTERS = 17;
/**
* Standard mapping of vertex input registers v0-v16 to D3D9 usages and usage indices
* (See D3DVSDE_REGISTER values in d3d8types.h or DirectX 8 docs for vertex shader input registers vN)
*
* \cite https://learn.microsoft.com/en-us/windows/win32/direct3d9/mapping-between-a-directx-9-declaration-and-directx-8
*/
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 d3d9::D3DDECLTYPE or d3d8 D3DVSDT_TYPE */
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
*
* \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 |= VSD_ENCODE(usage, D3DSP_DCL_USAGE); // bits 0:4 DxsoUsage (TODO: missing MSB)
token |= VSD_ENCODE(index, D3DSP_DCL_USAGEINDEX); // bits 16:19 usageIndex
token |= 1 << 31; // bit 31 always 1
return token;
}
/**
* Converts a D3D8 vertex shader + declaration
* to a D3D9 vertex shader + declaration.
*/
D3D9VertexShaderCode TranslateVertexShader8(
const DWORD* pDeclaration,
const DWORD* pFunction,
const D3D8Options& options) {
using d3d9::D3DDECLTYPE;
using d3d9::D3DDECLTYPE_UNUSED;
D3D9VertexShaderCode result;
std::vector<DWORD>& tokens = result.function;
std::vector<DWORD> 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;
auto addVertexElement = [&] (D3DVSDE_REGISTER reg, D3DVSDT_TYPE type) {
vertexElements[elementIdx].Stream = currentStream;
vertexElements[elementIdx].Offset = currentOffset;
vertexElements[elementIdx].Method = d3d9::D3DDECLMETHOD_DEFAULT;
vertexElements[elementIdx].Type = D3DDECLTYPE(type); // (D3DVSDT_TYPE values map directly to D3DDECLTYPE)
vertexElements[elementIdx].Usage = D3D8_VERTEX_INPUT_REGISTERS[reg][0];
vertexElements[elementIdx].UsageIndex = D3D8_VERTEX_INPUT_REGISTERS[reg][1];
// Increase stream offset
currentOffset += D3D9_DECL_TYPE_SIZES[type];
// Enable register vn
shaderInputRegisters |= 1 << reg;
// Finished with this element
elementIdx++;
};
// Remap d3d8 decl tokens to d3d9 vertex elements,
// and enable bits on shaderInputRegisters for each.
if (options.forceVsDecl.size() == 0) 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 = WORD(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 += WORD(skipCount) * sizeof(DWORD);
break;
}
// D3DVSD_REG
DWORD dataLoadType = VSD_SHIFT_MASK(token, D3DVSD_DATALOADTYPE);
if ( dataLoadType == 0 ) { // vertex
D3DVSDT_TYPE type = D3DVSDT_TYPE(VSD_SHIFT_MASK(token, D3DVSD_DATATYPE));
D3DVSDE_REGISTER reg = D3DVSDE_REGISTER(VSD_SHIFT_MASK(token, D3DVSD_VERTEXREG));
addVertexElement(reg, type);
dbg << "type=" << type << ", register=" << reg;
} else {
// TODO: When would this bit be 1?
dbg << "D3DVSD_DATALOADTYPE " << dataLoadType;
}
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;
break;
}
case D3DVSD_TOKEN_END: {
vertexElements[elementIdx++] = D3DDECL_END();
dbg << "END";
break;
}
default:
dbg << "UNKNOWN TYPE";
break;
}
dbg << "\n\t";
//dbg << std::hex << token << " ";
} while (token != D3DVSD_END());
Logger::debug(dbg.str());
// If forceVsDecl is set, use that decl instead.
if (options.forceVsDecl.size() > 0) {
for (auto [reg, type] : options.forceVsDecl) {
addVertexElement(reg, type);
}
vertexElements[elementIdx++] = D3DDECL_END();
}
if (pFunction != nullptr) {
// Copy first token (version)
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);
} while (token != D3DVS_END());
}
return result;
}
}

18
src/d3d8/d3d8_shader.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "d3d8_include.h"
#include "d3d8_options.h"
namespace dxvk {
struct D3D9VertexShaderCode {
d3d9::D3DVERTEXELEMENT9 declaration[MAXD3DDECLLENGTH + 1];
std::vector<DWORD> function;
};
D3D9VertexShaderCode TranslateVertexShader8(
const DWORD* pDeclaration,
const DWORD* pFunction,
const D3D8Options& overrides);
}

View File

@ -0,0 +1,50 @@
#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_textures[stage] = m_device->m_textures[stage].ptr();
}
if (m_capture.indices) {
m_baseVertexIndex = m_device->m_baseVertexIndex;
m_indices = m_device->m_indices.ptr();
}
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;
}

135
src/d3d8/d3d8_state_block.h Normal file
View File

@ -0,0 +1,135 @@
#pragma once
#include "d3d8_caps.h"
#include "d3d8_include.h"
#include "d3d8_device.h"
#include "d3d8_device_child.h"
#include <array>
namespace dxvk {
struct D3D8StateCapture {
bool vs : 1;
bool ps : 1;
bool indices : 1;
bool swvp : 1;
bit::bitset<d8caps::MAX_TEXTURE_STAGES> 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(
D3D8Device* pDevice,
D3DSTATEBLOCKTYPE Type,
Com<d3d9::IDirect3DStateBlock9>&& pStateBlock)
: m_device(pDevice)
, m_stateBlock(std::move(pStateBlock))
, m_type(Type) {
if (Type == D3DSBT_VERTEXSTATE || Type == D3DSBT_ALL) {
// Lights, D3DTSS_TEXCOORDINDEX and D3DTSS_TEXTURETRANSFORMFLAGS,
// vertex shader, VS constants, and various render states.
m_capture.vs = true;
}
if (Type == D3DSBT_PIXELSTATE || Type == D3DSBT_ALL) {
// Pixel shader, PS constants, and various RS/TSS states.
m_capture.ps = true;
}
if (Type == D3DSBT_ALL) {
m_capture.indices = true;
m_capture.swvp = true;
m_capture.textures.setAll();
}
m_textures.fill(nullptr);
}
~D3D8StateBlock() {}
// Construct a state block without a D3D9 object
D3D8StateBlock(D3D8Device* pDevice)
: D3D8StateBlock(pDevice, D3DSTATEBLOCKTYPE(0), nullptr) {
}
// Attach a D3D9 object to a state block that doesn't have one yet
void SetD3D9(Com<d3d9::IDirect3DStateBlock9>&& 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:
D3D8Device* m_device;
Com<d3d9::IDirect3DStateBlock9> m_stateBlock;
D3DSTATEBLOCKTYPE m_type;
private: // State Data //
D3D8StateCapture m_capture;
DWORD m_vertexShader; // vs
DWORD m_pixelShader; // ps
std::array<IDirect3DBaseTexture8*, d8caps::MAX_TEXTURE_STAGES> m_textures; // textures
IDirect3DIndexBuffer8* m_indices = nullptr; // indices
UINT m_baseVertexIndex; // indices
bool m_isSWVP; // D3DRS_SOFTWAREVERTEXPROCESSING
};
}

View File

@ -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 <typename D3D9, typename D3D8>
class D3D8Subresource : public D3D8Resource<D3D9, D3D8> {
using Resource = D3D8Resource<D3D9, D3D8>;
public:
D3D8Subresource(
D3D8Device* pDevice,
Com<D3D9>&& 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;
};
}

25
src/d3d8/d3d8_surface.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "d3d8_surface.h"
#include "d3d8_device.h"
namespace dxvk {
Com<d3d9::IDirect3DSurface9> D3D8Surface::CreateBlitImage() {
d3d9::D3DSURFACE_DESC desc;
GetD3D9()->GetDesc(&desc);
Com<d3d9::IDirect3DSurface9> 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;
}
}

81
src/d3d8/d3d8_surface.h Normal file
View File

@ -0,0 +1,81 @@
#pragma once
#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<d3d9::IDirect3DSurface9, IDirect3DSurface8>;
class D3D8Surface final : public D3D8SurfaceBase {
public:
D3D8Surface(
D3D8Device* pDevice,
IDirect3DBaseTexture8* pTexture,
Com<d3d9::IDirect3DSurface9>&& pSurface)
: D3D8SurfaceBase (pDevice, std::move(pSurface), pTexture) {
}
// A surface does not need to be attached to a texture
D3D8Surface(
D3D8Device* pDevice,
Com<d3d9::IDirect3DSurface9>&& 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<d3d9::IDirect3DSurface9> GetBlitImage() {
if (unlikely(m_blitImage == nullptr)) {
m_blitImage = CreateBlitImage();
}
return m_blitImage;
}
private:
Com<d3d9::IDirect3DSurface9> CreateBlitImage();
Com<d3d9::IDirect3DSurface9> m_blitImage = nullptr;
};
}

42
src/d3d8/d3d8_swapchain.h Normal file
View File

@ -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<d3d9::IDirect3DSwapChain9, IDirect3DSwapChain8>;
class D3D8SwapChain final : public D3D8SwapChainBase {
public:
D3D8SwapChain(
D3D8Device* pDevice,
Com<d3d9::IDirect3DSwapChain9>&& 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 D3D8Device::GetBackBuffer
if (unlikely(m_backBuffer == nullptr)) {
Com<d3d9::IDirect3DSurface9> 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<D3D8Surface> m_backBuffer = nullptr;
};
}

233
src/d3d8/d3d8_texture.h Normal file
View File

@ -0,0 +1,233 @@
#pragma once
#include "d3d8_resource.h"
#include "d3d8_surface.h"
#include "d3d8_volume.h"
#include "d3d8_d3d9_util.h"
#include <vector>
#include <new>
namespace dxvk {
template <typename SubresourceType, typename D3D9, typename D3D8>
class D3D8BaseTexture : public D3D8Resource<D3D9, D3D8> {
public:
constexpr static UINT CUBE_FACES = 6;
using SubresourceType8 = typename SubresourceType::D3D8;
using SubresourceType9 = typename SubresourceType::D3D9;
D3D8BaseTexture(
D3D8Device* pDevice,
Com<D3D9>&& pBaseTexture,
UINT SubresourceCount)
: D3D8Resource<D3D9, D3D8> ( 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<D3D9, D3D8>::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<SubresourceType9> 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:
Com<SubresourceType9> LookupSubresource(UINT Index) {
Com<SubresourceType9> ptr = nullptr;
HRESULT res = D3DERR_INVALIDCALL;
if constexpr (std::is_same_v<D3D8, IDirect3DTexture8>) {
res = this->GetD3D9()->GetSurfaceLevel(Index, &ptr);
} else if constexpr (std::is_same_v<D3D8, IDirect3DVolume8>) {
res = this->GetD3D9()->GetVolumeLevel(Index, &ptr);
} else if constexpr (std::is_same_v<D3D8, IDirect3DCubeTexture8>) {
res = this->GetD3D9()->GetCubeMapSurface(d3d9::D3DCUBEMAP_FACES(Index % CUBE_FACES), Index / CUBE_FACES, &ptr);
}
if (FAILED(res))
throw res;
return ptr;
}
std::vector<Com<SubresourceType, false>> m_subresources;
};
using D3D8Texture2DBase = D3D8BaseTexture<D3D8Surface, d3d9::IDirect3DTexture9, IDirect3DTexture8>;
class D3D8Texture2D final : public D3D8Texture2DBase {
public:
D3D8Texture2D(
D3D8Device* pDevice,
Com<d3d9::IDirect3DTexture9>&& 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<d3d9::D3DLOCKED_RECT*>(pLockedRect), pRect, Flags);
}
HRESULT STDMETHODCALLTYPE UnlockRect(UINT Level) {
return GetD3D9()->UnlockRect(Level);
}
HRESULT STDMETHODCALLTYPE AddDirtyRect(CONST RECT* pDirtyRect) {
return GetD3D9()->AddDirtyRect(pDirtyRect);
}
};
using D3D8Texture3DBase = D3D8BaseTexture<D3D8Volume, d3d9::IDirect3DVolumeTexture9, IDirect3DVolumeTexture8>;
class D3D8Texture3D final : public D3D8Texture3DBase {
public:
D3D8Texture3D(
D3D8Device* pDevice,
Com<d3d9::IDirect3DVolumeTexture9>&& 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<d3d9::D3DLOCKED_BOX*>(pLockedBox),
reinterpret_cast<const d3d9::D3DBOX*>(pBox),
Flags
);
}
HRESULT STDMETHODCALLTYPE UnlockBox(UINT Level) {
return GetD3D9()->UnlockBox(Level);
}
HRESULT STDMETHODCALLTYPE AddDirtyBox(CONST D3DBOX* pDirtyBox) {
return GetD3D9()->AddDirtyBox(reinterpret_cast<const d3d9::D3DBOX*>(pDirtyBox));
}
};
using D3D8TextureCubeBase = D3D8BaseTexture<D3D8Surface, d3d9::IDirect3DCubeTexture9, IDirect3DCubeTexture8>;
class D3D8TextureCube final : public D3D8TextureCubeBase {
public:
D3D8TextureCube(
D3D8Device* pDevice,
Com<d3d9::IDirect3DCubeTexture9>&& 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<d3d9::D3DLOCKED_RECT*>(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);
}
};
}

40
src/d3d8/d3d8_volume.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include "d3d8_subresource.h"
#include "d3d8_d3d9_util.h"
namespace dxvk {
using D3D8VolumeBase = D3D8Subresource<d3d9::IDirect3DVolume9, IDirect3DVolume8>;
class D3D8Volume final : public D3D8VolumeBase {
public:
D3D8Volume(
D3D8Device* pDevice,
IDirect3DVolumeTexture8* pTexture,
Com<d3d9::IDirect3DVolume9>&& 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<d3d9::D3DLOCKED_BOX*>(pLockedBox),
reinterpret_cast<const d3d9::D3DBOX*>(pBox),
Flags
);
}
HRESULT STDMETHODCALLTYPE UnlockBox() final {
return GetD3D9()->UnlockBox();
}
};
}

View File

@ -0,0 +1,68 @@
#pragma once
#include "d3d8_include.h"
namespace dxvk {
template <typename D3D9Type, typename D3D8Type>
class D3D8WrappedObject : public ComObjectClamp<D3D8Type> {
public:
using D3D9 = D3D9Type;
using D3D8 = D3D8Type;
D3D8WrappedObject(Com<D3D9>&& 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 <typename T>
static D3D9* GetD3D9Nullable(Com<T>& 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<D3D9> m_d3d9;
};
}

32
src/d3d8/meson.build Normal file
View File

@ -0,0 +1,32 @@
d3d8_res = wrc_generator.process('version.rc')
d3d8_src = [
'd3d8_main.cpp',
'd3d8_interface.cpp',
'd3d8_device.cpp',
'd3d8_options.cpp',
'd3d8_surface.cpp',
'd3d8_state_block.cpp',
'd3d8_shader.cpp'
]
d3d8_ld_args = []
d3d8_link_depends = []
if platform != 'windows'
lib_d3d9 = d3d9_dep
d3d8_ld_args += [ '-Wl,--version-script', join_paths(meson.current_source_dir(), 'd3d8.sym') ]
d3d8_link_depends += files('d3d8.sym')
endif
d3d8_dll = shared_library('d3d8'+dll_ext, d3d8_src, d3d8_res,
name_prefix : dxvk_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,
link_args : d3d8_ld_args,
link_depends : [ d3d8_link_depends ],
)

31
src/d3d8/version.rc Normal file
View File

@ -0,0 +1,31 @@
#include <windows.h>
// DLL version information.
VS_VERSION_INFO VERSIONINFO
FILEVERSION 10,0,17763,1
PRODUCTVERSION 10,0,17763,1
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080904b0"
BEGIN
VALUE "CompanyName", "DXVK"
VALUE "FileDescription", "Direct3D 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

View File

@ -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

View File

@ -644,10 +644,6 @@ namespace dxvk {
{ R"(\\SWTFU2\.exe$)", {{
{ "d3d9.forceSamplerTypeSpecConstants", "True" },
}} },
/* Scrapland (Remastered) */
{ R"(\\Scrap\.exe$)", {{
{ "d3d9.deferSurfaceCreation", "True" },
}} },
/* Majesty 2 (Collection) *
* Crashes on UMA without a memory limit, *
* since the game(s) will allocate all *
@ -676,10 +672,6 @@ namespace dxvk {
{ R"(\\bionic_commando\.exe$)", {{
{ "d3d9.maxFrameRate", "60" },
}} },
/* Need For Speed 3 modern patch */
{ R"(\\nfs3\.exe$)", {{
{ "d3d9.enableDialogMode", "True" },
}} },
/* Beyond Good And Evil *
* UI breaks at high fps */
{ R"(\\BGE\.exe$)", {{
@ -817,6 +809,129 @@ namespace dxvk {
{ R"(\\WILD HEARTS(_Trial)?\.exe$)", {{
{ "dxvk.maxChunkSize", "4" },
}} },
/**********************************************/
/* 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" },
}} },
/* Motor City Online */
{ R"(\\MCity_d\.exe$)", {{
{ "d3d9.cachedDynamicBuffers", "True" },
{ "d3d8.managedBufferPlacement", "False" },
{ "d3d8.batching", "True" },
}} },
/* Brigade E5: New Jagged Union */
{ R"(\\E5\.exe$)", {{
{ "d3d8.managedBufferPlacement", "False" },
}} },
/* Railroad Tycoon 3 */
{ R"(\\RT3\.exe$)", {{
{ "d3d9.maxFrameRate", "60" },
{ "d3d8.managedBufferPlacement", "False" },
}} },
/* Supreme Ruler 2020: Gold *
* Only put the large main vertex buffer in *
* MANAGED to fix flickering on text and UI. */
{ R"(\\SupremeRuler2020GC\.exe$)", {{
{ "d3d8.managedBufferPlacement", "21600" },
}} },
/* Pure Pinball 2.0 REDUX *
* This game reads from undeclared vs inputs *
* but somehow works on native. Let's just *
* change its declaration to make them work. */
{ R"(\\Pure Pinball 2\.0 REDUX\.exe$)", {{
{ "d3d8.forceVsDecl", "0:2,4:2,7:4,9:1,8:1" },
}} },
/* Supreme Ruler 2010 *
* Needs the same workaround as SR2020 to *
* fix flickering on UI text */
{ R"(\\SupremeRuler\.exe$)", {{
{ "d3d8.managedBufferPlacement", "21600" },
}} },
/* Need for Speed III: Hot Pursuit *
(with the "Modern Patch") */
{ R"(\\nfs3\.exe$)", {{
{ "d3d9.enableDialogMode", "True" },
{ "d3d9.cachedDynamicBuffers", "True" },
{ "d3d8.managedBufferPlacement", "False" },
{ "d3d8.batching", "True" },
}} },
/* Need for Speed: High Stakes / Road *
Challenge (with the "Modern Patch") - *
Won't actually render anything in game *
without a memory limit in place */
{ R"(\\nfs4\.exe$)", {{
{ "d3d9.enableDialogMode", "True" },
{ "d3d9.cachedDynamicBuffers", "True" },
{ "d3d9.memoryTrackTest", "True" },
{ "d3d9.maxAvailableMemory", "256" },
{ "d3d8.managedBufferPlacement", "False" },
{ "d3d8.batching", "True" },
}} },
/* Project I.G.I. 2: Covert Strike */
{ R"(\\igi2\.exe$)", {{
{ "d3d8.managedBufferPlacement", "False" },
{ "d3d9.cachedDynamicBuffers", "True" },
}} },
/* Treasure Planet: Battle at Procyon *
* Declares v5 as color but shader uses v6 */
{ R"(\\TP_Win32\.exe$)", {{
{ "d3d8.forceVsDecl", "0:2,3:2,6:4,7:1" },
}} },
/* D&D - The Temple Of Elemental Evil */
{ R"(\\ToEE(a)?\.exe$)", {{
{ "d3d9.allowDiscard", "False" },
}} },
/* Scrapland (Remastered) */
{ R"(\\Scrap\.exe$)", {{
{ "d3d9.deferSurfaceCreation", "True" },
}} },
/* Port Royale 2 *
* UI rendering issues with managed buffers */
{ R"(\\PR2\.exe$)", {{
{ "d3d8.managedBufferPlacement", "False" },
}} },
/* Sherlock Holmes: The Secret of the Silver *
* Earring */
{ R"(\\Sherlock Holmes.*(SSE|Silver Earring)\\game\.exe$)", {{
{ "d3d8.managedBufferPlacement", "False" },
}} },
/* The Guild Gold Edition (Europa 1400) *
* UI rendering issues with managed buffers */
{ R"(\\Europa1400Gold_TL\.exe$)", {{
{ "d3d8.managedBufferPlacement", "False" },
}} },
/* Icoming Forces */
{ R"(\\forces\.exe$)", {{
{ "d3d8.managedBufferPlacement", "False" },
}} },
/* Chaser */
{ R"(\\Chaser\.exe$)", {{
{ "d3d8.managedBufferPlacement", "False" },
}} },
/* Rise of Nations Gold */
{ R"(\\(nations|patriots)\.exe$)", {{
{ "d3d8.managedBufferPlacement", "False" },
}} },
}};

View File

@ -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(