| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| LIBRARY D3D8.DLL | ||
| EXPORTS | ||
| ValidatePixelShader @ 2 | ||
| ValidateVertexShader @ 3 | ||
| DebugSetMute @ 4 | ||
| Direct3DCreate8 @ 5 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| global: | ||
| ValidatePixelShader; | ||
| ValidateVertexShader; | ||
| DebugSetMute; | ||
| Direct3DCreate8; | ||
|
|
||
| local: | ||
| *; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,243 @@ | ||
| #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) { | ||
| if (unlikely(pDesc == nullptr)) | ||
| return D3DERR_INVALIDCALL; | ||
|
|
||
| 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; | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| #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) { | ||
| m_options = this->GetParent()->GetOptions(); | ||
| } | ||
|
|
||
| HRESULT STDMETHODCALLTYPE Lock( | ||
| UINT OffsetToLock, | ||
| UINT SizeToLock, | ||
| BYTE** ppbData, | ||
| DWORD Flags) { | ||
|
|
||
| if (m_options->forceLegacyDiscard && | ||
| (Flags & D3DLOCK_DISCARD) && | ||
| !((m_usage & D3DUSAGE_DYNAMIC) && | ||
| (m_usage & D3DUSAGE_WRITEONLY))) | ||
| Flags &= ~D3DLOCK_DISCARD; | ||
|
|
||
| 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: | ||
| const D3D8Options* m_options; | ||
| const D3DPOOL m_pool; | ||
| 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) { | ||
| return GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DVERTEXBUFFER_DESC*>(pDesc)); | ||
| } | ||
|
|
||
| }; | ||
|
|
||
| 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 { | ||
| return GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DINDEXBUFFER_DESC*>(pDesc)); | ||
| } | ||
|
|
||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| #pragma once | ||
|
|
||
| namespace dxvk::d8caps { | ||
|
|
||
| inline constexpr uint32_t MAX_TEXTURE_STAGES = 8; | ||
| inline constexpr uint32_t MAX_STREAMS = 16; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,180 @@ | ||
| #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_BLENDFACTOR | ||
| & ~D3DPBLENDCAPS_INVSRCCOLOR2 | ||
| & ~D3DPBLENDCAPS_SRCCOLOR2; | ||
|
|
||
| pCaps8->DestBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR | ||
| & ~D3DPBLENDCAPS_INVSRCCOLOR2 | ||
| & ~D3DPBLENDCAPS_SRCCOLOR2; | ||
|
|
||
| pCaps8->LineCaps &= ~D3DLINECAPS_ANTIALIAS; | ||
|
|
||
| pCaps8->StencilCaps &= ~D3DSTENCILCAPS_TWOSIDED; | ||
|
|
||
| pCaps8->VertexProcessingCaps &= ~D3DVTXPCAPS_TEXGEN_SPHEREMAP; | ||
| } | ||
|
|
||
| // (9<-8) D3DD3DPRESENT_PARAMETERS: Returns D3D9's params given an input for D3D8 | ||
| inline d3d9::D3DPRESENT_PARAMETERS ConvertPresentParameters9(D3DPRESENT_PARAMETERS* pParams) { | ||
| // A 0 back buffer count needs to be corrected and made visible to the D3D8 application as well | ||
| pParams->BackBufferCount = std::max(pParams->BackBufferCount, 1u); | ||
|
|
||
| if (pParams->BackBufferFormat == D3DFMT_UNKNOWN) | ||
| pParams->BackBufferFormat = D3DFMT_X8R8G8B8; | ||
|
|
||
| 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; | ||
|
|
||
| // D3DPRESENT_RATE_UNLIMITED is unsupported, use D3DPRESENT_RATE_DEFAULT (or 0) | ||
| if (unlikely(pParams->FullScreen_RefreshRateInHz == D3DPRESENT_RATE_UNLIMITED)) { | ||
| params.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; | ||
| } else { | ||
| 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 -1u | ||
| 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_MAXMIPLEVEL; | ||
| case D3DTSS_MAXANISOTROPY: return d3d9::D3DSAMP_MAXANISOTROPY; | ||
| // 25: | ||
| case D3DTSS_ADDRESSW: return d3d9::D3DSAMP_ADDRESSW; | ||
| default: return d3d9::D3DSAMPLERSTATETYPE(-1u); | ||
| } | ||
| } | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
|
|
||
| }; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| #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/sync/sync_recursive.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 | ||
|
|
||
| #ifndef D3DPRESENT_RATE_UNLIMITED | ||
| #define D3DPRESENT_RATE_UNLIMITED 0x7FFFFFFF | ||
| #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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| #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_bridge->SetD3D8CompatibilityMode(true); | ||
|
|
||
| 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) { | ||
| if (unlikely(pIdentifier == nullptr)) | ||
| return D3DERR_INVALIDCALL; | ||
|
|
||
| // 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); | ||
|
|
||
| if (likely(SUCCEEDED(res))) { | ||
| 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) { | ||
| InitReturnPtr(ppReturnedDeviceInterface); | ||
|
|
||
| if (unlikely(pPresentationParameters == nullptr || | ||
| ppReturnedDeviceInterface == nullptr)) | ||
| return D3DERR_INVALIDCALL; | ||
|
|
||
| // D3DSWAPEFFECT_COPY can not be used with more than one back buffer. | ||
| // This is also technically true for D3DSWAPEFFECT_COPY_VSYNC, however | ||
| // RC Cars depends on it not being rejected. | ||
| if (unlikely(pPresentationParameters->SwapEffect == D3DSWAPEFFECT_COPY | ||
| && pPresentationParameters->BackBufferCount > 1)) | ||
| return D3DERR_INVALIDCALL; | ||
|
|
||
| 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 (likely(SUCCEEDED(res))) | ||
| *ppReturnedDeviceInterface = ref(new D3D8Device( | ||
| this, std::move(pDevice9), | ||
| DeviceType, hFocusWindow, BehaviorFlags, | ||
| pPresentationParameters | ||
| )); | ||
|
|
||
| return res; | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| #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) { | ||
| // Ignore the bWindowed parameter when querying D3D9. D3D8 does | ||
| // identical validations between windowed and fullscreen modes, adhering | ||
| // to the stricter fullscreen adapter and back buffer format validations. | ||
| return m_d3d9->CheckDeviceType( | ||
| Adapter, | ||
| (d3d9::D3DDEVTYPE)DevType, | ||
| (d3d9::D3DFORMAT)AdapterFormat, | ||
| (d3d9::D3DFORMAT)BackBufferFormat, | ||
| FALSE | ||
| ); | ||
| } | ||
|
|
||
| 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) { | ||
| if (unlikely(pCaps == nullptr)) | ||
| return D3DERR_INVALIDCALL; | ||
|
|
||
| d3d9::D3DCAPS9 caps9; | ||
| HRESULT res = m_d3d9->GetDeviceCaps(Adapter, (d3d9::D3DDEVTYPE)DeviceType, &caps9); | ||
|
|
||
| if (likely(SUCCEEDED(res))) | ||
| 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; | ||
|
|
||
| Com<d3d9::IDirect3D9> m_d3d9; | ||
| Com<IDxvkD3D8InterfaceBridge> m_bridge; | ||
| D3D8Options m_d3d8Options; | ||
| }; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| #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 HRESULT __stdcall ValidatePixelShader( | ||
| const DWORD* pPixelShader, | ||
| const D3DCAPS8* pCaps, | ||
| BOOL errorReturn, | ||
| char** pErrorString) { | ||
| dxvk::Logger::warn("D3D8: ValidatePixelShader: Stub"); | ||
|
|
||
| if (unlikely(pPixelShader == nullptr)) | ||
| return E_FAIL; | ||
|
|
||
| if (errorReturn && pErrorString != nullptr) { | ||
| const char* errorMessage = ""; | ||
| *pErrorString = (char *) errorMessage; | ||
| } | ||
|
|
||
| return S_OK; | ||
| } | ||
|
|
||
| DLLEXPORT HRESULT __stdcall ValidateVertexShader( | ||
| const DWORD* pVertexShader, | ||
| const DWORD* pVertexDecl, | ||
| const D3DCAPS8* pCaps, | ||
| BOOL errorReturn, | ||
| char** pErrorString) { | ||
| dxvk::Logger::warn("D3D8: ValidateVertexShader: Stub"); | ||
|
|
||
| if (unlikely(pVertexShader == nullptr)) | ||
| return E_FAIL; | ||
|
|
||
| if (errorReturn && pErrorString != nullptr) { | ||
| const char* errorMessage = ""; | ||
| *pErrorString = (char *) errorMessage; | ||
| } | ||
|
|
||
| return S_OK; | ||
| } | ||
|
|
||
| DLLEXPORT void __stdcall DebugSetMute() { | ||
| dxvk::Logger::debug("D3D8: DebugSetMute: Stub"); | ||
| } | ||
|
|
||
| DLLEXPORT IDirect3D8* __stdcall Direct3DCreate8(UINT nSDKVersion) { | ||
| IDirect3D8* pDirect3D = nullptr; | ||
| dxvk::CreateD3D8(&pDirect3D); | ||
|
|
||
| return pDirect3D; | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| #include "d3d8_device.h" | ||
|
|
||
| namespace dxvk { | ||
|
|
||
| D3D8Multithread::D3D8Multithread( | ||
| BOOL Protected) | ||
| : m_protected( Protected ) { } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| #pragma once | ||
|
|
||
| #include "d3d8_include.h" | ||
|
|
||
| namespace dxvk { | ||
|
|
||
| /** | ||
| * \brief Device lock | ||
| * | ||
| * Lightweight RAII wrapper that implements | ||
| * a subset of the functionality provided by | ||
| * \c std::unique_lock, with the goal of being | ||
| * cheaper to construct and destroy. | ||
| */ | ||
| class D3D8DeviceLock { | ||
|
|
||
| public: | ||
|
|
||
| D3D8DeviceLock() | ||
| : m_mutex(nullptr) { } | ||
|
|
||
| D3D8DeviceLock(sync::RecursiveSpinlock& mutex) | ||
| : m_mutex(&mutex) { | ||
| mutex.lock(); | ||
| } | ||
|
|
||
| D3D8DeviceLock(D3D8DeviceLock&& other) | ||
| : m_mutex(other.m_mutex) { | ||
| other.m_mutex = nullptr; | ||
| } | ||
|
|
||
| D3D8DeviceLock& operator = (D3D8DeviceLock&& other) { | ||
| if (m_mutex) | ||
| m_mutex->unlock(); | ||
|
|
||
| m_mutex = other.m_mutex; | ||
| other.m_mutex = nullptr; | ||
| return *this; | ||
| } | ||
|
|
||
| ~D3D8DeviceLock() { | ||
| if (m_mutex != nullptr) | ||
| m_mutex->unlock(); | ||
| } | ||
|
|
||
| private: | ||
|
|
||
| sync::RecursiveSpinlock* m_mutex; | ||
|
|
||
| }; | ||
|
|
||
|
|
||
| /** | ||
| * \brief D3D8 context lock | ||
| */ | ||
| class D3D8Multithread { | ||
|
|
||
| public: | ||
|
|
||
| D3D8Multithread( | ||
| BOOL Protected); | ||
|
|
||
| D3D8DeviceLock AcquireLock() { | ||
| return m_protected | ||
| ? D3D8DeviceLock(m_mutex) | ||
| : D3D8DeviceLock(); | ||
| } | ||
|
|
||
| private: | ||
|
|
||
| BOOL m_protected; | ||
|
|
||
| sync::RecursiveSpinlock m_mutex; | ||
|
|
||
| }; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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)); | ||
| } | ||
|
|
||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| #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; | ||
|
|
||
| /// Rayman 3 relies on D3DLOCK_DISCARD being ignored for everything except D3DUSAGE_DYNAMIC + | ||
| /// D3DUSAGE_WRITEONLY buffers, however this approach incurs a performance penalty. | ||
| /// | ||
| /// Some titles might abuse this early D3D8 quirk, however at some point in its history | ||
| /// it was brought in line with standard D3D9 behavior. | ||
| bool forceLegacyDiscard = 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); | ||
| forceLegacyDiscard = config.getOption<bool> ("d3d8.forceLegacyDiscard", forceLegacyDiscard); | ||
|
|
||
| parseVsDecl(forceVsDeclStr); | ||
| } | ||
|
|
||
| void parseVsDecl(const std::string& decl); | ||
| }; | ||
| } |