mirror of
https://github.com/doitsujin/dxvk.git
synced 2024-11-28 16:24:13 +01:00
[d3d8] Implement Direct3D 8 Frontend
Co-authored-by: WinterSnowfall <WinterSnowfall@users.noreply.github.com> ## Config Changes Co-authored-by: Blisto91 <47954800+Blisto91@users.noreply.github.com> Co-authored-by: simifor <simirmonfor@gmail.com>
This commit is contained in:
parent
ef0c6b6f6f
commit
60e523b4bf
7
.github/workflows/test-build-windows.yml
vendored
7
.github/workflows/test-build-windows.yml
vendored
@ -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: |
|
||||
|
1
LICENSE
1
LICENSE
@ -1,5 +1,6 @@
|
||||
Copyright (c) 2017 Philip Rebohle
|
||||
Copyright (c) 2019 Joshua Ashton
|
||||
Copyright (c) 2023 Jeffrey Ellison
|
||||
|
||||
zlib/libpng license
|
||||
|
||||
|
@ -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).
|
||||
|
||||
|
26
dxvk.conf
26
dxvk.conf
@ -709,3 +709,29 @@
|
||||
# - True/False
|
||||
|
||||
# d3d9.countLosableResources = True
|
||||
|
||||
# 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
|
||||
|
@ -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
3
src/d3d8/d3d8.def
Normal file
@ -0,0 +1,3 @@
|
||||
LIBRARY D3D8.DLL
|
||||
EXPORTS
|
||||
Direct3DCreate8 @ 5
|
7
src/d3d8/d3d8.sym
Normal file
7
src/d3d8/d3d8.sym
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
global:
|
||||
Direct3DCreate8;
|
||||
|
||||
local:
|
||||
*;
|
||||
};
|
239
src/d3d8/d3d8_batch.h
Normal file
239
src/d3d8/d3d8_batch.h
Normal file
@ -0,0 +1,239 @@
|
||||
#pragma once
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "d3d8_buffer.h"
|
||||
#include "d3d8_format.h"
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <climits>
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
inline constexpr size_t D3DPT_COUNT = size_t(D3DPT_TRIANGLEFAN) + 1;
|
||||
inline 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;
|
||||
|
||||
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() {
|
||||
// Nothing to be done.
|
||||
}
|
||||
|
||||
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
102
src/d3d8/d3d8_buffer.h
Normal 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
8
src/d3d8/d3d8_caps.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace dxvk::d8caps {
|
||||
|
||||
inline constexpr uint32_t MAX_TEXTURE_STAGES = 8;
|
||||
inline constexpr uint32_t MAX_STREAMS = 16;
|
||||
|
||||
}
|
164
src/d3d8/d3d8_d3d9_util.h
Normal file
164
src/d3d8/d3d8_d3d9_util.h
Normal file
@ -0,0 +1,164 @@
|
||||
#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 {
|
||||
|
||||
// (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1738
src/d3d8/d3d8_device.cpp
Normal file
1738
src/d3d8/d3d8_device.cpp
Normal file
@ -0,0 +1,1738 @@
|
||||
#include "d3d8_device.h"
|
||||
#include "d3d8_interface.h"
|
||||
#include "d3d8_shader.h"
|
||||
|
||||
#ifdef MSC_VER
|
||||
#pragma fenv_access (on)
|
||||
#endif
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
static constexpr DWORD isFVF(DWORD Handle) {
|
||||
return (Handle & D3DFVF_RESERVED0) == 0;
|
||||
}
|
||||
|
||||
static constexpr DWORD getShaderHandle(DWORD Index) {
|
||||
return (Index << 1) | D3DFVF_RESERVED0;
|
||||
}
|
||||
|
||||
static constexpr DWORD getShaderIndex(DWORD Handle) {
|
||||
if ((Handle & D3DFVF_RESERVED0) != 0) {
|
||||
return (Handle & ~(D3DFVF_RESERVED0)) >> 1;
|
||||
} else {
|
||||
return Handle;
|
||||
}
|
||||
}
|
||||
|
||||
struct D3D8VertexShaderInfo {
|
||||
d3d9::IDirect3DVertexDeclaration9* pVertexDecl = nullptr;
|
||||
d3d9::IDirect3DVertexShader9* pVertexShader = nullptr;
|
||||
std::vector<DWORD> declaration;
|
||||
std::vector<DWORD> function;
|
||||
};
|
||||
|
||||
D3D8Device::D3D8Device(
|
||||
D3D8Interface* pParent,
|
||||
Com<d3d9::IDirect3DDevice9>&& pDevice,
|
||||
D3DDEVTYPE DeviceType,
|
||||
HWND hFocusWindow,
|
||||
DWORD BehaviorFlags,
|
||||
D3DPRESENT_PARAMETERS* pParams)
|
||||
: D3D8DeviceBase(std::move(pDevice))
|
||||
, m_d3d8Options(pParent->GetOptions())
|
||||
, m_parent(pParent)
|
||||
, m_presentParams(*pParams)
|
||||
, m_deviceType(DeviceType)
|
||||
, m_window(hFocusWindow)
|
||||
, m_behaviorFlags(BehaviorFlags) {
|
||||
// Get the bridge interface to D3D9.
|
||||
if (FAILED(GetD3D9()->QueryInterface(__uuidof(IDxvkD3D8Bridge), (void**)&m_bridge))) {
|
||||
throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
|
||||
}
|
||||
|
||||
m_bridge->SetAPIName("D3D8");
|
||||
m_bridge->SetD3D8CompatibilityMode(true);
|
||||
|
||||
ResetState();
|
||||
RecreateBackBuffersAndAutoDepthStencil();
|
||||
|
||||
if (m_d3d8Options.batching)
|
||||
m_batcher = new D3D8Batcher(this, GetD3D9());
|
||||
}
|
||||
|
||||
D3D8Device::~D3D8Device() {
|
||||
if (m_batcher)
|
||||
delete m_batcher;
|
||||
|
||||
// Delete any remaining state blocks.
|
||||
for (D3D8StateBlock* block : m_stateBlocks) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetInfo(DWORD DevInfoID, void* pDevInfoStruct, DWORD DevInfoStructSize) {
|
||||
Logger::debug(str::format("D3D8Device::GetInfo: ", DevInfoID));
|
||||
|
||||
if (unlikely(pDevInfoStruct == nullptr || DevInfoStructSize == 0))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
HRESULT res;
|
||||
d3d9::IDirect3DQuery9* pQuery = nullptr;
|
||||
|
||||
switch (DevInfoID) {
|
||||
// pre-D3D8 queries
|
||||
case 0:
|
||||
case D3DDEVINFOID_TEXTUREMANAGER:
|
||||
case D3DDEVINFOID_D3DTEXTUREMANAGER:
|
||||
case D3DDEVINFOID_TEXTURING:
|
||||
return E_FAIL;
|
||||
|
||||
case D3DDEVINFOID_VCACHE:
|
||||
// The query will return D3D_OK on Nvidia and D3DERR_NOTAVAILABLE on AMD/Intel
|
||||
// in D3D9, however in the case of the latter we'll need to return a
|
||||
// zeroed out query result and S_FALSE. This behavior has been observed both
|
||||
// on modern native AMD drivers and D3D8-era native ATI drivers.
|
||||
res = GetD3D9()->CreateQuery(d3d9::D3DQUERYTYPE_VCACHE, &pQuery);
|
||||
|
||||
struct D3DDEVINFO_VCACHE {
|
||||
DWORD Pattern;
|
||||
DWORD OptMethod;
|
||||
DWORD CacheSize;
|
||||
DWORD MagicNumber;
|
||||
};
|
||||
|
||||
if(FAILED(res)) {
|
||||
if (DevInfoStructSize != sizeof(D3DDEVINFO_VCACHE))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
memset(pDevInfoStruct, 0, sizeof(D3DDEVINFO_VCACHE));
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
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("D3D8Device::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 D3D8Device::TestCooperativeLevel() {
|
||||
// Equivalent of D3D11/DXGI present tests.
|
||||
return GetD3D9()->TestCooperativeLevel();
|
||||
}
|
||||
|
||||
UINT STDMETHODCALLTYPE D3D8Device::GetAvailableTextureMem() {
|
||||
return GetD3D9()->GetAvailableTextureMem();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::ResourceManagerDiscardBytes(DWORD bytes) {
|
||||
return GetD3D9()->EvictManagedResources();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetDirect3D(IDirect3D8** ppD3D8) {
|
||||
if (ppD3D8 == nullptr)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
*ppD3D8 = m_parent.ref();
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetDeviceCaps(D3DCAPS8* pCaps) {
|
||||
d3d9::D3DCAPS9 caps9;
|
||||
HRESULT res = GetD3D9()->GetDeviceCaps(&caps9);
|
||||
dxvk::ConvertCaps8(caps9, pCaps);
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetDisplayMode(D3DDISPLAYMODE* pMode) {
|
||||
// swap chain 0
|
||||
return GetD3D9()->GetDisplayMode(0, (d3d9::D3DDISPLAYMODE*)pMode);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS* pParameters) {
|
||||
return GetD3D9()->GetCreationParameters((d3d9::D3DDEVICE_CREATION_PARAMETERS*)pParameters);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetCursorProperties(
|
||||
UINT XHotSpot,
|
||||
UINT YHotSpot,
|
||||
IDirect3DSurface8* pCursorBitmap) {
|
||||
D3D8Surface* surf = static_cast<D3D8Surface*>(pCursorBitmap);
|
||||
return GetD3D9()->SetCursorProperties(XHotSpot, YHotSpot, D3D8Surface::GetD3D9Nullable(surf));
|
||||
}
|
||||
|
||||
void STDMETHODCALLTYPE D3D8Device::SetCursorPosition(UINT XScreenSpace, UINT YScreenSpace, DWORD Flags) {
|
||||
GetD3D9()->SetCursorPosition(XScreenSpace, YScreenSpace, Flags);
|
||||
}
|
||||
|
||||
// Microsoft d3d8.h in the DirectX 9 SDK uses a different function signature...
|
||||
void STDMETHODCALLTYPE D3D8Device::SetCursorPosition(int X, int Y, DWORD Flags) {
|
||||
GetD3D9()->SetCursorPosition(X, Y, Flags);
|
||||
}
|
||||
|
||||
BOOL STDMETHODCALLTYPE D3D8Device::ShowCursor(BOOL bShow) {
|
||||
return GetD3D9()->ShowCursor(bShow);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::CreateAdditionalSwapChain(
|
||||
D3DPRESENT_PARAMETERS* pPresentationParameters,
|
||||
IDirect3DSwapChain8** ppSwapChain) {
|
||||
|
||||
Com<d3d9::IDirect3DSwapChain9> 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 D3D8Device::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) {
|
||||
StateChange();
|
||||
|
||||
m_presentParams = *pPresentationParameters;
|
||||
ResetState();
|
||||
|
||||
d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);
|
||||
HRESULT res = GetD3D9()->Reset(¶ms);
|
||||
|
||||
if (FAILED(res))
|
||||
return res;
|
||||
|
||||
RecreateBackBuffersAndAutoDepthStencil();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::Present(
|
||||
const RECT* pSourceRect,
|
||||
const RECT* pDestRect,
|
||||
HWND hDestWindowOverride,
|
||||
const RGNDATA* pDirtyRegion) {
|
||||
m_batcher->EndFrame();
|
||||
StateChange();
|
||||
return GetD3D9()->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetBackBuffer(
|
||||
UINT iBackBuffer,
|
||||
D3DBACKBUFFER_TYPE Type,
|
||||
IDirect3DSurface8** ppBackBuffer) {
|
||||
InitReturnPtr(ppBackBuffer);
|
||||
|
||||
if (iBackBuffer >= m_backBuffers.size() || m_backBuffers[iBackBuffer] == nullptr) {
|
||||
Com<d3d9::IDirect3DSurface9> pSurface9;
|
||||
HRESULT res = GetD3D9()->GetBackBuffer(0, iBackBuffer, (d3d9::D3DBACKBUFFER_TYPE)Type, &pSurface9);
|
||||
|
||||
if (FAILED(res)) return res;
|
||||
|
||||
m_backBuffers[iBackBuffer] = new D3D8Surface(this, std::move(pSurface9));
|
||||
*ppBackBuffer = m_backBuffers[iBackBuffer].ref();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
*ppBackBuffer = m_backBuffers[iBackBuffer].ref();
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetRasterStatus(D3DRASTER_STATUS* pRasterStatus) {
|
||||
return GetD3D9()->GetRasterStatus(0, (d3d9::D3DRASTER_STATUS*)pRasterStatus);
|
||||
}
|
||||
|
||||
void STDMETHODCALLTYPE D3D8Device::SetGammaRamp(DWORD Flags, const D3DGAMMARAMP* pRamp) {
|
||||
StateChange();
|
||||
// For swap chain 0
|
||||
GetD3D9()->SetGammaRamp(0, Flags, reinterpret_cast<const d3d9::D3DGAMMARAMP*>(pRamp));
|
||||
}
|
||||
|
||||
void STDMETHODCALLTYPE D3D8Device::GetGammaRamp(D3DGAMMARAMP* pRamp) {
|
||||
// For swap chain 0
|
||||
GetD3D9()->GetGammaRamp(0, reinterpret_cast<d3d9::D3DGAMMARAMP*>(pRamp));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::CreateTexture(
|
||||
UINT Width,
|
||||
UINT Height,
|
||||
UINT Levels,
|
||||
DWORD Usage,
|
||||
D3DFORMAT Format,
|
||||
D3DPOOL Pool,
|
||||
IDirect3DTexture8** ppTexture) {
|
||||
InitReturnPtr(ppTexture);
|
||||
|
||||
// Nvidia & Intel workaround for The Lord of the Rings: The Fellowship of the Ring
|
||||
if (m_d3d8Options.placeP8InScratch && Format == D3DFMT_P8)
|
||||
Pool = D3DPOOL_SCRATCH;
|
||||
|
||||
Com<d3d9::IDirect3DTexture9> 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 D3D8Device::CreateVolumeTexture(
|
||||
UINT Width,
|
||||
UINT Height,
|
||||
UINT Depth,
|
||||
UINT Levels,
|
||||
DWORD Usage,
|
||||
D3DFORMAT Format,
|
||||
D3DPOOL Pool,
|
||||
IDirect3DVolumeTexture8** ppVolumeTexture) {
|
||||
Com<d3d9::IDirect3DVolumeTexture9> 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 D3D8Device::CreateCubeTexture(
|
||||
UINT EdgeLength,
|
||||
UINT Levels,
|
||||
DWORD Usage,
|
||||
D3DFORMAT Format,
|
||||
D3DPOOL Pool,
|
||||
IDirect3DCubeTexture8** ppCubeTexture) {
|
||||
Com<d3d9::IDirect3DCubeTexture9> 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 D3D8Device::CreateVertexBuffer(
|
||||
UINT Length,
|
||||
DWORD Usage,
|
||||
DWORD FVF,
|
||||
D3DPOOL Pool,
|
||||
IDirect3DVertexBuffer8** ppVertexBuffer) {
|
||||
InitReturnPtr(ppVertexBuffer);
|
||||
|
||||
if (ShouldBatch()) {
|
||||
*ppVertexBuffer = m_batcher->CreateVertexBuffer(Length, Usage, FVF, Pool);
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
Com<d3d9::IDirect3DVertexBuffer9> pVertexBuffer9 = nullptr;
|
||||
|
||||
HRESULT res = GetD3D9()->CreateVertexBuffer(Length, Usage, FVF, d3d9::D3DPOOL(Pool), &pVertexBuffer9, NULL);
|
||||
|
||||
if (!FAILED(res))
|
||||
*ppVertexBuffer = ref(new D3D8VertexBuffer(this, std::move(pVertexBuffer9), Pool, Usage));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::CreateIndexBuffer(
|
||||
UINT Length,
|
||||
DWORD Usage,
|
||||
D3DFORMAT Format,
|
||||
D3DPOOL Pool,
|
||||
IDirect3DIndexBuffer8** ppIndexBuffer) {
|
||||
InitReturnPtr(ppIndexBuffer);
|
||||
Com<d3d9::IDirect3DIndexBuffer9> pIndexBuffer9 = nullptr;
|
||||
|
||||
HRESULT res = GetD3D9()->CreateIndexBuffer(Length, Usage, d3d9::D3DFORMAT(Format), d3d9::D3DPOOL(Pool), &pIndexBuffer9, NULL);
|
||||
|
||||
if (!FAILED(res))
|
||||
*ppIndexBuffer = ref(new D3D8IndexBuffer(this, std::move(pIndexBuffer9), Pool, Usage));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::CreateRenderTarget(
|
||||
UINT Width,
|
||||
UINT Height,
|
||||
D3DFORMAT Format,
|
||||
D3DMULTISAMPLE_TYPE MultiSample,
|
||||
BOOL Lockable,
|
||||
IDirect3DSurface8** ppSurface) {
|
||||
Com<d3d9::IDirect3DSurface9> 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 D3D8Device::CreateDepthStencilSurface(
|
||||
UINT Width,
|
||||
UINT Height,
|
||||
D3DFORMAT Format,
|
||||
D3DMULTISAMPLE_TYPE MultiSample,
|
||||
IDirect3DSurface8** ppSurface) {
|
||||
Com<d3d9::IDirect3DSurface9> 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 D3D8Device::CreateImageSurface(
|
||||
UINT Width,
|
||||
UINT Height,
|
||||
D3DFORMAT Format,
|
||||
IDirect3DSurface8** ppSurface) {
|
||||
// FIXME: Handle D3DPOOL_SCRATCH in CopyRects
|
||||
D3DPOOL pool = isUnsupportedSurfaceFormat(Format) ? D3DPOOL_SCRATCH : D3DPOOL_SYSTEMMEM;
|
||||
|
||||
Com<d3d9::IDirect3DSurface9> pSurf = nullptr;
|
||||
HRESULT res = GetD3D9()->CreateOffscreenPlainSurface(
|
||||
Width,
|
||||
Height,
|
||||
d3d9::D3DFORMAT(Format),
|
||||
d3d9::D3DPOOL(pool),
|
||||
&pSurf,
|
||||
NULL);
|
||||
|
||||
*ppSurface = ref(new D3D8Surface(this, std::move(pSurf)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief D3D8 CopyRects implementation
|
||||
*
|
||||
* \details
|
||||
* The following table shows the possible combinations of source
|
||||
* and destination surface pools, and how we handle each of them.
|
||||
*
|
||||
* ┌────────────┬───────────────────────────┬───────────────────────┬───────────────────────┬──────────┐
|
||||
* │ Src/Dst │ DEFAULT │ MANAGED │ SYSTEMMEM │ SCRATCH │
|
||||
* ├────────────┼───────────────────────────┼───────────────────────┼───────────────────────┼──────────┤
|
||||
* │ DEFAULT │ StretchRect │ GetRenderTargetData │ GetRenderTargetData │ - │
|
||||
* │ MANAGED │ UpdateTextureFromBuffer │ memcpy │ memcpy │ - │
|
||||
* │ SYSTEMMEM │ UpdateSurface │ memcpy │ memcpy │ - │
|
||||
* │ SCRATCH │ - │ - │ - │ - │
|
||||
* └────────────┴───────────────────────────┴───────────────────────┴───────────────────────┴──────────┘
|
||||
*/
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::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<D3D8Surface> src = static_cast<D3D8Surface*>(pSourceSurface);
|
||||
Com<D3D8Surface> dst = static_cast<D3D8Surface*>(pDestinationSurface);
|
||||
|
||||
d3d9::D3DSURFACE_DESC srcDesc, dstDesc;
|
||||
src->GetD3D9()->GetDesc(&srcDesc);
|
||||
dst->GetD3D9()->GetDesc(&dstDesc);
|
||||
|
||||
// This method cannot be applied to surfaces whose formats
|
||||
// are classified as depth stencil formats.
|
||||
if (unlikely(isDepthStencilFormat(D3DFORMAT(srcDesc.Format)) ||
|
||||
isDepthStencilFormat(D3DFORMAT(dstDesc.Format)))) {
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
StateChange();
|
||||
|
||||
// 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;
|
||||
|
||||
auto unhandled = [&] {
|
||||
Logger::debug(str::format("CopyRects: Hit unhandled case from src pool ", srcDesc.Pool, " to dst pool ", dstDesc.Pool));
|
||||
return 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: {
|
||||
// SYSTEMMEM -> DEFAULT: use UpdateSurface
|
||||
res = GetD3D9()->UpdateSurface(
|
||||
src->GetD3D9(),
|
||||
&srcRect,
|
||||
dst->GetD3D9(),
|
||||
&dstPt
|
||||
);
|
||||
goto done;
|
||||
}
|
||||
case D3DPOOL_SCRATCH:
|
||||
default: {
|
||||
// TODO: Unhandled case.
|
||||
return unhandled();
|
||||
}
|
||||
} break;
|
||||
|
||||
// Dest: MANAGED
|
||||
case D3DPOOL_MANAGED:
|
||||
switch (srcDesc.Pool) {
|
||||
case D3DPOOL_DEFAULT: {
|
||||
// TODO: Copy on GPU (handle MANAGED similarly to SYSTEMMEM for now)
|
||||
|
||||
// Get temporary off-screen surface for stretching.
|
||||
Com<d3d9::IDirect3DSurface9> 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;
|
||||
}
|
||||
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.
|
||||
return unhandled();
|
||||
}
|
||||
} break;
|
||||
|
||||
// DEST: SYSTEMMEM
|
||||
case D3DPOOL_SYSTEMMEM: {
|
||||
|
||||
// RT (DEFAULT) -> SYSTEMMEM: Use GetRenderTargetData as fast path if possible
|
||||
if ((srcDesc.Usage & D3DUSAGE_RENDERTARGET || m_renderTarget.ptr() == src.ptr())) {
|
||||
|
||||
// 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<d3d9::IDirect3DSurface9> 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.
|
||||
return unhandled();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
// DEST: SCRATCH
|
||||
case D3DPOOL_SCRATCH:
|
||||
default: {
|
||||
// TODO: Unhandled case.
|
||||
return unhandled();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::UpdateTexture(
|
||||
IDirect3DBaseTexture8* pSourceTexture,
|
||||
IDirect3DBaseTexture8* pDestinationTexture) {
|
||||
D3D8Texture2D* src = static_cast<D3D8Texture2D*>(pSourceTexture);
|
||||
D3D8Texture2D* dst = static_cast<D3D8Texture2D*>(pDestinationTexture);
|
||||
|
||||
StateChange();
|
||||
return GetD3D9()->UpdateTexture(D3D8Texture2D::GetD3D9Nullable(src), D3D8Texture2D::GetD3D9Nullable(dst));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetFrontBuffer(IDirect3DSurface8* pDestSurface) {
|
||||
if (unlikely(pDestSurface == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
Com<D3D8Surface> surf = static_cast<D3D8Surface*>(pDestSurface);
|
||||
|
||||
StateChange();
|
||||
// This actually gets a copy of the front buffer and writes it to pDestSurface
|
||||
return GetD3D9()->GetFrontBufferData(0, D3D8Surface::GetD3D9Nullable(surf));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetRenderTarget(IDirect3DSurface8* pRenderTarget, IDirect3DSurface8* pNewZStencil) {
|
||||
HRESULT res;
|
||||
|
||||
if (pRenderTarget != NULL) {
|
||||
D3D8Surface* surf = static_cast<D3D8Surface*>(pRenderTarget);
|
||||
|
||||
if(likely(m_renderTarget.ptr() != surf)) {
|
||||
StateChange();
|
||||
res = GetD3D9()->SetRenderTarget(0, surf->GetD3D9());
|
||||
|
||||
if (FAILED(res)) return res;
|
||||
|
||||
if (likely(m_renderTarget != surf))
|
||||
m_renderTarget = surf;
|
||||
}
|
||||
}
|
||||
|
||||
// SetDepthStencilSurface is a separate call
|
||||
D3D8Surface* zStencil = static_cast<D3D8Surface*>(pNewZStencil);
|
||||
|
||||
if(likely(m_depthStencil.ptr() != zStencil)) {
|
||||
StateChange();
|
||||
res = GetD3D9()->SetDepthStencilSurface(D3D8Surface::GetD3D9Nullable(zStencil));
|
||||
|
||||
if (FAILED(res)) return res;
|
||||
|
||||
if (likely(m_depthStencil != zStencil))
|
||||
m_depthStencil = zStencil;
|
||||
}
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetRenderTarget(IDirect3DSurface8** ppRenderTarget) {
|
||||
InitReturnPtr(ppRenderTarget);
|
||||
|
||||
if (unlikely(m_renderTarget == nullptr)) {
|
||||
Com<d3d9::IDirect3DSurface9> pRT9 = nullptr;
|
||||
HRESULT res = GetD3D9()->GetRenderTarget(0, &pRT9); // use RT index 0
|
||||
|
||||
if(FAILED(res)) return res;
|
||||
|
||||
m_renderTarget = new D3D8Surface(this, std::move(pRT9));
|
||||
|
||||
*ppRenderTarget = m_renderTarget.ref();
|
||||
return res;
|
||||
}
|
||||
|
||||
*ppRenderTarget = m_renderTarget.ref();
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetDepthStencilSurface(IDirect3DSurface8** ppZStencilSurface) {
|
||||
InitReturnPtr(ppZStencilSurface);
|
||||
|
||||
if (unlikely(m_depthStencil == nullptr)) {
|
||||
Com<d3d9::IDirect3DSurface9> pStencil9 = nullptr;
|
||||
HRESULT res = GetD3D9()->GetDepthStencilSurface(&pStencil9);
|
||||
|
||||
if(FAILED(res)) return res;
|
||||
|
||||
m_depthStencil = new D3D8Surface(this, std::move(pStencil9));
|
||||
|
||||
*ppZStencilSurface = m_depthStencil.ref();
|
||||
return res;
|
||||
}
|
||||
|
||||
*ppZStencilSurface = m_depthStencil.ref();
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::BeginScene() { return GetD3D9()->BeginScene(); }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::EndScene() { StateChange(); return GetD3D9()->EndScene(); }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::Clear(
|
||||
DWORD Count,
|
||||
const D3DRECT* pRects,
|
||||
DWORD Flags,
|
||||
D3DCOLOR Color,
|
||||
float Z,
|
||||
DWORD Stencil) {
|
||||
StateChange();
|
||||
return GetD3D9()->Clear(Count, pRects, Flags, Color, Z, Stencil);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetTransform(D3DTRANSFORMSTATETYPE State, const D3DMATRIX* pMatrix) {
|
||||
StateChange();
|
||||
return GetD3D9()->SetTransform(d3d9::D3DTRANSFORMSTATETYPE(State), pMatrix);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix) {
|
||||
return GetD3D9()->GetTransform(d3d9::D3DTRANSFORMSTATETYPE(State), pMatrix);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix) {
|
||||
StateChange();
|
||||
return GetD3D9()->MultiplyTransform(d3d9::D3DTRANSFORMSTATETYPE(TransformState), pMatrix);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetViewport(const D3DVIEWPORT8* pViewport) {
|
||||
StateChange();
|
||||
return GetD3D9()->SetViewport(reinterpret_cast<const d3d9::D3DVIEWPORT9*>(pViewport));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetViewport(D3DVIEWPORT8* pViewport) {
|
||||
return GetD3D9()->GetViewport(reinterpret_cast<d3d9::D3DVIEWPORT9*>(pViewport));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetMaterial(const D3DMATERIAL8* pMaterial) {
|
||||
StateChange();
|
||||
return GetD3D9()->SetMaterial((const d3d9::D3DMATERIAL9*)pMaterial);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetMaterial(D3DMATERIAL8* pMaterial) {
|
||||
return GetD3D9()->GetMaterial((d3d9::D3DMATERIAL9*)pMaterial);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetLight(DWORD Index, const D3DLIGHT8* pLight) {
|
||||
StateChange();
|
||||
return GetD3D9()->SetLight(Index, (const d3d9::D3DLIGHT9*)pLight);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetLight(DWORD Index, D3DLIGHT8* pLight) {
|
||||
return GetD3D9()->GetLight(Index, (d3d9::D3DLIGHT9*)pLight);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::LightEnable(DWORD Index, BOOL Enable) {
|
||||
StateChange();
|
||||
return GetD3D9()->LightEnable(Index, Enable);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetLightEnable(DWORD Index, BOOL* pEnable) {
|
||||
return GetD3D9()->GetLightEnable(Index, pEnable);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetClipPlane(DWORD Index, const float* pPlane) {
|
||||
StateChange();
|
||||
return GetD3D9()->SetClipPlane(Index, pPlane);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetClipPlane(DWORD Index, float* pPlane) {
|
||||
return GetD3D9()->GetClipPlane(Index, pPlane);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::CreateStateBlock(
|
||||
D3DSTATEBLOCKTYPE Type,
|
||||
DWORD* pToken) {
|
||||
Com<d3d9::IDirect3DStateBlock9> pStateBlock9;
|
||||
HRESULT res = GetD3D9()->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(Type), &pStateBlock9);
|
||||
|
||||
D3D8StateBlock* pStateBlock = new D3D8StateBlock(this, Type, pStateBlock9.ref());
|
||||
m_stateBlocks.insert(pStateBlock);
|
||||
|
||||
*pToken = DWORD(reinterpret_cast<uintptr_t>(pStateBlock));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::CaptureStateBlock(DWORD Token) {
|
||||
return reinterpret_cast<D3D8StateBlock*>(Token)->Capture();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::ApplyStateBlock(DWORD Token) {
|
||||
StateChange();
|
||||
return reinterpret_cast<D3D8StateBlock*>(Token)->Apply();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::DeleteStateBlock(DWORD Token) {
|
||||
// "Applications cannot delete a device-state block while another is being recorded"
|
||||
if (unlikely(ShouldRecord()))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
D3D8StateBlock* block = reinterpret_cast<D3D8StateBlock*>(Token);
|
||||
m_stateBlocks.erase(block);
|
||||
delete block;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::BeginStateBlock() {
|
||||
if (unlikely(m_recorder != nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
m_recorder = new D3D8StateBlock(this);
|
||||
m_stateBlocks.insert(m_recorder);
|
||||
|
||||
return GetD3D9()->BeginStateBlock();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::EndStateBlock(DWORD* pToken) {
|
||||
if (unlikely(pToken == nullptr || m_recorder == nullptr))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
Com<d3d9::IDirect3DStateBlock9> pStateBlock;
|
||||
HRESULT res = GetD3D9()->EndStateBlock(&pStateBlock);
|
||||
|
||||
m_recorder->SetD3D9(std::move(pStateBlock));
|
||||
|
||||
*pToken = DWORD(reinterpret_cast<uintptr_t>(m_recorder));
|
||||
|
||||
m_recorder = nullptr;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetClipStatus(const D3DCLIPSTATUS8* pClipStatus) {
|
||||
StateChange();
|
||||
return GetD3D9()->SetClipStatus(reinterpret_cast<const d3d9::D3DCLIPSTATUS9*>(pClipStatus));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetClipStatus(D3DCLIPSTATUS8* pClipStatus) {
|
||||
return GetD3D9()->GetClipStatus(reinterpret_cast<d3d9::D3DCLIPSTATUS9*>(pClipStatus));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetTexture(DWORD Stage, IDirect3DBaseTexture8** ppTexture) {
|
||||
InitReturnPtr(ppTexture);
|
||||
|
||||
*ppTexture = m_textures[Stage].ref();
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::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<D3D8Texture2D*>(pTexture);
|
||||
|
||||
if(unlikely(m_textures[Stage].ptr() == tex))
|
||||
return D3D_OK;
|
||||
|
||||
StateChange();
|
||||
|
||||
m_textures[Stage] = tex;
|
||||
|
||||
return GetD3D9()->SetTexture(Stage, D3D8Texture2D::GetD3D9Nullable(tex));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::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 D3D8Device::SetTextureStageState(
|
||||
DWORD Stage,
|
||||
D3DTEXTURESTAGESTATETYPE Type,
|
||||
DWORD Value) {
|
||||
d3d9::D3DSAMPLERSTATETYPE stateType = GetSamplerStateType9(Type);
|
||||
|
||||
StateChange();
|
||||
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 D3D8Device::ValidateDevice(DWORD* pNumPasses) {
|
||||
return GetD3D9()->ValidateDevice(pNumPasses);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetPaletteEntries(UINT PaletteNumber, const PALETTEENTRY* pEntries) {
|
||||
StateChange();
|
||||
return GetD3D9()->SetPaletteEntries(PaletteNumber, pEntries);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries) {
|
||||
return GetD3D9()->GetPaletteEntries(PaletteNumber, pEntries);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetCurrentTexturePalette(UINT PaletteNumber) {
|
||||
StateChange();
|
||||
return GetD3D9()->SetCurrentTexturePalette(PaletteNumber);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetCurrentTexturePalette(UINT* PaletteNumber) {
|
||||
return GetD3D9()->GetCurrentTexturePalette(PaletteNumber);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::DrawPrimitive(
|
||||
D3DPRIMITIVETYPE PrimitiveType,
|
||||
UINT StartVertex,
|
||||
UINT PrimitiveCount) {
|
||||
if (ShouldBatch())
|
||||
return m_batcher->DrawPrimitive(PrimitiveType, StartVertex, PrimitiveCount);
|
||||
return GetD3D9()->DrawPrimitive(d3d9::D3DPRIMITIVETYPE(PrimitiveType), StartVertex, PrimitiveCount);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::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 D3D8Device::DrawPrimitiveUP(
|
||||
D3DPRIMITIVETYPE PrimitiveType,
|
||||
UINT PrimitiveCount,
|
||||
const void* pVertexStreamZeroData,
|
||||
UINT VertexStreamZeroStride) {
|
||||
StateChange();
|
||||
|
||||
// Stream 0 is set to null by this call
|
||||
m_streams[0] = D3D8VBO {nullptr, 0};
|
||||
|
||||
return GetD3D9()->DrawPrimitiveUP(d3d9::D3DPRIMITIVETYPE(PrimitiveType), PrimitiveCount, pVertexStreamZeroData, VertexStreamZeroStride);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::DrawIndexedPrimitiveUP(
|
||||
D3DPRIMITIVETYPE PrimitiveType,
|
||||
UINT MinVertexIndex,
|
||||
UINT NumVertices,
|
||||
UINT PrimitiveCount,
|
||||
const void* pIndexData,
|
||||
D3DFORMAT IndexDataFormat,
|
||||
const void* pVertexStreamZeroData,
|
||||
UINT VertexStreamZeroStride) {
|
||||
StateChange();
|
||||
|
||||
// Stream 0 and the index buffer are set to null by this call
|
||||
m_streams[0] = D3D8VBO {nullptr, 0};
|
||||
m_indices = nullptr;
|
||||
m_baseVertexIndex = 0;
|
||||
|
||||
return GetD3D9()->DrawIndexedPrimitiveUP(
|
||||
d3d9::D3DPRIMITIVETYPE(PrimitiveType),
|
||||
MinVertexIndex,
|
||||
NumVertices,
|
||||
PrimitiveCount,
|
||||
pIndexData,
|
||||
d3d9::D3DFORMAT(IndexDataFormat),
|
||||
pVertexStreamZeroData,
|
||||
VertexStreamZeroStride);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::ProcessVertices(
|
||||
UINT SrcStartIndex,
|
||||
UINT DestIndex,
|
||||
UINT VertexCount,
|
||||
IDirect3DVertexBuffer8* pDestBuffer,
|
||||
DWORD Flags) {
|
||||
if (unlikely(!pDestBuffer))
|
||||
return D3DERR_INVALIDCALL;
|
||||
D3D8VertexBuffer* buffer = static_cast<D3D8VertexBuffer*>(pDestBuffer);
|
||||
return GetD3D9()->ProcessVertices(
|
||||
SrcStartIndex,
|
||||
DestIndex,
|
||||
VertexCount,
|
||||
buffer->GetD3D9(),
|
||||
nullptr,
|
||||
Flags
|
||||
);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetVertexShaderConstant(
|
||||
DWORD StartRegister,
|
||||
const void* pConstantData,
|
||||
DWORD ConstantCount) {
|
||||
StateChange();
|
||||
// ConstantCount is actually the same as Vector4fCount
|
||||
return GetD3D9()->SetVertexShaderConstantF(StartRegister, reinterpret_cast<const float*>(pConstantData), ConstantCount);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetVertexShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount) {
|
||||
return GetD3D9()->GetVertexShaderConstantF(Register, (float*)pConstantData, ConstantCount);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetStreamSource(
|
||||
UINT StreamNumber,
|
||||
IDirect3DVertexBuffer8* pStreamData,
|
||||
UINT Stride) {
|
||||
if (unlikely(StreamNumber >= d8caps::MAX_STREAMS))
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
D3D8VertexBuffer* buffer = static_cast<D3D8VertexBuffer*>(pStreamData);
|
||||
|
||||
if (ShouldBatch())
|
||||
m_batcher->SetStream(StreamNumber, buffer, Stride);
|
||||
|
||||
m_streams[StreamNumber] = D3D8VBO {buffer, Stride};
|
||||
|
||||
// DXVK: Never fails
|
||||
return GetD3D9()->SetStreamSource(StreamNumber, D3D8VertexBuffer::GetD3D9Nullable(buffer), 0, Stride);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::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 D3D8Device::SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) {
|
||||
if (unlikely(ShouldRecord()))
|
||||
return m_recorder->SetIndices(pIndexData, BaseVertexIndex);
|
||||
|
||||
// used by DrawIndexedPrimitive
|
||||
m_baseVertexIndex = static_cast<INT>(BaseVertexIndex);
|
||||
|
||||
D3D8IndexBuffer* buffer = static_cast<D3D8IndexBuffer*>(pIndexData);
|
||||
|
||||
if (ShouldBatch())
|
||||
m_batcher->SetIndices(buffer, m_baseVertexIndex);
|
||||
|
||||
m_indices = buffer;
|
||||
|
||||
// DXVK: Never fails
|
||||
return GetD3D9()->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(buffer));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetIndices(
|
||||
IDirect3DIndexBuffer8** ppIndexData,
|
||||
UINT* pBaseVertexIndex) {
|
||||
InitReturnPtr(ppIndexData);
|
||||
|
||||
*ppIndexData = m_indices.ref();
|
||||
*pBaseVertexIndex = m_baseVertexIndex;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShaderConstant(DWORD Register, void* pConstantData, DWORD ConstantCount) {
|
||||
return GetD3D9()->GetPixelShaderConstantF(Register, (float*)pConstantData, ConstantCount);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetPixelShaderConstant(
|
||||
DWORD StartRegister,
|
||||
const void* pConstantData,
|
||||
DWORD ConstantCount) {
|
||||
StateChange();
|
||||
// ConstantCount is actually the same as Vector4fCount
|
||||
return GetD3D9()->SetPixelShaderConstantF(StartRegister, reinterpret_cast<const float*>(pConstantData), ConstantCount);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::DrawRectPatch(
|
||||
UINT Handle,
|
||||
const float* pNumSegs,
|
||||
const D3DRECTPATCH_INFO* pRectPatchInfo) {
|
||||
return GetD3D9()->DrawRectPatch(Handle, pNumSegs, reinterpret_cast<const d3d9::D3DRECTPATCH_INFO*>(pRectPatchInfo));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::DrawTriPatch(
|
||||
UINT Handle,
|
||||
const float* pNumSegs,
|
||||
const D3DTRIPATCH_INFO* pTriPatchInfo) {
|
||||
return GetD3D9()->DrawTriPatch(Handle, pNumSegs, reinterpret_cast<const d3d9::D3DTRIPATCH_INFO*>(pTriPatchInfo));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::DeletePatch(UINT Handle) {
|
||||
return GetD3D9()->DeletePatch(Handle);
|
||||
}
|
||||
|
||||
|
||||
// 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 D3D8Device::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) {
|
||||
d3d9::D3DRENDERSTATETYPE State9 = (d3d9::D3DRENDERSTATETYPE)State;
|
||||
bool stateChange = true;
|
||||
|
||||
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<D3DLINEPATTERN>(Value);
|
||||
stateChange = false;
|
||||
} break;
|
||||
|
||||
// Not supported by D3D8.
|
||||
case D3DRS_ZVISIBLE:
|
||||
stateChange = false;
|
||||
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<DWORD>(float(Value) * ZBIAS_SCALE);
|
||||
break;
|
||||
|
||||
case D3DRS_SOFTWAREVERTEXPROCESSING:
|
||||
// D3D9 can return D3DERR_INVALIDCALL, but we don't care.
|
||||
if (!(m_behaviorFlags & D3DCREATE_MIXED_VERTEXPROCESSING))
|
||||
return D3D_OK;
|
||||
|
||||
// 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:
|
||||
stateChange = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (stateChange) {
|
||||
DWORD value;
|
||||
GetRenderState(State, &value);
|
||||
if (value != Value)
|
||||
StateChange();
|
||||
}
|
||||
|
||||
return GetD3D9()->SetRenderState(State9, Value);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::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<DWORD>(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 D3D8Device::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, m_d3d8Options);
|
||||
|
||||
// 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(D3D8Device* device, DWORD Handle) {
|
||||
|
||||
Handle = getShaderIndex(Handle);
|
||||
|
||||
if (unlikely(Handle >= device->m_vertexShaders.size())) {
|
||||
Logger::debug(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::debug(str::format("getVertexShaderInfo: Application provided deleted vertex shader ", std::hex, Handle));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &info;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::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;
|
||||
|
||||
StateChange();
|
||||
|
||||
// Cache current shader
|
||||
m_currentVertexShader = Handle;
|
||||
|
||||
GetD3D9()->SetVertexDeclaration(info->pVertexDecl);
|
||||
return GetD3D9()->SetVertexShader(info->pVertexShader);
|
||||
|
||||
} else if (m_currentVertexShader != Handle) {
|
||||
StateChange();
|
||||
|
||||
// Cache current FVF
|
||||
m_currentVertexShader = Handle;
|
||||
|
||||
//GetD3D9()->SetVertexDeclaration(nullptr);
|
||||
GetD3D9()->SetVertexShader(nullptr);
|
||||
return GetD3D9()->SetFVF( Handle );
|
||||
}
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::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 D3D8Device::DeleteVertexShader(DWORD Handle) {
|
||||
if (!isFVF(Handle)) {
|
||||
D3D8VertexShaderInfo* info = getVertexShaderInfo(this, Handle);
|
||||
|
||||
if (!info)
|
||||
return D3DERR_INVALIDCALL;
|
||||
|
||||
if (info->pVertexDecl)
|
||||
info->pVertexDecl->Release();
|
||||
if (info->pVertexShader)
|
||||
info->pVertexShader->Release();
|
||||
|
||||
info->declaration.clear();
|
||||
info->function.clear();
|
||||
}
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::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 D3D8Device::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 D3D8Device::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(D3D8Device* device, DWORD Handle) {
|
||||
Handle = getShaderIndex(Handle);
|
||||
|
||||
if (unlikely(Handle >= device->m_pixelShaders.size())) {
|
||||
Logger::debug(str::format("getPixelShaderPtr: Invalid pixel shader index ", std::hex, Handle));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
d3d9::IDirect3DPixelShader9* pPixelShader = device->m_pixelShaders[Handle];
|
||||
|
||||
if (unlikely(pPixelShader == nullptr)) {
|
||||
Logger::debug(str::format("getPixelShaderPtr: Application provided deleted pixel shader ", std::hex, Handle));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pPixelShader;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::SetPixelShader(DWORD Handle) {
|
||||
if (unlikely(ShouldRecord())) {
|
||||
return m_recorder->SetPixelShader(Handle);
|
||||
}
|
||||
|
||||
if (Handle == DWORD(NULL)) {
|
||||
StateChange();
|
||||
m_currentPixelShader = DWORD(NULL);
|
||||
return GetD3D9()->SetPixelShader(nullptr);
|
||||
}
|
||||
|
||||
d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle);
|
||||
|
||||
if (unlikely(!pPixelShader)) {
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
StateChange();
|
||||
|
||||
// Cache current pixel shader
|
||||
m_currentPixelShader = Handle;
|
||||
|
||||
return GetD3D9()->SetPixelShader(pPixelShader);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::GetPixelShader(DWORD* pHandle) {
|
||||
// Return cached shader
|
||||
*pHandle = m_currentPixelShader;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::DeletePixelShader(DWORD Handle) {
|
||||
d3d9::IDirect3DPixelShader9* pPixelShader = getPixelShaderPtr(this, Handle);
|
||||
|
||||
if (unlikely(!pPixelShader)) {
|
||||
return D3DERR_INVALIDCALL;
|
||||
}
|
||||
|
||||
pPixelShader->Release();
|
||||
|
||||
m_pixelShaders[getShaderIndex(Handle)] = nullptr;
|
||||
|
||||
return D3D_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE D3D8Device::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);
|
||||
}
|
||||
|
||||
}
|
454
src/d3d8/d3d8_device.h
Normal file
454
src/d3d8/d3d8_device.h
Normal file
@ -0,0 +1,454 @@
|
||||
#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);
|
||||
|
||||
m_autoDepthStencil = nullptr;
|
||||
}
|
||||
|
||||
inline void RecreateBackBuffersAndAutoDepthStencil() {
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
}
|
71
src/d3d8/d3d8_device_child.h
Normal file
71
src/d3d8/d3d8_device_child.h
Normal file
@ -0,0 +1,71 @@
|
||||
#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;
|
||||
|
||||
};
|
||||
|
||||
}
|
220
src/d3d8/d3d8_format.h
Normal file
220
src/d3d8/d3d8_format.h
Normal file
@ -0,0 +1,220 @@
|
||||
#pragma once
|
||||
|
||||
#include "d3d8_include.h"
|
||||
|
||||
namespace dxvk {
|
||||
constexpr bool isDXT(D3DFORMAT fmt) {
|
||||
return fmt == D3DFMT_DXT1
|
||||
|| fmt == D3DFMT_DXT2
|
||||
|| fmt == D3DFMT_DXT3
|
||||
|| fmt == D3DFMT_DXT4
|
||||
|| fmt == D3DFMT_DXT5;
|
||||
}
|
||||
|
||||
constexpr bool isDXT(d3d9::D3DFORMAT fmt) {
|
||||
return isDXT(D3DFORMAT(fmt));
|
||||
}
|
||||
|
||||
constexpr bool isUnsupportedSurfaceFormat(D3DFORMAT fmt) {
|
||||
// mirror what dxvk doesn't support in terms of d3d9 surface formats
|
||||
return fmt == D3DFMT_R8G8B8
|
||||
|| fmt == D3DFMT_R3G3B2
|
||||
|| fmt == D3DFMT_A8R3G3B2
|
||||
|| fmt == D3DFMT_A8P8
|
||||
|| fmt == D3DFMT_P8;
|
||||
// not included in the d3d8 spec
|
||||
//|| fmt == D3DFMT_CXV8U8;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
constexpr bool isDepthStencilFormat(D3DFORMAT fmt) {
|
||||
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)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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
200
src/d3d8/d3d8_include.h
Normal 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__) || !defined(_WIN32)
|
||||
#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
|
136
src/d3d8/d3d8_interface.cpp
Normal file
136
src/d3d8/d3d8_interface.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#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,
|
||||
¶ms,
|
||||
&pDevice9
|
||||
);
|
||||
|
||||
if (FAILED(res)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
*ppReturnedDeviceInterface = ref(new D3D8Device(
|
||||
this, std::move(pDevice9),
|
||||
DeviceType, hFocusWindow, BehaviorFlags,
|
||||
pPresentationParameters
|
||||
));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
}
|
163
src/d3d8/d3d8_interface.h
Normal file
163
src/d3d8/d3d8_interface.h
Normal file
@ -0,0 +1,163 @@
|
||||
#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
24
src/d3d8/d3d8_main.cpp
Normal 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
55
src/d3d8/d3d8_options.cpp
Normal 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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
48
src/d3d8/d3d8_options.h
Normal file
48
src/d3d8/d3d8_options.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "d3d8_include.h"
|
||||
#include "../d3d9/d3d9_bridge.h"
|
||||
#include "../util/config/config.h"
|
||||
|
||||
namespace dxvk {
|
||||
struct D3D8Options {
|
||||
|
||||
/// 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;
|
||||
|
||||
/// The Lord of the Rings: The Fellowship of the Ring tries to create a P8 texture
|
||||
/// in D3DPOOL_MANAGED on Nvidia and Intel, which fails, but has a separate code
|
||||
/// path for ATI/AMD that creates it in D3DPOOL_SCRATCH instead, which works.
|
||||
///
|
||||
/// The internal logic determining this path doesn't seem to be d3d-related, but
|
||||
/// the game works universally if we mimic its own ATI/AMD workaround during P8
|
||||
/// texture creation.
|
||||
///
|
||||
/// Early Nvidia GPUs, such as the GeForce 4 generation cards, included and exposed
|
||||
/// P8 texture support. However, it was no longer advertised with cards in the FX series
|
||||
/// and above. Most likely ATI/AMD drivers never supported P8 in the first place.
|
||||
bool placeP8InScratch = false;
|
||||
|
||||
D3D8Options() {}
|
||||
D3D8Options(const Config& config) {
|
||||
auto forceVsDeclStr = config.getOption<std::string>("d3d8.forceVsDecl", "");
|
||||
batching = config.getOption<bool> ("d3d8.batching", batching);
|
||||
placeP8InScratch = config.getOption<bool> ("d3d8.placeP8InScratch", placeP8InScratch);
|
||||
|
||||
parseVsDecl(forceVsDeclStr);
|
||||
}
|
||||
|
||||
void parseVsDecl(const std::string& decl);
|
||||
};
|
||||
}
|
100
src/d3d8/d3d8_resource.h
Normal file
100
src/d3d8/d3d8_resource.h
Normal 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
336
src/d3d8/d3d8_shader.cpp
Normal 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 constexpr 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 constexpr 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
18
src/d3d8/d3d8_shader.h
Normal 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);
|
||||
|
||||
}
|
49
src/d3d8/d3d8_state_block.cpp
Normal file
49
src/d3d8/d3d8_state_block.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#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;
|
||||
}
|
134
src/d3d8/d3d8_state_block.h
Normal file
134
src/d3d8/d3d8_state_block.h
Normal file
@ -0,0 +1,134 @@
|
||||
#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
|
||||
};
|
||||
|
||||
|
||||
}
|
61
src/d3d8/d3d8_subresource.h
Normal file
61
src/d3d8/d3d8_subresource.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
26
src/d3d8/d3d8_surface.cpp
Normal file
26
src/d3d8/d3d8_surface.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
#include "d3d8_surface.h"
|
||||
#include "d3d8_device.h"
|
||||
|
||||
namespace dxvk {
|
||||
|
||||
Com<d3d9::IDirect3DSurface9> D3D8Surface::CreateBlitImage() {
|
||||
d3d9::D3DSURFACE_DESC desc;
|
||||
GetD3D9()->GetDesc(&desc);
|
||||
|
||||
// NOTE: This adds a D3DPOOL_DEFAULT resource to the
|
||||
// device, which counts as losable during device reset
|
||||
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");
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
81
src/d3d8/d3d8_surface.h
Normal file
81
src/d3d8/d3d8_surface.h
Normal 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
42
src/d3d8/d3d8_swapchain.h
Normal 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
233
src/d3d8/d3d8_texture.h
Normal 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
40
src/d3d8/d3d8_volume.h
Normal 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();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
68
src/d3d8/d3d8_wrapped_object.h
Normal file
68
src/d3d8/d3d8_wrapped_object.h
Normal 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;
|
||||
|
||||
};
|
||||
|
||||
}
|
42
src/d3d8/meson.build
Normal file
42
src/d3d8/meson.build
Normal file
@ -0,0 +1,42 @@
|
||||
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(dxvk_name_prefix+'d3d8', d3d8_src, d3d8_res,
|
||||
dependencies : [ lib_d3d9, util_dep, dxso_dep, dxvk_dep ],
|
||||
include_directories : dxvk_include_path,
|
||||
install : true,
|
||||
vs_module_defs : 'd3d8'+def_spec_ext,
|
||||
link_args : d3d8_ld_args,
|
||||
link_depends : [ d3d8_link_depends ],
|
||||
kwargs : dxvk_so_version,
|
||||
)
|
||||
|
||||
d3d8_dep = declare_dependency(
|
||||
link_with : [ d3d8_dll ],
|
||||
include_directories : [ dxvk_include_path ],
|
||||
)
|
||||
|
||||
if platform != 'windows'
|
||||
pkg.generate(d3d8_dll,
|
||||
filebase: dxvk_pkg_prefix + 'd3d8',
|
||||
subdirs: 'dxvk',
|
||||
)
|
||||
endif
|
31
src/d3d8/version.rc
Normal file
31
src/d3d8/version.rc
Normal 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
|
@ -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
|
||||
|
@ -698,10 +698,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 *
|
||||
@ -732,10 +728,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$)", {{
|
||||
@ -996,6 +988,133 @@ namespace dxvk {
|
||||
{ R"(\\P3R\.exe$)", {{
|
||||
{ "dxgi.syncInterval", "1" },
|
||||
}} },
|
||||
|
||||
/**********************************************/
|
||||
/* 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" },
|
||||
}} },
|
||||
/* Red Faction *
|
||||
* Fixes crashing when starting a new game */
|
||||
{ R"(\\RF\.exe$)", {{
|
||||
{ "d3d9.allowDirectBufferMapping", "False" },
|
||||
}} },
|
||||
/* Commandos 3 *
|
||||
* The game doesn't use NOOVERWRITE properly *
|
||||
* and reads from actively modified buffers, *
|
||||
* which causes graphical glitches at times */
|
||||
{ R"(\\Commandos3\.exe$)", {{
|
||||
{ "d3d9.allowDirectBufferMapping", "False" },
|
||||
}} },
|
||||
/* Motor City Online */
|
||||
{ R"(\\MCity_d\.exe$)", {{
|
||||
{ "d3d9.cachedDynamicBuffers", "True" },
|
||||
{ "d3d8.batching", "True" },
|
||||
}} },
|
||||
/* Railroad Tycoon 3 */
|
||||
{ R"(\\RT3\.exe$)", {{
|
||||
{ "d3d9.maxFrameRate", "60" },
|
||||
}} },
|
||||
/* 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" },
|
||||
}} },
|
||||
/* Need for Speed III: Hot Pursuit *
|
||||
(with the "Modern Patch") */
|
||||
{ R"(\\nfs3\.exe$)", {{
|
||||
{ "d3d9.enableDialogMode", "True" },
|
||||
{ "d3d9.cachedDynamicBuffers", "True" },
|
||||
{ "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.batching", "True" },
|
||||
}} },
|
||||
/* Need for Speed: Hot Pursuit 2 */
|
||||
{ R"(\\NFSHP2\.exe$)", {{
|
||||
{ "d3d9.cachedDynamicBuffers", "True" },
|
||||
}} },
|
||||
/* Project I.G.I. 2: Covert Strike */
|
||||
{ R"(\\igi2\.exe$)", {{
|
||||
{ "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" },
|
||||
}} },
|
||||
/* Scrapland (Remastered) */
|
||||
{ R"(\\Scrap\.exe$)", {{
|
||||
{ "d3d9.deferSurfaceCreation", "True" },
|
||||
}} },
|
||||
/* V-Rally 3 */
|
||||
{ R"(\\VRally3(Demo)?\.exe$)", {{
|
||||
{ "d3d9.maxFrameRate", "60" },
|
||||
}} },
|
||||
/* Soldiers: Heroes Of World War II *
|
||||
* Fills up all available memory and hangs *
|
||||
* while loading the main menu otherwise */
|
||||
{ R"(\\Soldiers\.exe$)", {{
|
||||
{ "d3d9.memoryTrackTest", "True" },
|
||||
{ "d3d9.maxAvailableMemory", "512" },
|
||||
}} },
|
||||
/* Cossacks II: Napoleonic Wars & *
|
||||
* Battle for Europe */
|
||||
{ R"(\\Cossacks II.*\\engine\.exe$)", {{
|
||||
{ "d3d9.maxFrameRate", "60" },
|
||||
}} },
|
||||
/* Alexander */
|
||||
{ R"(\\Alexander\\Data\\engine\.exe$)", {{
|
||||
{ "d3d9.maxFrameRate", "60" },
|
||||
}} },
|
||||
/* 3DMark2001 (SE) *
|
||||
* Fixes a drastic performance drop in the *
|
||||
* "Car Chase - High Detail" benchmark */
|
||||
{ R"(\\3DMark2001(SE)?\.exe$)", {{
|
||||
{ "d3d9.allowDirectBufferMapping", "False" },
|
||||
}} },
|
||||
/* Delta Force: Black Hawk Down */
|
||||
{ R"(\\dfbhd\.exe$)", {{
|
||||
{ "d3d9.cachedDynamicBuffers", "True" },
|
||||
}} },
|
||||
/* X2: The Threat */
|
||||
{ R"(\\X2\.exe$)", {{
|
||||
{ "d3d9.cachedDynamicBuffers", "True" },
|
||||
}} },
|
||||
/* The Lord of the Rings: *
|
||||
* The Fellowship of the Ring */
|
||||
{ R"(\\Fellowship\.exe$)", {{
|
||||
{ "d3d9.maxFrameRate", "60" },
|
||||
{ "d3d8.placeP8InScratch", "True" },
|
||||
}} },
|
||||
}};
|
||||
|
||||
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user