Play-/Source/ui_win32/GSH_Direct3D9.cpp
Jean-Philip Desjardins 8a9b1ce6b9 Cleanup.
2017-09-13 16:58:35 -04:00

1741 lines
47 KiB
C++

#include "GSH_Direct3D9.h"
#include "../Log.h"
#include "../gs/GsPixelFormats.h"
#include "D3D9TextureUtils.h"
#include "math/Matrix4.h"
#define D3D_DEBUG_INFO
//#define _WIREFRAME
struct CUSTOMVERTEX
{
float x, y, z;
DWORD color;
float s, t, q;
};
struct PRESENTVERTEX
{
float x, y, z;
float u, v;
};
#define PRESENTFVF (D3DFVF_XYZ | D3DFVF_TEX1)
static const PRESENTVERTEX g_presentVertices[] =
{
// X Y Z U V
{ -1, -1, 0, 0, 1 },
{ 1, -1, 0, 1, 1 },
{ -1, 1, 0, 0, 0 },
{ 1, 1, 0, 1, 0 }
};
static uint8 MulBy2Clamp(uint8 value)
{
if(value >= 0x80) return 0xFF;
return value << 1;
}
CGSH_Direct3D9::CGSH_Direct3D9(Framework::Win32::CWindow* outputWindow)
: m_outputWnd(outputWindow)
{
memset(&m_renderState, 0, sizeof(m_renderState));
m_primitiveMode <<= 0;
}
Framework::CBitmap CGSH_Direct3D9::GetFramebuffer(uint64 frameReg)
{
Framework::CBitmap result;
m_mailBox.SendCall([&] () { result = GetFramebufferImpl(frameReg); }, true );
return result;
}
Framework::CBitmap CGSH_Direct3D9::GetTexture(uint64 tex0Reg, uint32 maxMip, uint64 miptbp1Reg, uint64 miptbp2Reg, uint32 mipLevel)
{
Framework::CBitmap result;
m_mailBox.SendCall([&] () { result = GetTextureImpl(tex0Reg, maxMip, miptbp1Reg, miptbp2Reg, mipLevel); }, true);
return result;
}
const CGSH_Direct3D9::VERTEX* CGSH_Direct3D9::GetInputVertices() const
{
return m_vtxBuffer;
}
CGSHandler::FactoryFunction CGSH_Direct3D9::GetFactoryFunction(Framework::Win32::CWindow* outputWnd)
{
return [=] () { return new CGSH_Direct3D9(outputWnd); };
}
void CGSH_Direct3D9::ProcessHostToLocalTransfer()
{
auto bltBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
uint32 transferAddress = bltBuf.GetDstPtr();
if(m_trxCtx.nDirty)
{
//FlushVertexBuffer();
m_renderState.isValid = false;
auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
//Find the pages that are touched by this transfer
auto transferPageSize = CGsPixelFormats::GetPsmPageSize(bltBuf.nDstPsm);
uint32 pageCountX = (bltBuf.GetDstWidth() + transferPageSize.first - 1) / transferPageSize.first;
uint32 pageCountY = (trxReg.nRRH + transferPageSize.second - 1) / transferPageSize.second;
uint32 pageCount = pageCountX * pageCountY;
uint32 transferSize = pageCount * CGsPixelFormats::PAGESIZE;
uint32 transferOffset = (trxPos.nDSAY / transferPageSize.second) * pageCountX * CGsPixelFormats::PAGESIZE;
m_textureCache.InvalidateRange(transferAddress + transferOffset, transferSize);
#if 0
bool isUpperByteTransfer = (bltBuf.nDstPsm == PSMT8H) || (bltBuf.nDstPsm == PSMT4HL) || (bltBuf.nDstPsm == PSMT4HH);
for(const auto& framebuffer : m_framebuffers)
{
if((framebuffer->m_psm == PSMCT24) && isUpperByteTransfer) continue;
framebuffer->m_cachedArea.Invalidate(transferAddress + transferOffset, transferSize);
}
#endif
}
}
void CGSH_Direct3D9::ProcessLocalToHostTransfer()
{
//This is constrained to work only with ps2autotest, will be unconstrained later
auto bltBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
if(bltBuf.nSrcPsm != PSMCT32) return;
uint32 transferAddress = bltBuf.GetSrcPtr();
if(transferAddress != 0) return;
if((trxReg.nRRW != 32) || (trxReg.nRRH != 32)) return;
if((trxPos.nSSAX != 0) || (trxPos.nSSAY != 0)) return;
auto framebufferIterator = std::find_if(m_framebuffers.begin(), m_framebuffers.end(),
[] (const FramebufferPtr& framebuffer)
{
return (framebuffer->m_psm == PSMCT32) && (framebuffer->m_basePtr == 0);
}
);
if(framebufferIterator == std::end(m_framebuffers)) return;
const auto& framebuffer = (*framebufferIterator);
m_renderState.isValid = false;
auto renderTarget = framebuffer->m_renderTarget;
SurfacePtr offscreenSurface, renderTargetSurface;
HRESULT result = S_OK;
result = renderTarget->GetSurfaceLevel(0, &renderTargetSurface);
assert(SUCCEEDED(result));
result = m_device->CreateOffscreenPlainSurface(framebuffer->m_width, framebuffer->m_height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &offscreenSurface, nullptr);
assert(SUCCEEDED(result));
result = m_device->GetRenderTargetData(renderTargetSurface, offscreenSurface);
assert(SUCCEEDED(result));
D3DLOCKED_RECT lockedRect = {};
result = offscreenSurface->LockRect(&lockedRect, nullptr, D3DLOCK_READONLY);
assert(SUCCEEDED(result));
auto pixels = reinterpret_cast<uint32*>(lockedRect.pBits);
//Write back to RAM
{
CGsPixelFormats::CPixelIndexorPSMCT32 indexor(m_pRAM, bltBuf.GetSrcPtr(), bltBuf.nSrcWidth);
for(uint32 y = trxPos.nSSAY; y < (trxPos.nSSAY + trxReg.nRRH); y++)
{
for(uint32 x = trxPos.nSSAX; x < (trxPos.nSSAX + trxReg.nRRH); x++)
{
uint32 pixel = pixels[x + (y * lockedRect.Pitch / 4)];
indexor.SetPixel(x, y, pixel);
}
}
}
result = offscreenSurface->UnlockRect();
assert(SUCCEEDED(result));
}
void CGSH_Direct3D9::ProcessLocalToLocalTransfer()
{
}
void CGSH_Direct3D9::ProcessClutTransfer(uint32, uint32)
{
m_renderState.isValid = false;
}
void CGSH_Direct3D9::ReadFramebuffer(uint32, uint32, void*)
{
}
bool CGSH_Direct3D9::GetDepthTestingEnabled() const
{
return m_depthTestingEnabled;
}
void CGSH_Direct3D9::SetDepthTestingEnabled(bool depthTestingEnabled)
{
m_depthTestingEnabled = depthTestingEnabled;
m_renderState.isValid = false;
}
bool CGSH_Direct3D9::GetAlphaBlendingEnabled() const
{
return m_alphaBlendingEnabled;
}
void CGSH_Direct3D9::SetAlphaBlendingEnabled(bool alphaBlendingEnabled)
{
m_alphaBlendingEnabled = alphaBlendingEnabled;
m_renderState.isValid = false;
}
bool CGSH_Direct3D9::GetAlphaTestingEnabled() const
{
return m_alphaTestingEnabled;
}
void CGSH_Direct3D9::SetAlphaTestingEnabled(bool alphaTestingEnabled)
{
m_alphaTestingEnabled = alphaTestingEnabled;
m_renderState.isValid = false;
}
void CGSH_Direct3D9::InitializeImpl()
{
m_d3d = Direct3DPtr(Direct3DCreate9(D3D_SDK_VERSION));
CreateDevice();
m_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
PresentBackbuffer();
m_cvtBuffer = new uint8[CVTBUFFERSIZE];
SetupTextureUpdaters();
}
void CGSH_Direct3D9::ResetImpl()
{
memset(&m_vtxBuffer, 0, sizeof(m_vtxBuffer));
m_framebuffers.clear();
m_depthbuffers.clear();
m_textureCache.Flush();
m_renderState.isValid = false;
CGSHandler::ResetImpl();
}
void CGSH_Direct3D9::ReleaseImpl()
{
delete [] m_cvtBuffer;
}
CGSH_Direct3D9::FramebufferPtr CGSH_Direct3D9::FindFramebuffer(uint64 frameReg) const
{
auto frame = make_convertible<FRAME>(frameReg);
auto framebufferIterator = std::find_if(std::begin(m_framebuffers), std::end(m_framebuffers),
[&] (const FramebufferPtr& framebuffer)
{
return (framebuffer->m_basePtr == frame.GetBasePtr()) && (framebuffer->m_width == frame.GetWidth());
}
);
return (framebufferIterator != std::end(m_framebuffers)) ? *(framebufferIterator) : FramebufferPtr();
}
Framework::CBitmap CGSH_Direct3D9::GetFramebufferImpl(uint64 frameReg)
{
auto framebuffer = FindFramebuffer(frameReg);
if(!framebuffer)
{
return Framework::CBitmap();
}
else
{
return CreateBitmapFromRenderTarget(framebuffer->m_renderTarget,
framebuffer->m_width, framebuffer->m_height, framebuffer->m_width, framebuffer->m_height);
}
}
CGSH_Direct3D9::DepthbufferPtr CGSH_Direct3D9::FindDepthbuffer(uint64 zbufReg, uint64 frameReg) const
{
auto zbuf = make_convertible<ZBUF>(zbufReg);
auto frame = make_convertible<FRAME>(frameReg);
auto depthbufferIterator = std::find_if(std::begin(m_depthbuffers), std::end(m_depthbuffers),
[&] (const DepthbufferPtr& depthBuffer)
{
return (depthBuffer->m_basePtr == zbuf.GetBasePtr()) && (depthBuffer->m_width == frame.GetWidth());
}
);
return (depthbufferIterator != std::end(m_depthbuffers)) ? *(depthbufferIterator) : DepthbufferPtr();
}
Framework::CBitmap CGSH_Direct3D9::GetTextureImpl(uint64 tex0Reg, uint32 maxMip, uint64 miptbp1Reg, uint64 miptbp2Reg, uint32 mipLevel)
{
auto tex0 = make_convertible<TEX0>(tex0Reg);
auto miptbp1 = make_convertible<MIPTBP1>(miptbp1Reg);
auto miptbp2 = make_convertible<MIPTBP2>(miptbp2Reg);
auto texInfo = LoadTexture(tex0, maxMip, miptbp1, miptbp2);
if(texInfo.isRenderTarget)
{
return CreateBitmapFromRenderTarget(texInfo.texture,
texInfo.renderTargetWidth, texInfo.renderTargetHeight, tex0.GetWidth(), tex0.GetHeight());
}
else
{
return CreateBitmapFromTexture(texInfo.texture, tex0.GetWidth(), tex0.GetHeight(), tex0.nPsm, mipLevel);
}
}
Framework::CBitmap CGSH_Direct3D9::CreateBitmapFromTexture(const TexturePtr& texture, uint32 width, uint32 height, uint8 format, uint32 mipLevel)
{
width = std::max<uint32>(width >> mipLevel, 1);
height = std::max<uint32>(height >> mipLevel, 1);
uint32 bitsPerPixel =
[format]()
{
switch(format)
{
case PSMCT16:
return 16;
case PSMT4:
case PSMT8:
case PSMT8H:
case PSMT4HL:
case PSMT4HH:
return 8;
default:
assert(false);
case PSMCT32:
case PSMCT24:
return 32;
}
}();
auto bitmap = Framework::CBitmap(width, height, bitsPerPixel);
switch(bitsPerPixel)
{
case 8:
CopyTextureToBitmap<uint8>(bitmap, texture, mipLevel);
break;
case 16:
CopyTextureToBitmap<uint16>(bitmap, texture, mipLevel);
break;
case 32:
CopyTextureToBitmap<uint32>(bitmap, texture, mipLevel);
break;
default:
assert(false);
break;
}
return bitmap;
}
Framework::CBitmap CGSH_Direct3D9::CreateBitmapFromRenderTarget(const TexturePtr& renderTarget, uint32 renderTargetWidth, uint32 renderTargetHeight, uint32 width, uint32 height)
{
HRESULT result = S_OK;
SurfacePtr offscreenSurface, renderTargetSurface;
result = renderTarget->GetSurfaceLevel(0, &renderTargetSurface);
assert(SUCCEEDED(result));
result = m_device->CreateOffscreenPlainSurface(renderTargetWidth, renderTargetHeight, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &offscreenSurface, nullptr);
assert(SUCCEEDED(result));
result = m_device->GetRenderTargetData(renderTargetSurface, offscreenSurface);
assert(SUCCEEDED(result));
auto bitmap = Framework::CBitmap(width, height, 32);
D3DLOCKED_RECT lockedRect = {};
result = offscreenSurface->LockRect(&lockedRect, nullptr, D3DLOCK_READONLY);
assert(SUCCEEDED(result));
auto srcPtr = reinterpret_cast<uint8*>(lockedRect.pBits);
auto dstPtr = reinterpret_cast<uint8*>(bitmap.GetPixels());
uint32 copyWidth = std::min<uint32>(renderTargetWidth, width);
uint32 copyHeight = std::min<uint32>(renderTargetHeight, height);
for(unsigned int y = 0; y < copyHeight; y++)
{
memcpy(dstPtr, srcPtr, copyWidth * 4);
dstPtr += bitmap.GetPitch();
srcPtr += lockedRect.Pitch;
}
result = offscreenSurface->UnlockRect();
assert(SUCCEEDED(result));
return bitmap;
}
void CGSH_Direct3D9::BeginScene()
{
if(!m_sceneBegun)
{
#ifdef _WIREFRAME
m_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 0.0f, 0);
#endif
HRESULT result = m_device->BeginScene();
assert(result == S_OK);
m_sceneBegun = true;
}
}
void CGSH_Direct3D9::EndScene()
{
if(m_sceneBegun)
{
HRESULT result = m_device->EndScene();
assert(result == S_OK);
m_sceneBegun = false;
}
}
void CGSH_Direct3D9::PresentBackbuffer()
{
if(!m_device.IsEmpty())
{
HRESULT result = S_OK;
EndScene();
if(TestDevice())
{
result = m_device->Present(NULL, NULL, NULL, NULL);
assert(SUCCEEDED(result));
}
BeginScene();
}
}
unsigned int CGSH_Direct3D9::GetCurrentReadCircuit()
{
//assert((m_nPMODE & 0x3) != 0x03);
if(m_nPMODE & 0x1) return 0;
if(m_nPMODE & 0x2) return 1;
//Getting here is bad
return 0;
}
void CGSH_Direct3D9::FlipImpl()
{
DrawActiveFramebuffer();
PresentBackbuffer();
CGSHandler::FlipImpl();
}
void CGSH_Direct3D9::DrawActiveFramebuffer()
{
HRESULT result = S_OK;
m_renderState.isValid = false;
DISPLAY d;
DISPFB fb;
{
std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
unsigned int readCircuit = GetCurrentReadCircuit();
switch(readCircuit)
{
case 0:
d <<= m_nDISPLAY1.value.q;
fb <<= m_nDISPFB1.value.q;
break;
case 1:
d <<= m_nDISPLAY2.value.q;
fb <<= m_nDISPFB2.value.q;
break;
}
}
unsigned int dispWidth = (d.nW + 1) / (d.nMagX + 1);
unsigned int dispHeight = (d.nH + 1);
bool halfHeight = GetCrtIsInterlaced() && GetCrtIsFrameMode();
if(halfHeight) dispHeight /= 2;
FramebufferPtr framebuffer;
for(const auto& candidateFramebuffer : m_framebuffers)
{
if(
(candidateFramebuffer->m_basePtr == fb.GetBufPtr()) &&
//(GetFramebufferBitDepth(candidateFramebuffer->m_psm) == GetFramebufferBitDepth(fb.nPSM)) &&
(candidateFramebuffer->m_width == fb.GetBufWidth())
)
{
//We have a winner
framebuffer = candidateFramebuffer;
break;
}
}
//TODO: Create new framebuffer if none is found
//TODO: Commit dirty framebuffer pages to video memory
//TODO: Resolve multisample
{
SurfacePtr backbuffer;
result = m_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);
assert(SUCCEEDED(result));
result = m_device->SetRenderTarget(0, backbuffer);
assert(SUCCEEDED(result));
}
m_device->SetVertexDeclaration(nullptr);
m_device->SetVertexShader(nullptr);
m_device->SetPixelShader(nullptr);
D3DVIEWPORT9 viewport = {};
viewport.X = 0;
viewport.Y = 0;
viewport.Width = m_presentationParams.windowWidth;
viewport.Height = m_presentationParams.windowHeight;
result = m_device->SetViewport(&viewport);
assert(SUCCEEDED(result));
result = m_device->Clear(1, nullptr, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 0, 0);
assert(SUCCEEDED(result));
auto identityMatrix = CMatrix4::MakeIdentity();
m_device->SetTransform(D3DTS_PROJECTION, reinterpret_cast<D3DMATRIX*>(&identityMatrix));
m_device->SetTransform(D3DTS_VIEW, reinterpret_cast<D3DMATRIX*>(&identityMatrix));
m_device->SetTransform(D3DTS_WORLD, reinterpret_cast<D3DMATRIX*>(&identityMatrix));
if(framebuffer)
{
float u1 = static_cast<float>(dispWidth) / static_cast<float>(framebuffer->m_width);
float v1 = static_cast<float>(dispHeight) / static_cast<float>(framebuffer->m_height);
auto textureMatrix = CMatrix4::MakeScale(u1, v1, 1);
m_device->SetTransform(D3DTS_TEXTURE0, reinterpret_cast<D3DMATRIX*>(&textureMatrix));
result = m_device->SetStreamSource(0, m_presentVb, 0, sizeof(PRESENTVERTEX));
assert(SUCCEEDED(result));
result = m_device->SetFVF(PRESENTFVF);
assert(SUCCEEDED(result));
result = m_device->SetTexture(0, framebuffer->m_renderTarget);
assert(SUCCEEDED(result));
result = m_device->SetTexture(1, nullptr);
assert(SUCCEEDED(result));
result = m_device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
assert(SUCCEEDED(result));
}
}
void CGSH_Direct3D9::MakeLinearZOrtho(float* matrix, float left, float right, float bottom, float top)
{
matrix[ 0] = 2.0f / (right - left);
matrix[ 1] = 0;
matrix[ 2] = 0;
matrix[ 3] = 0;
matrix[ 4] = 0;
matrix[ 5] = - 2.0f / (top - bottom);
matrix[ 6] = 0;
matrix[ 7] = 0;
matrix[ 8] = 0;
matrix[ 9] = 0;
matrix[10] = 1;
matrix[11] = 0;
matrix[12] = - (right + left) / (right - left);
matrix[13] = (top + bottom) / (top - bottom);
matrix[14] = 0;
matrix[15] = 1;
}
bool CGSH_Direct3D9::TestDevice()
{
HRESULT coopLevelResult = m_device->TestCooperativeLevel();
if(FAILED(coopLevelResult))
{
if(coopLevelResult == D3DERR_DEVICELOST)
{
return false;
}
else if(coopLevelResult == D3DERR_DEVICENOTRESET)
{
OnDeviceResetting();
auto presentParams = CreatePresentParams();
HRESULT result = m_device->Reset(&presentParams);
if(FAILED(result))
{
assert(0);
return false;
}
OnDeviceReset();
}
else
{
assert(0);
}
}
auto clientRect = m_outputWnd->GetClientRect();
bool sizeChanged =
(clientRect.Width() != m_deviceWindowWidth) ||
(clientRect.Height() != m_deviceWindowHeight);
if(sizeChanged)
{
OnDeviceResetting();
auto presentParams = CreatePresentParams();
HRESULT result = m_device->Reset(&presentParams);
if(FAILED(result))
{
assert(0);
}
OnDeviceReset();
}
return true;
}
D3DPRESENT_PARAMETERS CGSH_Direct3D9::CreatePresentParams()
{
RECT clientRect = m_outputWnd->GetClientRect();
unsigned int outputWidth = clientRect.right;
unsigned int outputHeight = clientRect.bottom;
D3DPRESENT_PARAMETERS d3dpp;
memset(&d3dpp, 0, sizeof(D3DPRESENT_PARAMETERS));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = m_outputWnd->m_hWnd;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferWidth = outputWidth;
d3dpp.BackBufferHeight = outputHeight;
return d3dpp;
}
void CGSH_Direct3D9::CreateDevice()
{
auto presentParams = CreatePresentParams();
HRESULT result = S_OK;
result = m_d3d->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
m_outputWnd->m_hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&presentParams,
&m_device);
assert(SUCCEEDED(result));
OnDeviceReset();
m_sceneBegun = false;
BeginScene();
}
void CGSH_Direct3D9::OnDeviceReset()
{
HRESULT result = S_OK;
#ifdef _WIREFRAME
m_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
#endif
m_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
m_device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
m_device->SetRenderState(D3DRS_LIGHTING, FALSE);
m_device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
m_device->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
result = m_device->CreateVertexBuffer(4 * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &m_drawVb, nullptr);
assert(SUCCEEDED(result));
result = m_device->CreateVertexBuffer(4 * sizeof(PRESENTVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, PRESENTFVF, D3DPOOL_DEFAULT, &m_presentVb, nullptr);
assert(SUCCEEDED(result));
static const D3DVERTEXELEMENT9 vertexElements[] =
{
{ 0, offsetof(CUSTOMVERTEX, x), D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0 },
{ 0, offsetof(CUSTOMVERTEX, s), D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_TEXCOORD, 0 },
{ 0, offsetof(CUSTOMVERTEX, color), D3DDECLTYPE_D3DCOLOR, 0, D3DDECLUSAGE_TEXCOORD, 1 },
D3DDECL_END()
};
result = m_device->CreateVertexDeclaration(vertexElements, &m_vertexDeclaration);
assert(SUCCEEDED(result));
{
uint8* buffer = nullptr;
result = m_presentVb->Lock(0, sizeof(g_presentVertices), reinterpret_cast<void**>(&buffer), D3DLOCK_DISCARD);
assert(SUCCEEDED(result));
memcpy(buffer, g_presentVertices, sizeof(g_presentVertices));
result = m_presentVb->Unlock();
assert(SUCCEEDED(result));
}
auto clientRect = m_outputWnd->GetClientRect();
m_deviceWindowWidth = clientRect.Width();
m_deviceWindowHeight = clientRect.Height();
result = m_device->CreateTexture(16, 1, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_clutTexture4, nullptr);
assert(SUCCEEDED(result));
result = m_device->CreateTexture(256, 1, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_clutTexture8, nullptr);
assert(SUCCEEDED(result));
m_renderState.isValid = false;
}
void CGSH_Direct3D9::OnDeviceResetting()
{
m_drawVb.Reset();
m_presentVb.Reset();
m_vertexDeclaration.Reset();
m_framebuffers.clear();
m_depthbuffers.clear();
m_textureCache.Flush();
m_clutTexture4.Reset();
m_clutTexture8.Reset();
}
float CGSH_Direct3D9::GetZ(float nZ)
{
if(nZ < 256)
{
//The number is small, so scale to a smaller ratio (65536)
return nZ / 32768.0f;
}
else
{
// nZ -= m_nMaxZ;
if(nZ > m_nMaxZ) return 1.0;
// if(nZ < -m_nMaxZ) return -1.0;
return nZ / m_nMaxZ;
}
}
void CGSH_Direct3D9::Prim_Line()
{
float nU1 = 0, nU2 = 0;
float nV1 = 0, nV2 = 0;
float nQ1 = 1, nQ2 = 1;
float nF1 = 0, nF2 = 0;
XYZ vertex[2];
vertex[0] <<= m_vtxBuffer[1].nPosition;
vertex[1] <<= m_vtxBuffer[0].nPosition;
float nX1 = vertex[0].GetX(), nX2 = vertex[1].GetX();
float nY1 = vertex[0].GetY(), nY2 = vertex[1].GetY();
float nZ1 = vertex[0].GetZ(), nZ2 = vertex[1].GetZ();
RGBAQ rgbaq[2];
rgbaq[0] <<= m_vtxBuffer[1].nRGBAQ;
rgbaq[1] <<= m_vtxBuffer[0].nRGBAQ;
nX1 -= m_nPrimOfsX;
nX2 -= m_nPrimOfsX;
nY1 -= m_nPrimOfsY;
nY2 -= m_nPrimOfsY;
nZ1 = GetZ(nZ1);
nZ2 = GetZ(nZ2);
if(m_primitiveMode.nFog)
{
//glEnable(GL_FOG);
nF1 = (float)(0xFF - m_vtxBuffer[1].nFog) / 255.0f;
nF2 = (float)(0xFF - m_vtxBuffer[0].nFog) / 255.0f;
}
else
{
nF1 = nF2 = 0.0;
}
if(m_primitiveMode.nTexture)
{
//Textured triangle
if(m_primitiveMode.nUseUV)
{
UV uv[2];
uv[0] <<= m_vtxBuffer[1].nUV;
uv[1] <<= m_vtxBuffer[0].nUV;
nU1 = uv[0].GetU() / static_cast<float>(m_currentTextureWidth);
nU2 = uv[1].GetU() / static_cast<float>(m_currentTextureWidth);
nV1 = uv[0].GetV() / static_cast<float>(m_currentTextureHeight);
nV2 = uv[1].GetV() / static_cast<float>(m_currentTextureHeight);
}
else
{
ST st[2];
st[0] <<= m_vtxBuffer[1].nST;
st[1] <<= m_vtxBuffer[0].nST;
nU1 = st[0].nS, nU2 = st[1].nS;
nV1 = st[0].nT, nV2 = st[1].nT;
nQ1 = rgbaq[0].nQ; nQ2 = rgbaq[1].nQ;
}
}
//Build vertex buffer
{
HRESULT result = S_OK;
DWORD color0 = D3DCOLOR_ARGB(rgbaq[0].nA, rgbaq[0].nR, rgbaq[0].nG, rgbaq[0].nB);
DWORD color1 = D3DCOLOR_ARGB(rgbaq[1].nA, rgbaq[1].nR, rgbaq[1].nG, rgbaq[1].nB);
CUSTOMVERTEX vertices[] =
{
{ nX1, nY1, nZ1, color0, nU1, nV1, nQ1 },
{ nX2, nY2, nZ2, color1, nU2, nV2, nQ2 },
};
uint8* buffer = nullptr;
result = m_drawVb->Lock(0, sizeof(CUSTOMVERTEX) * 3, reinterpret_cast<void**>(&buffer), D3DLOCK_DISCARD);
assert(SUCCEEDED(result));
{
memcpy(buffer, vertices, sizeof(vertices));
}
result = m_drawVb->Unlock();
assert(SUCCEEDED(result));
result = m_device->SetVertexDeclaration(m_vertexDeclaration);
assert(SUCCEEDED(result));
result = m_device->SetStreamSource(0, m_drawVb, 0, sizeof(CUSTOMVERTEX));
assert(SUCCEEDED(result));
result = m_device->DrawPrimitive(D3DPT_LINELIST, 0, 1);
assert(SUCCEEDED(result));
m_drawCallCount++;
}
if(m_primitiveMode.nFog)
{
//glDisable(GL_FOG);
}
}
void CGSH_Direct3D9::Prim_Triangle()
{
float nU1 = 0, nU2 = 0, nU3 = 0;
float nV1 = 0, nV2 = 0, nV3 = 0;
float nQ1 = 1, nQ2 = 1, nQ3 = 1;
float nF1 = 0, nF2 = 0, nF3 = 0;
XYZ vertex[3];
vertex[0] <<= m_vtxBuffer[2].nPosition;
vertex[1] <<= m_vtxBuffer[1].nPosition;
vertex[2] <<= m_vtxBuffer[0].nPosition;
float nX1 = vertex[0].GetX(), nX2 = vertex[1].GetX(), nX3 = vertex[2].GetX();
float nY1 = vertex[0].GetY(), nY2 = vertex[1].GetY(), nY3 = vertex[2].GetY();
float nZ1 = vertex[0].GetZ(), nZ2 = vertex[1].GetZ(), nZ3 = vertex[2].GetZ();
RGBAQ rgbaq[3];
rgbaq[0] <<= m_vtxBuffer[2].nRGBAQ;
rgbaq[1] <<= m_vtxBuffer[1].nRGBAQ;
rgbaq[2] <<= m_vtxBuffer[0].nRGBAQ;
nX1 -= m_nPrimOfsX;
nX2 -= m_nPrimOfsX;
nX3 -= m_nPrimOfsX;
nY1 -= m_nPrimOfsY;
nY2 -= m_nPrimOfsY;
nY3 -= m_nPrimOfsY;
nZ1 = GetZ(nZ1);
nZ2 = GetZ(nZ2);
nZ3 = GetZ(nZ3);
if(m_primitiveMode.nFog)
{
//glEnable(GL_FOG);
nF1 = (float)(0xFF - m_vtxBuffer[2].nFog) / 255.0f;
nF2 = (float)(0xFF - m_vtxBuffer[1].nFog) / 255.0f;
nF3 = (float)(0xFF - m_vtxBuffer[0].nFog) / 255.0f;
}
else
{
nF1 = nF2 = nF3 = 0.0;
}
if(m_primitiveMode.nTexture)
{
//Textured triangle
if(m_primitiveMode.nUseUV)
{
UV uv[3];
uv[0] <<= m_vtxBuffer[2].nUV;
uv[1] <<= m_vtxBuffer[1].nUV;
uv[2] <<= m_vtxBuffer[0].nUV;
nU1 = uv[0].GetU() / static_cast<float>(m_currentTextureWidth);
nU2 = uv[1].GetU() / static_cast<float>(m_currentTextureWidth);
nU3 = uv[2].GetU() / static_cast<float>(m_currentTextureWidth);
nV1 = uv[0].GetV() / static_cast<float>(m_currentTextureHeight);
nV2 = uv[1].GetV() / static_cast<float>(m_currentTextureHeight);
nV3 = uv[2].GetV() / static_cast<float>(m_currentTextureHeight);
}
else
{
ST st[3];
st[0] <<= m_vtxBuffer[2].nST;
st[1] <<= m_vtxBuffer[1].nST;
st[2] <<= m_vtxBuffer[0].nST;
nU1 = st[0].nS, nU2 = st[1].nS, nU3 = st[2].nS;
nV1 = st[0].nT, nV2 = st[1].nT, nV3 = st[2].nT;
nQ1 = rgbaq[0].nQ; nQ2 = rgbaq[1].nQ; nQ3 = rgbaq[2].nQ;
}
}
//Build vertex buffer
{
HRESULT result = S_OK;
DWORD color0 = D3DCOLOR_ARGB(rgbaq[0].nA, rgbaq[0].nR, rgbaq[0].nG, rgbaq[0].nB);
DWORD color1 = D3DCOLOR_ARGB(rgbaq[1].nA, rgbaq[1].nR, rgbaq[1].nG, rgbaq[1].nB);
DWORD color2 = D3DCOLOR_ARGB(rgbaq[2].nA, rgbaq[2].nR, rgbaq[2].nG, rgbaq[2].nB);
if(m_primitiveMode.nShading == 0)
{
//Flat shaded triangles use the last color set
color0 = color1 = color2;
}
CUSTOMVERTEX vertices[] =
{
{ nX1, nY1, nZ1, color0, nU1, nV1, nQ1 },
{ nX2, nY2, nZ2, color1, nU2, nV2, nQ2 },
{ nX3, nY3, nZ3, color2, nU3, nV3, nQ3 },
};
uint8* buffer = nullptr;
result = m_drawVb->Lock(0, sizeof(CUSTOMVERTEX) * 3, reinterpret_cast<void**>(&buffer), D3DLOCK_DISCARD);
assert(SUCCEEDED(result));
{
memcpy(buffer, vertices, sizeof(vertices));
}
result = m_drawVb->Unlock();
assert(SUCCEEDED(result));
result = m_device->SetVertexDeclaration(m_vertexDeclaration);
assert(SUCCEEDED(result));
result = m_device->SetStreamSource(0, m_drawVb, 0, sizeof(CUSTOMVERTEX));
assert(SUCCEEDED(result));
result = m_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
assert(SUCCEEDED(result));
m_drawCallCount++;
}
if(m_primitiveMode.nFog)
{
//glDisable(GL_FOG);
}
}
void CGSH_Direct3D9::Prim_Sprite()
{
float nU1 = 0, nU2 = 0;
float nV1 = 0, nV2 = 0;
float nF1 = 0, nF2 = 0;
XYZ vertex[2];
vertex[0] <<= m_vtxBuffer[1].nPosition;
vertex[1] <<= m_vtxBuffer[0].nPosition;
float nX1 = vertex[0].GetX(), nY1 = vertex[0].GetY();
float nX2 = vertex[1].GetX(), nY2 = vertex[1].GetY(), nZ = vertex[1].GetZ();
RGBAQ rgbaq[2];
rgbaq[0] <<= m_vtxBuffer[1].nRGBAQ;
rgbaq[1] <<= m_vtxBuffer[0].nRGBAQ;
nX1 -= m_nPrimOfsX;
nX2 -= m_nPrimOfsX;
nY1 -= m_nPrimOfsY;
nY2 -= m_nPrimOfsY;
nZ = GetZ(nZ);
if(m_primitiveMode.nTexture)
{
//Textured triangle
if(m_primitiveMode.nUseUV)
{
UV uv[2];
uv[0] <<= m_vtxBuffer[1].nUV;
uv[1] <<= m_vtxBuffer[0].nUV;
nU1 = uv[0].GetU() / static_cast<float>(m_currentTextureWidth);
nU2 = uv[1].GetU() / static_cast<float>(m_currentTextureWidth);
nV1 = uv[0].GetV() / static_cast<float>(m_currentTextureHeight);
nV2 = uv[1].GetV() / static_cast<float>(m_currentTextureHeight);
}
else
{
ST st[2];
st[0] <<= m_vtxBuffer[1].nST;
st[1] <<= m_vtxBuffer[0].nST;
float q1 = rgbaq[1].nQ;
float q2 = rgbaq[0].nQ;
if(q1 == 0) q1 = 1;
if(q2 == 0) q2 = 1;
nU1 = st[0].nS / q1;
nU2 = st[1].nS / q2;
nV1 = st[0].nT / q1;
nV2 = st[1].nT / q2;
}
}
{
HRESULT result;
DWORD color1 = D3DCOLOR_ARGB(rgbaq[1].nA, rgbaq[1].nR, rgbaq[1].nG, rgbaq[1].nB);
CUSTOMVERTEX vertices[] =
{
{ nX1, nY2, nZ, color1, nU1, nV2, 1 },
{ nX1, nY1, nZ, color1, nU1, nV1, 1 },
{ nX2, nY2, nZ, color1, nU2, nV2, 1 },
{ nX2, nY1, nZ, color1, nU2, nV1, 1 },
};
uint8* buffer = nullptr;
result = m_drawVb->Lock(0, sizeof(CUSTOMVERTEX) * 4, reinterpret_cast<void**>(&buffer), D3DLOCK_DISCARD);
assert(SUCCEEDED(result));
{
memcpy(buffer, vertices, sizeof(vertices));
}
result = m_drawVb->Unlock();
assert(SUCCEEDED(result));
result = m_device->SetVertexDeclaration(m_vertexDeclaration);
assert(SUCCEEDED(result));
result = m_device->SetStreamSource(0, m_drawVb, 0, sizeof(CUSTOMVERTEX));
assert(SUCCEEDED(result));
result = m_device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
assert(SUCCEEDED(result));
m_drawCallCount++;
}
}
void CGSH_Direct3D9::FillShaderCapsFromTexture(SHADERCAPS& shaderCaps, const uint64& tex0Reg)
{
auto tex0 = make_convertible<TEX0>(tex0Reg);
shaderCaps.texSourceMode = TEXTURE_SOURCE_MODE_STD;
if(CGsPixelFormats::IsPsmIDTEX(tex0.nPsm))
{
shaderCaps.texSourceMode = CGsPixelFormats::IsPsmIDTEX4(tex0.nPsm) ? TEXTURE_SOURCE_MODE_IDX4 : TEXTURE_SOURCE_MODE_IDX8;
}
shaderCaps.texFunction = tex0.nFunction;
}
void CGSH_Direct3D9::SetRenderingContext(uint64 primReg)
{
auto prim = make_convertible<PRMODE>(primReg);
unsigned int context = prim.nContext;
uint64 testReg = m_nReg[GS_REG_TEST_1 + context];
uint64 frameReg = m_nReg[GS_REG_FRAME_1 + context];
uint64 alphaReg = m_nReg[GS_REG_ALPHA_1 + context];
uint64 zbufReg = m_nReg[GS_REG_ZBUF_1 + context];
uint64 tex0Reg = m_nReg[GS_REG_TEX0_1 + context];
uint64 tex1Reg = m_nReg[GS_REG_TEX1_1 + context];
uint64 miptbp1Reg = m_nReg[GS_REG_MIPTBP1_1 + context];
uint64 miptbp2Reg = m_nReg[GS_REG_MIPTBP2_1 + context];
uint64 clampReg = m_nReg[GS_REG_CLAMP_1 + context];
uint64 scissorReg = m_nReg[GS_REG_SCISSOR_1 + context];
//Get shader caps
auto shaderCaps = make_convertible<SHADERCAPS>(0);
FillShaderCapsFromTexture(shaderCaps, tex0Reg);
if(!prim.nTexture)
{
shaderCaps.texSourceMode = TEXTURE_SOURCE_MODE_NONE;
}
{
auto shaderIterator = m_vertexShaders.find(shaderCaps);
if(shaderIterator == std::end(m_vertexShaders))
{
auto shader = CreateVertexShader(shaderCaps);
m_vertexShaders.insert(std::make_pair(shaderCaps, shader));
shaderIterator = m_vertexShaders.find(shaderCaps);
}
m_device->SetVertexShader(shaderIterator->second);
}
{
auto shaderIterator = m_pixelShaders.find(shaderCaps);
if(shaderIterator == std::end(m_pixelShaders))
{
auto shader = CreatePixelShader(shaderCaps);
m_pixelShaders.insert(std::make_pair(shaderCaps, shader));
shaderIterator = m_pixelShaders.find(shaderCaps);
}
m_device->SetPixelShader(shaderIterator->second);
}
if(!m_renderState.isValid ||
(m_renderState.primReg != primReg))
{
m_device->SetRenderState(D3DRS_ALPHABLENDENABLE, ((prim.nAlpha != 0) && m_alphaBlendingEnabled) ? TRUE : FALSE);
}
if(!m_renderState.isValid ||
(m_renderState.alphaReg != alphaReg))
{
SetupBlendingFunction(alphaReg);
}
if(!m_renderState.isValid ||
(m_renderState.testReg != testReg))
{
SetupTestFunctions(testReg);
}
if(!m_renderState.isValid ||
(m_renderState.zbufReg != zbufReg) ||
(m_renderState.frameReg != frameReg))
{
SetupDepthBuffer(zbufReg, frameReg);
}
if(!m_renderState.isValid ||
(m_renderState.frameReg != frameReg) ||
(m_renderState.scissorReg != scissorReg))
{
SetupFramebuffer(frameReg, scissorReg);
}
if(!m_renderState.isValid ||
(m_renderState.tex0Reg != tex0Reg) ||
(m_renderState.tex1Reg != tex1Reg) ||
(m_renderState.clampReg != clampReg))
{
SetupTexture(tex0Reg, tex1Reg, miptbp1Reg, miptbp2Reg, clampReg);
}
m_renderState.isValid = true;
m_renderState.primReg = primReg;
m_renderState.alphaReg = alphaReg;
m_renderState.testReg = testReg;
m_renderState.zbufReg = zbufReg;
m_renderState.frameReg = frameReg;
m_renderState.tex0Reg = tex0Reg;
m_renderState.tex1Reg = tex1Reg;
m_renderState.clampReg = clampReg;
m_renderState.scissorReg = scissorReg;
auto offset = make_convertible<XYOFFSET>(m_nReg[GS_REG_XYOFFSET_1 + context]);
m_nPrimOfsX = offset.GetX();
m_nPrimOfsY = offset.GetY();
if(GetCrtIsInterlaced() && GetCrtIsFrameMode())
{
if(m_nCSR & CSR_FIELD)
{
m_nPrimOfsY += 0.5;
}
}
}
void CGSH_Direct3D9::SetupBlendingFunction(uint64 alphaReg)
{
auto alpha = make_convertible<ALPHA>(alphaReg);
auto blendOp = D3DBLENDOP_ADD;
m_device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, D3DZB_TRUE);
m_device->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
m_device->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_ZERO);
if((alpha.nA == alpha.nB) && (alpha.nD == ALPHABLEND_ABD_CS))
{
//ab*0 (when a == b) - Cs
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
}
else if((alpha.nA == alpha.nB) && (alpha.nD == ALPHABLEND_ABD_CD))
{
//ab*1 (when a == b) - Cd
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
}
else if((alpha.nA == alpha.nB) && (alpha.nD == ALPHABLEND_ABD_ZERO))
{
//ab*2 (when a == b) - Zero
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
}
else if((alpha.nA == 0) && (alpha.nB == 1) && (alpha.nC == 0) && (alpha.nD == 1))
{
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
}
else if((alpha.nA == 0) && (alpha.nB == 1) && (alpha.nC == 1) && (alpha.nD == 1))
{
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVDESTALPHA);
}
else if((alpha.nA == ALPHABLEND_ABD_CS) && (alpha.nB == ALPHABLEND_ABD_CD) && (alpha.nC == ALPHABLEND_C_FIX) && (alpha.nD == ALPHABLEND_ABD_CD))
{
//0121 - Cs * FIX + Cd * (1 - FIX)
uint8 fix = MulBy2Clamp(alpha.nFix);
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_BLENDFACTOR);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVBLENDFACTOR);
m_device->SetRenderState(D3DRS_BLENDFACTOR, D3DCOLOR_ARGB(fix, fix, fix, fix));
}
else if((alpha.nA == ALPHABLEND_ABD_CS) && (alpha.nB == ALPHABLEND_ABD_ZERO) && (alpha.nC == ALPHABLEND_C_FIX) && (alpha.nD == ALPHABLEND_ABD_CD))
{
//0221 - Cs * FIX + Cd
uint8 fix = MulBy2Clamp(alpha.nFix);
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_BLENDFACTOR);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
m_device->SetRenderState(D3DRS_BLENDFACTOR, D3DCOLOR_ARGB(fix, fix, fix, fix));
}
else if((alpha.nA == 0) && (alpha.nB == 2) && (alpha.nC == 0) && (alpha.nD == 1))
{
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
}
else if((alpha.nA == ALPHABLEND_ABD_CS) && (alpha.nB == ALPHABLEND_ABD_ZERO) && (alpha.nC == ALPHABLEND_C_AS) && (alpha.nD == ALPHABLEND_ABD_ZERO))
{
//0202 - Cs * As
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
}
else if((alpha.nA == ALPHABLEND_ABD_CS) && (alpha.nB == ALPHABLEND_ABD_ZERO) && (alpha.nC == ALPHABLEND_C_FIX) && (alpha.nD == ALPHABLEND_ABD_ZERO))
{
//0222 - Cs * FIX
uint8 fix = MulBy2Clamp(alpha.nFix);
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_BLENDFACTOR);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
m_device->SetRenderState(D3DRS_BLENDFACTOR, D3DCOLOR_ARGB(fix, fix, fix, fix));
}
else if((alpha.nA == 1) && (alpha.nB == 0) && (alpha.nC == 0) && (alpha.nD == 0))
{
//(Cd - Cs) * As + Cs
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVSRCALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCALPHA);
}
else if((alpha.nA == ALPHABLEND_ABD_CD) && (alpha.nB == ALPHABLEND_ABD_CS) && (alpha.nC == ALPHABLEND_C_AD) && (alpha.nD == ALPHABLEND_ABD_CS))
{
//1010 -> Cs * (1 - Ad) + Cd * Ad
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVDESTALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_DESTALPHA);
}
else if((alpha.nA == ALPHABLEND_ABD_CD) && (alpha.nB == ALPHABLEND_ABD_CS) && (alpha.nC == ALPHABLEND_C_FIX) && (alpha.nD == ALPHABLEND_ABD_CS))
{
//1020 -> Cs * (1 - FIX) + Cd * FIX
uint8 fix = MulBy2Clamp(alpha.nFix);
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVBLENDFACTOR);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_BLENDFACTOR);
m_device->SetRenderState(D3DRS_BLENDFACTOR, D3DCOLOR_ARGB(fix, fix, fix, fix));
}
else if((alpha.nA == ALPHABLEND_ABD_CD) && (alpha.nB == ALPHABLEND_ABD_ZERO) && (alpha.nC == ALPHABLEND_C_AS) && (alpha.nD == ALPHABLEND_ABD_ZERO))
{
//1202 - Cd * As
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCALPHA);
}
else if((alpha.nA == ALPHABLEND_ABD_CD) && (alpha.nB == ALPHABLEND_ABD_ZERO) && (alpha.nC == ALPHABLEND_C_FIX) && (alpha.nD == ALPHABLEND_ABD_CS))
{
//1220 -> Cd * FIX + Cs
uint8 fix = MulBy2Clamp(alpha.nFix);
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_BLENDFACTOR);
m_device->SetRenderState(D3DRS_BLENDFACTOR, D3DCOLOR_ARGB(fix, fix, fix, fix));
}
else if((alpha.nA == ALPHABLEND_ABD_ZERO) && (alpha.nB == ALPHABLEND_ABD_CS) && (alpha.nC == ALPHABLEND_C_AS) && (alpha.nD == ALPHABLEND_ABD_CD))
{
//2001 -> Cd - Cs * As
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
blendOp = D3DBLENDOP_REVSUBTRACT;
}
else if((alpha.nA == ALPHABLEND_ABD_ZERO) && (alpha.nB == ALPHABLEND_ABD_CS) && (alpha.nC == ALPHABLEND_C_AD) && (alpha.nD == ALPHABLEND_ABD_CD))
{
//2011 -> Cd - Cs * Ad
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
blendOp = D3DBLENDOP_REVSUBTRACT;
}
else if((alpha.nA == ALPHABLEND_ABD_ZERO) && (alpha.nB == ALPHABLEND_ABD_CS) && (alpha.nC == ALPHABLEND_C_FIX) && (alpha.nD == ALPHABLEND_ABD_CD))
{
//2021 -> Cd - Cs * FIX
uint8 fix = MulBy2Clamp(alpha.nFix);
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_BLENDFACTOR);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
m_device->SetRenderState(D3DRS_BLENDFACTOR, D3DCOLOR_ARGB(fix, fix, fix, fix));
blendOp = D3DBLENDOP_REVSUBTRACT;
}
else if((alpha.nA == ALPHABLEND_ABD_ZERO) && (alpha.nB == ALPHABLEND_ABD_CD) && (alpha.nC == ALPHABLEND_C_AS) && (alpha.nD == ALPHABLEND_ABD_CD))
{
//2101 -> Cd * (1 - As)
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
}
else if((alpha.nA == 2) && (alpha.nB == 1) && (alpha.nC == 2) && (alpha.nD == 1))
{
//(0 - Cd) * FIX + Cd
// -> 0 * Cs + (1 - FIX) * Cd
uint8 fix = alpha.nFix;
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVBLENDFACTOR);
m_device->SetRenderState(D3DRS_BLENDFACTOR, D3DCOLOR_ARGB(fix, fix, fix, fix));
}
else
{
assert(0);
//Default blending
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
}
m_device->SetRenderState(D3DRS_BLENDOP, blendOp);
}
void CGSH_Direct3D9::SetupTestFunctions(uint64 testReg)
{
auto tst = make_convertible<TEST>(testReg);
if(tst.nAlphaEnabled)
{
static const D3DCMPFUNC g_alphaTestFunc[ALPHA_TEST_MAX] =
{
D3DCMP_NEVER,
D3DCMP_ALWAYS,
D3DCMP_LESS,
D3DCMP_LESSEQUAL,
D3DCMP_EQUAL,
D3DCMP_GREATEREQUAL,
D3DCMP_GREATER,
D3DCMP_NOTEQUAL
};
//If alpha test is set to always fail but don't keep fragment info, we need to set
//proper masks at in other places
if(tst.nAlphaMethod == ALPHA_TEST_NEVER && tst.nAlphaFail != ALPHA_TEST_FAIL_KEEP)
{
m_device->SetRenderState(D3DRS_ALPHATESTENABLE, D3DZB_FALSE);
}
else
{
m_device->SetRenderState(D3DRS_ALPHAFUNC, g_alphaTestFunc[tst.nAlphaMethod]);
m_device->SetRenderState(D3DRS_ALPHAREF, tst.nAlphaRef);
m_device->SetRenderState(D3DRS_ALPHATESTENABLE, m_alphaTestingEnabled ? D3DZB_TRUE : D3DZB_FALSE);
}
}
else
{
m_device->SetRenderState(D3DRS_ALPHATESTENABLE, D3DZB_FALSE);
}
if(tst.nDepthEnabled)
{
unsigned int depthFunc = D3DCMP_NEVER;
switch(tst.nDepthMethod)
{
case 0:
depthFunc = D3DCMP_NEVER;
break;
case 1:
depthFunc = D3DCMP_ALWAYS;
break;
case 2:
depthFunc = D3DCMP_GREATEREQUAL;
break;
case 3:
depthFunc = D3DCMP_GREATER;
break;
}
m_device->SetRenderState(D3DRS_ZENABLE, m_depthTestingEnabled ? D3DZB_TRUE : D3DZB_FALSE);
m_device->SetRenderState(D3DRS_ZFUNC, depthFunc);
}
else
{
m_device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
}
}
void CGSH_Direct3D9::SetupTexture(uint64 tex0Reg, uint64 tex1Reg, uint64 miptbp1Reg, uint64 miptbp2Reg, uint64 clampReg)
{
if(tex0Reg == 0)
{
return;
}
auto tex0 = make_convertible<TEX0>(tex0Reg);
auto tex1 = make_convertible<TEX1>(tex1Reg);
auto miptbp1 = make_convertible<MIPTBP1>(miptbp1Reg);
auto miptbp2 = make_convertible<MIPTBP2>(miptbp2Reg);
auto clamp = make_convertible<CLAMP>(clampReg);
auto texInfo = LoadTexture(tex0, tex1.nMaxMip, miptbp1, miptbp2);
m_currentTextureWidth = tex0.GetWidth();
m_currentTextureHeight = tex0.GetHeight();
int nMagFilter = D3DTEXF_NONE, nMinFilter = D3DTEXF_NONE, nMipFilter = D3DTEXF_NONE;
int nWrapS = 0, nWrapT = 0;
if(tex1.nMagFilter == 0)
{
nMagFilter = D3DTEXF_POINT;
}
else
{
nMagFilter = D3DTEXF_LINEAR;
}
switch(tex1.nMinFilter)
{
case 0:
nMinFilter = D3DTEXF_POINT;
break;
case 1:
nMinFilter = D3DTEXF_LINEAR;
break;
case 5:
nMinFilter = D3DTEXF_LINEAR;
nMipFilter = D3DTEXF_LINEAR;
break;
default:
assert(0);
break;
}
if(CGsPixelFormats::IsPsmIDTEX(tex0.nPsm))
{
//Make sure point sampling is used, filtering will be done in shader
if(nMinFilter == D3DTEXF_LINEAR) nMinFilter = D3DTEXF_POINT;
if(nMagFilter == D3DTEXF_LINEAR) nMagFilter = D3DTEXF_POINT;
if(nMipFilter == D3DTEXF_LINEAR) nMipFilter = D3DTEXF_POINT;
auto clutTexture = GetClutTexture(tex0);
m_device->SetTexture(1, clutTexture);
m_device->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_POINT);
m_device->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
m_device->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
m_device->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
m_device->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
}
m_device->SetTexture(0, texInfo.texture);
m_device->SetSamplerState(0, D3DSAMP_MINFILTER, nMinFilter);
m_device->SetSamplerState(0, D3DSAMP_MAGFILTER, nMagFilter);
m_device->SetSamplerState(0, D3DSAMP_MIPFILTER, nMipFilter);
}
void CGSH_Direct3D9::SetupFramebuffer(uint64 frameReg, uint64 scissorReg)
{
if(frameReg == 0) return;
auto frame = make_convertible<FRAME>(frameReg);
auto scissor = make_convertible<SCISSOR>(scissorReg);
{
bool r = (frame.nMask & 0x000000FF) == 0;
bool g = (frame.nMask & 0x0000FF00) == 0;
bool b = (frame.nMask & 0x00FF0000) == 0;
bool a = (frame.nMask & 0xFF000000) == 0;
UINT colorMask =
(r ? D3DCOLORWRITEENABLE_RED : 0) |
(g ? D3DCOLORWRITEENABLE_GREEN : 0) |
(b ? D3DCOLORWRITEENABLE_BLUE : 0) |
(a ? D3DCOLORWRITEENABLE_ALPHA : 0);
m_device->SetRenderState(D3DRS_COLORWRITEENABLE, colorMask);
}
bool newFramebuffer = false;
auto framebuffer = FindFramebuffer(frameReg);
if(!framebuffer)
{
framebuffer = FramebufferPtr(new CFramebuffer(m_device, frame.GetBasePtr(), frame.GetWidth(), 1024, frame.nPsm));
m_framebuffers.push_back(framebuffer);
newFramebuffer = true;
}
//Any framebuffer selected at this point can be used as a texture
framebuffer->m_canBeUsedAsTexture = true;
float projWidth = static_cast<float>(framebuffer->m_width);
float projHeight = static_cast<float>(framebuffer->m_height);
HRESULT result = S_OK;
Framework::Win32::CComPtr<IDirect3DSurface9> renderSurface;
result = framebuffer->m_renderTarget->GetSurfaceLevel(0, &renderSurface);
assert(SUCCEEDED(result));
result = m_device->SetRenderTarget(0, renderSurface);
assert(SUCCEEDED(result));
if(newFramebuffer)
{
//TODO: Get actual contents from GS RAM
m_device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
m_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0);
}
RECT scissorRect = {};
scissorRect.left = scissor.scax0;
scissorRect.top = scissor.scay0;
scissorRect.right = scissor.scax1 + 1;
scissorRect.bottom = scissor.scay1 + 1;
m_device->SetScissorRect(&scissorRect);
m_device->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
float projMatrix[16];
MakeLinearZOrtho(projMatrix, 0, projWidth, 0, projHeight);
m_device->SetVertexShaderConstantF(0, projMatrix, 4);
}
void CGSH_Direct3D9::SetupDepthBuffer(uint64 zbufReg, uint64 frameReg)
{
auto frame = make_convertible<FRAME>(frameReg);
auto zbuf = make_convertible<ZBUF>(zbufReg);
auto depthbuffer = FindDepthbuffer(zbufReg, frameReg);
if(!depthbuffer)
{
depthbuffer = DepthbufferPtr(new CDepthbuffer(m_device, zbuf.GetBasePtr(), frame.GetWidth(), 1024, zbuf.nPsm));
m_depthbuffers.push_back(depthbuffer);
}
switch(CGsPixelFormats::GetPsmPixelSize(zbuf.nPsm))
{
case 16:
m_nMaxZ = 32768.0f;
break;
case 24:
m_nMaxZ = 8388608.0f;
break;
default:
case 32:
m_nMaxZ = 2147483647.0f;
break;
}
HRESULT result = m_device->SetDepthStencilSurface(depthbuffer->m_depthSurface);
assert(SUCCEEDED(result));
m_device->SetRenderState(D3DRS_ZWRITEENABLE, (zbuf.nMask == 0) ? TRUE : FALSE);
}
void CGSH_Direct3D9::WriteRegisterImpl(uint8 nRegister, uint64 nData)
{
CGSHandler::WriteRegisterImpl(nRegister, nData);
switch(nRegister)
{
case GS_REG_PRIM:
m_primitiveType = static_cast<unsigned int>(nData & 0x07);
switch(m_primitiveType)
{
case PRIM_POINT:
m_vtxCount = 1;
break;
case PRIM_LINE:
case PRIM_LINESTRIP:
m_vtxCount = 2;
break;
case PRIM_TRIANGLE:
case PRIM_TRIANGLESTRIP:
case PRIM_TRIANGLEFAN:
m_vtxCount = 3;
break;
case PRIM_SPRITE:
m_vtxCount = 2;
break;
}
break;
case GS_REG_XYZ2:
case GS_REG_XYZ3:
case GS_REG_XYZF2:
case GS_REG_XYZF3:
VertexKick(nRegister, nData);
break;
//case GS_REG_FOGCOL:
// SetupFogColor();
// break;
}
}
void CGSH_Direct3D9::VertexKick(uint8 nRegister, uint64 nValue)
{
if(m_vtxCount == 0) return;
bool drawingKick = (nRegister == GS_REG_XYZ2) || (nRegister == GS_REG_XYZF2);
bool fog = (nRegister == GS_REG_XYZF2) || (nRegister == GS_REG_XYZF3);
if(fog)
{
m_vtxBuffer[m_vtxCount - 1].nPosition = nValue & 0x00FFFFFFFFFFFFFFULL;
m_vtxBuffer[m_vtxCount - 1].nRGBAQ = m_nReg[GS_REG_RGBAQ];
m_vtxBuffer[m_vtxCount - 1].nUV = m_nReg[GS_REG_UV];
m_vtxBuffer[m_vtxCount - 1].nST = m_nReg[GS_REG_ST];
m_vtxBuffer[m_vtxCount - 1].nFog = (uint8)(nValue >> 56);
}
else
{
m_vtxBuffer[m_vtxCount - 1].nPosition = nValue;
m_vtxBuffer[m_vtxCount - 1].nRGBAQ = m_nReg[GS_REG_RGBAQ];
m_vtxBuffer[m_vtxCount - 1].nUV = m_nReg[GS_REG_UV];
m_vtxBuffer[m_vtxCount - 1].nST = m_nReg[GS_REG_ST];
m_vtxBuffer[m_vtxCount - 1].nFog = (uint8)(m_nReg[GS_REG_FOG] >> 56);
}
m_vtxCount--;
if(m_vtxCount == 0)
{
{
if((m_nReg[GS_REG_PRMODECONT] & 1) != 0)
{
m_primitiveMode <<= m_nReg[GS_REG_PRIM];
}
else
{
m_primitiveMode <<= m_nReg[GS_REG_PRMODE];
}
SetRenderingContext(m_primitiveMode);
switch(m_primitiveType)
{
case PRIM_POINT:
//if(drawingKick) Prim_Point();
m_vtxCount = 1;
break;
case PRIM_LINE:
if(drawingKick) Prim_Line();
m_vtxCount = 2;
break;
case PRIM_LINESTRIP:
if(drawingKick) Prim_Line();
memcpy(&m_vtxBuffer[1], &m_vtxBuffer[0], sizeof(VERTEX));
m_vtxCount = 1;
break;
case PRIM_TRIANGLE:
if(drawingKick) Prim_Triangle();
m_vtxCount = 3;
break;
case PRIM_TRIANGLESTRIP:
if(drawingKick) Prim_Triangle();
memcpy(&m_vtxBuffer[2], &m_vtxBuffer[1], sizeof(VERTEX));
memcpy(&m_vtxBuffer[1], &m_vtxBuffer[0], sizeof(VERTEX));
m_vtxCount = 1;
break;
case PRIM_TRIANGLEFAN:
if(drawingKick) Prim_Triangle();
memcpy(&m_vtxBuffer[1], &m_vtxBuffer[0], sizeof(VERTEX));
m_vtxCount = 1;
break;
case PRIM_SPRITE:
if(drawingKick) Prim_Sprite();
m_vtxCount = 2;
break;
}
}
}
}
/////////////////////////////////////////////////////////////
// Framebuffer
/////////////////////////////////////////////////////////////
CGSH_Direct3D9::CFramebuffer::CFramebuffer(DevicePtr& device, uint32 basePtr, uint32 width, uint32 height, uint32 psm)
: m_basePtr(basePtr)
, m_width(width)
, m_height(height)
, m_psm(psm)
, m_canBeUsedAsTexture(false)
{
HRESULT result = S_OK;
result = device->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_renderTarget, NULL);
assert(SUCCEEDED(result));
}
CGSH_Direct3D9::CFramebuffer::~CFramebuffer()
{
}
/////////////////////////////////////////////////////////////
// Depthbuffer
/////////////////////////////////////////////////////////////
CGSH_Direct3D9::CDepthbuffer::CDepthbuffer(DevicePtr& device, uint32 basePtr, uint32 width, uint32 height, uint32 psm)
: m_basePtr(basePtr)
, m_width(width)
, m_height(height)
, m_psm(psm)
{
HRESULT result = S_OK;
result = device->CreateDepthStencilSurface(width, height, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, FALSE, &m_depthSurface, nullptr);
assert(SUCCEEDED(result));
}
CGSH_Direct3D9::CDepthbuffer::~CDepthbuffer()
{
}