mirror of https://github.com/PCSX2/pcsx2.git
297 lines
6.8 KiB
C++
297 lines
6.8 KiB
C++
/*
|
|
* Copyright (C) 2007-2009 Gabest
|
|
* http://www.gabest.org
|
|
*
|
|
* This Program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This Program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with GNU Make; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA.
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "GSRendererDX9.h"
|
|
#include "GSCrc.h"
|
|
#include "resource.h"
|
|
|
|
GSRendererDX9::GSRendererDX9()
|
|
: GSRendererDX(new GSTextureCache9(this))
|
|
{
|
|
}
|
|
|
|
bool GSRendererDX9::CreateDevice(GSDevice* dev)
|
|
{
|
|
if(!__super::CreateDevice(dev))
|
|
return false;
|
|
|
|
//
|
|
|
|
memset(&m_fba.dss, 0, sizeof(m_fba.dss));
|
|
|
|
m_fba.dss.StencilEnable = true;
|
|
m_fba.dss.StencilReadMask = 2;
|
|
m_fba.dss.StencilWriteMask = 2;
|
|
m_fba.dss.StencilFunc = D3DCMP_EQUAL;
|
|
m_fba.dss.StencilPassOp = D3DSTENCILOP_ZERO;
|
|
m_fba.dss.StencilFailOp = D3DSTENCILOP_ZERO;
|
|
m_fba.dss.StencilDepthFailOp = D3DSTENCILOP_ZERO;
|
|
m_fba.dss.StencilRef = 2;
|
|
|
|
memset(&m_fba.bs, 0, sizeof(m_fba.bs));
|
|
|
|
m_fba.bs.RenderTargetWriteMask = D3DCOLORWRITEENABLE_ALPHA;
|
|
|
|
//
|
|
|
|
return true;
|
|
}
|
|
|
|
void GSRendererDX9::EmulateChannelShuffle(GSTexture** rt, const GSTextureCache::Source* tex)
|
|
{
|
|
// Channel shuffle will never be supported on Direct3D9 through shaders so just
|
|
// use code that skips the bad draw calls.
|
|
if (m_channel_shuffle)
|
|
{
|
|
if (m_game.title == CRC::Tekken5)
|
|
{
|
|
if (m_context->FRAME.FBW == 1)
|
|
{
|
|
// Used in stages: Secret Garden, Acid Rain, Moonlit Wilderness
|
|
// 12 pages: 2 calls by channel, 3 channels, 1 blit
|
|
// Minus current draw call
|
|
m_skip = 12 * (3 + 3 + 1) - 1;
|
|
}
|
|
else
|
|
{
|
|
// Could skip model drawing if wrongly detected
|
|
m_channel_shuffle = false;
|
|
}
|
|
}
|
|
else if ((tex->m_texture->GetType() == GSTexture::DepthStencil) && !(tex->m_32_bits_fmt))
|
|
{
|
|
// So far 2 games hit this code path. Urban Chaos and Tales of Abyss.
|
|
throw GSDXRecoverableError();
|
|
}
|
|
else if (m_index.tail <= 64 && m_context->CLAMP.WMT == 3)
|
|
{
|
|
// Blood will tell. I think it is channel effect too but again
|
|
// implemented in a different way. I don't want to add more CRC stuff. So
|
|
// let's disable channel when the signature is different.
|
|
//
|
|
// Note: Tales Of Abyss and Tekken5 could hit this path too. Those games are
|
|
// handled above.
|
|
m_channel_shuffle = false;
|
|
}
|
|
else if (m_context->CLAMP.WMS == 3 && ((m_context->CLAMP.MAXU & 0x8) == 8))
|
|
{
|
|
// Read either blue or Alpha.
|
|
// MGS3/Kill Zone
|
|
throw GSDXRecoverableError();
|
|
}
|
|
else if (m_context->CLAMP.WMS == 3 && ((m_context->CLAMP.MINU & 0x8) == 0))
|
|
{
|
|
// Read either Red or Green.
|
|
// Terminator 3
|
|
throw GSDXRecoverableError();
|
|
}
|
|
else
|
|
{
|
|
m_channel_shuffle = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GSRendererDX9::EmulateTextureShuffleAndFbmask()
|
|
{
|
|
if (m_texture_shuffle)
|
|
{
|
|
// Texture shuffle is not supported so make sure nothing is written on all channels.
|
|
m_om_bsel.wrgba = 0;
|
|
}
|
|
else
|
|
{
|
|
m_ps_sel.dfmt = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmt;
|
|
|
|
m_om_bsel.wrgba = ~GSVector4i::load((int)m_context->FRAME.FBMSK).eq8(GSVector4i::xffffffff()).mask();
|
|
}
|
|
}
|
|
|
|
void GSRendererDX9::SetupIA(const float& sx, const float& sy)
|
|
{
|
|
D3DPRIMITIVETYPE topology;
|
|
|
|
switch(m_vt.m_primclass)
|
|
{
|
|
case GS_POINT_CLASS:
|
|
|
|
topology = D3DPT_POINTLIST;
|
|
|
|
break;
|
|
|
|
case GS_LINE_CLASS:
|
|
|
|
topology = D3DPT_LINELIST;
|
|
|
|
if(PRIM->IIP == 0)
|
|
{
|
|
for(size_t i = 0, j = m_index.tail; i < j; i += 2)
|
|
{
|
|
uint32 tmp = m_index.buff[i + 0];
|
|
m_index.buff[i + 0] = m_index.buff[i + 1];
|
|
m_index.buff[i + 1] = tmp;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case GS_TRIANGLE_CLASS:
|
|
|
|
topology = D3DPT_TRIANGLELIST;
|
|
|
|
if(PRIM->IIP == 0)
|
|
{
|
|
for(size_t i = 0, j = m_index.tail; i < j; i += 3)
|
|
{
|
|
uint32 tmp = m_index.buff[i + 0];
|
|
m_index.buff[i + 0] = m_index.buff[i + 2];
|
|
m_index.buff[i + 2] = tmp;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case GS_SPRITE_CLASS:
|
|
|
|
topology = D3DPT_TRIANGLELIST;
|
|
|
|
// each sprite converted to quad needs twice the space
|
|
|
|
Lines2Sprites();
|
|
|
|
break;
|
|
|
|
default:
|
|
__assume(0);
|
|
}
|
|
|
|
GSDevice9* dev = (GSDevice9*)m_dev;
|
|
|
|
(*dev)->SetRenderState(D3DRS_SHADEMODE, PRIM->IIP ? D3DSHADE_GOURAUD : D3DSHADE_FLAT); // TODO
|
|
|
|
void* ptr = NULL;
|
|
|
|
if(dev->IAMapVertexBuffer(&ptr, sizeof(GSVertexHW9), m_vertex.next))
|
|
{
|
|
GSVertex* RESTRICT s = (GSVertex*)m_vertex.buff;
|
|
GSVertexHW9* RESTRICT d = (GSVertexHW9*)ptr;
|
|
|
|
for(uint32 i = 0; i < m_vertex.next; i++, s++, d++)
|
|
{
|
|
GSVector4 p = GSVector4(GSVector4i::load(s->XYZ.u32[0]).upl16());
|
|
|
|
if(PRIM->TME && !PRIM->FST)
|
|
{
|
|
p = p.xyxy(GSVector4((float)s->XYZ.Z, s->RGBAQ.Q));
|
|
}
|
|
else
|
|
{
|
|
p = p.xyxy(GSVector4::load((float)s->XYZ.Z));
|
|
}
|
|
|
|
GSVector4 t = GSVector4::zero();
|
|
|
|
if(PRIM->TME)
|
|
{
|
|
if(PRIM->FST)
|
|
{
|
|
if(UserHacks_WildHack && !isPackedUV_HackFlag)
|
|
{
|
|
t = GSVector4(GSVector4i::load(s->UV & 0x3FEF3FEF).upl16());
|
|
//printf("GSDX: %08X | D3D9(%d) %s\n", s->UV & 0x3FEF3FEF, m_vertex.next, i == 0 ? "*" : "");
|
|
}
|
|
else
|
|
{
|
|
t = GSVector4(GSVector4i::load(s->UV).upl16());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
t = GSVector4::loadl(&s->ST);
|
|
}
|
|
}
|
|
|
|
t = t.xyxy(GSVector4::cast(GSVector4i(s->RGBAQ.u32[0], s->FOG)));
|
|
|
|
d->p = p;
|
|
d->t = t;
|
|
}
|
|
|
|
dev->IAUnmapVertexBuffer();
|
|
}
|
|
|
|
dev->IASetIndexBuffer(m_index.buff, m_index.tail);
|
|
|
|
dev->IASetPrimitiveTopology(topology);
|
|
}
|
|
|
|
void GSRendererDX9::UpdateFBA(GSTexture* rt)
|
|
{
|
|
if (!rt)
|
|
return;
|
|
|
|
GSDevice9* dev = (GSDevice9*)m_dev;
|
|
|
|
dev->BeginScene();
|
|
|
|
// om
|
|
|
|
dev->OMSetDepthStencilState(&m_fba.dss);
|
|
dev->OMSetBlendState(&m_fba.bs, 0);
|
|
|
|
// ia
|
|
|
|
GSVector4 s = GSVector4(rt->GetScale().x / rt->GetWidth(), rt->GetScale().y / rt->GetHeight());
|
|
GSVector4 off = GSVector4(-1.0f, 1.0f);
|
|
|
|
GSVector4 src = ((m_vt.m_min.p.xyxy(m_vt.m_max.p) + off.xxyy()) * s.xyxy()).sat(off.zzyy());
|
|
GSVector4 dst = src * 2.0f + off.xxxx();
|
|
|
|
GSVertexPT1 vertices[] =
|
|
{
|
|
{GSVector4(dst.x, -dst.y, 0.5f, 1.0f), GSVector2(0)},
|
|
{GSVector4(dst.z, -dst.y, 0.5f, 1.0f), GSVector2(0)},
|
|
{GSVector4(dst.x, -dst.w, 0.5f, 1.0f), GSVector2(0)},
|
|
{GSVector4(dst.z, -dst.w, 0.5f, 1.0f), GSVector2(0)},
|
|
};
|
|
|
|
dev->IASetVertexBuffer(vertices, sizeof(vertices[0]), countof(vertices));
|
|
dev->IASetInputLayout(dev->m_convert.il);
|
|
dev->IASetPrimitiveTopology(D3DPT_TRIANGLESTRIP);
|
|
|
|
// vs
|
|
|
|
dev->VSSetShader(dev->m_convert.vs, NULL, 0);
|
|
|
|
// ps
|
|
|
|
dev->PSSetShader(dev->m_convert.ps[4], NULL, 0);
|
|
|
|
//
|
|
|
|
dev->DrawPrimitive();
|
|
|
|
//
|
|
|
|
dev->EndScene();
|
|
}
|