/* * 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 "GSDevice11.h" #include "resource.h" #include "GSTables.h" bool GSDevice11::CreateTextureFX() { HRESULT hr; D3D11_BUFFER_DESC bd; memset(&bd, 0, sizeof(bd)); bd.ByteWidth = sizeof(VSConstantBuffer); bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; hr = m_dev->CreateBuffer(&bd, NULL, &m_vs_cb); if(FAILED(hr)) return false; memset(&bd, 0, sizeof(bd)); bd.ByteWidth = sizeof(GSConstantBuffer); bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; hr = m_dev->CreateBuffer(&bd, NULL, &m_gs_cb); if (FAILED(hr)) return false; memset(&bd, 0, sizeof(bd)); bd.ByteWidth = sizeof(PSConstantBuffer); bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; hr = m_dev->CreateBuffer(&bd, NULL, &m_ps_cb); if(FAILED(hr)) return false; D3D11_SAMPLER_DESC sd; memset(&sd, 0, sizeof(sd)); sd.Filter = theApp.GetConfigI("MaxAnisotropy") && !theApp.GetConfigB("paltex") ? D3D11_FILTER_ANISOTROPIC : D3D11_FILTER_MIN_MAG_MIP_POINT; sd.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; sd.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; sd.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; sd.MinLOD = -FLT_MAX; sd.MaxLOD = FLT_MAX; sd.MaxAnisotropy = theApp.GetConfigI("MaxAnisotropy"); sd.ComparisonFunc = D3D11_COMPARISON_NEVER; hr = m_dev->CreateSamplerState(&sd, &m_palette_ss); if(FAILED(hr)) return false; hr = m_dev->CreateSamplerState(&sd, &m_rt_ss); if(FAILED(hr)) return false; // create layout VSSelector sel; VSConstantBuffer cb; SetupVS(sel, &cb); GSConstantBuffer gcb; SetupGS(GSSelector(1), &gcb); // return true; } void GSDevice11::SetupVS(VSSelector sel, const VSConstantBuffer* cb) { auto i = std::as_const(m_vs).find(sel); if(i == m_vs.end()) { std::string str[3]; str[0] = format("%d", sel.bppz); str[1] = format("%d", sel.tme); str[2] = format("%d", sel.fst); D3D_SHADER_MACRO macro[] = { {"VS_BPPZ", str[0].c_str()}, {"VS_TME", str[1].c_str()}, {"VS_FST", str[2].c_str()}, {NULL, NULL}, }; D3D11_INPUT_ELEMENT_DESC layout[] = { {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 1, DXGI_FORMAT_R32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"POSITION", 0, DXGI_FORMAT_R16G16_UINT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"POSITION", 1, DXGI_FORMAT_R32_UINT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 2, DXGI_FORMAT_R16G16_UINT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"COLOR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0}, }; GSVertexShader11 vs; std::vector shader; theApp.LoadResource(IDR_TFX_FX, shader); CreateShader(shader, "tfx.fx", nullptr, "vs_main", macro, &vs.vs, layout, countof(layout), &vs.il); m_vs[sel] = vs; i = m_vs.find(sel); } if(m_vs_cb_cache.Update(cb)) { ID3D11DeviceContext* ctx = m_ctx; ctx->UpdateSubresource(m_vs_cb, 0, NULL, cb, 0, 0); } VSSetShader(i->second.vs, m_vs_cb); IASetInputLayout(i->second.il); } void GSDevice11::SetupGS(GSSelector sel, const GSConstantBuffer* cb) { CComPtr gs; bool Unscale_GSShader = (sel.point == 1 || sel.line == 1); if((sel.prim > 0 && (sel.iip == 0 || sel.prim == 3)) || Unscale_GSShader) // geometry shader works in every case, but not needed { auto i = std::as_const(m_gs).find(sel); if(i != m_gs.end()) { gs = i->second; } else { std::string str[4]; str[0] = format("%d", sel.iip); str[1] = format("%d", sel.prim); str[2] = format("%d", sel.point); str[3] = format("%d", sel.line); D3D_SHADER_MACRO macro[] = { {"GS_IIP", str[0].c_str()}, {"GS_PRIM", str[1].c_str()}, {"GS_POINT", str[2].c_str()}, {"GS_LINE", str[3].c_str()}, {NULL, NULL}, }; std::vector shader; theApp.LoadResource(IDR_TFX_FX, shader); CreateShader(shader, "tfx.fx", nullptr, "gs_main", macro, &gs); m_gs[sel] = gs; } } if (m_gs_cb_cache.Update(cb)) { ID3D11DeviceContext* ctx = m_ctx; ctx->UpdateSubresource(m_gs_cb, 0, NULL, cb, 0, 0); } GSSetShader(gs, m_gs_cb); } void GSDevice11::SetupPS(PSSelector sel, const PSConstantBuffer* cb, PSSamplerSelector ssel) { auto i = std::as_const(m_ps).find(sel); if(i == m_ps.end()) { std::string str[26]; str[0] = format("%d", sel.fst); str[1] = format("%d", sel.wms); str[2] = format("%d", sel.wmt); str[3] = format("%d", sel.fmt); str[4] = format("%d", sel.aem); str[5] = format("%d", sel.tfx); str[6] = format("%d", sel.tcc); str[7] = format("%d", sel.atst); str[8] = format("%d", sel.fog); str[9] = format("%d", sel.clr1); str[10] = format("%d", sel.fba); str[11] = format("%d", sel.aout); str[12] = format("%d", sel.ltf); str[13] = format("%d", sel.colclip); str[14] = format("%d", sel.spritehack); str[15] = format("%d", sel.tcoffsethack); str[16] = format("%d", sel.point_sampler); str[17] = format("%d", sel.shuffle); str[18] = format("%d", sel.read_ba); str[19] = format("%d", sel.channel); str[20] = format("%d", sel.tales_of_abyss_hle); str[21] = format("%d", sel.urban_chaos_hle); str[22] = format("%d", sel.dfmt); str[23] = format("%d", sel.depth_fmt); str[24] = format("%d", sel.fmt >> 2); str[25] = format("%d", m_upscale_multiplier); D3D_SHADER_MACRO macro[] = { {"PS_FST", str[0].c_str()}, {"PS_WMS", str[1].c_str()}, {"PS_WMT", str[2].c_str()}, {"PS_FMT", str[3].c_str()}, {"PS_AEM", str[4].c_str()}, {"PS_TFX", str[5].c_str()}, {"PS_TCC", str[6].c_str()}, {"PS_ATST", str[7].c_str()}, {"PS_FOG", str[8].c_str()}, {"PS_CLR1", str[9].c_str()}, {"PS_FBA", str[10].c_str()}, {"PS_AOUT", str[11].c_str()}, {"PS_LTF", str[12].c_str()}, {"PS_COLCLIP", str[13].c_str()}, {"PS_SPRITEHACK", str[14].c_str()}, {"PS_TCOFFSETHACK", str[15].c_str()}, {"PS_POINT_SAMPLER", str[16].c_str()}, {"PS_SHUFFLE", str[17].c_str() }, {"PS_READ_BA", str[18].c_str() }, {"PS_CHANNEL_FETCH", str[19].c_str() }, {"PS_TALES_OF_ABYSS_HLE", str[20].c_str() }, {"PS_URBAN_CHAOS_HLE", str[21].c_str() }, {"PS_DFMT", str[22].c_str() }, {"PS_DEPTH_FMT", str[23].c_str() }, {"PS_PAL_FMT", str[24].c_str() }, {"PS_SCALE_FACTOR", str[25].c_str() }, {NULL, NULL}, }; CComPtr ps; std::vector shader; theApp.LoadResource(IDR_TFX_FX, shader); CreateShader(shader, "tfx.fx", nullptr, "ps_main", macro, &ps); m_ps[sel] = ps; i = m_ps.find(sel); } if(m_ps_cb_cache.Update(cb)) { ID3D11DeviceContext* ctx = m_ctx; ctx->UpdateSubresource(m_ps_cb, 0, NULL, cb, 0, 0); } CComPtr ss0, ss1; if(sel.tfx != 4) { if(!(sel.fmt < 3 && sel.wms < 3 && sel.wmt < 3)) { ssel.ltf = 0; } auto i = std::as_const(m_ps_ss).find(ssel); if(i != m_ps_ss.end()) { ss0 = i->second; } else { D3D11_SAMPLER_DESC sd, af; memset(&sd, 0, sizeof(sd)); af.Filter = theApp.GetConfigI("MaxAnisotropy") && !theApp.GetConfigB("paltex") ? D3D11_FILTER_ANISOTROPIC : D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; sd.Filter = ssel.ltf ? af.Filter : D3D11_FILTER_MIN_MAG_MIP_POINT; sd.AddressU = ssel.tau ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP; sd.AddressV = ssel.tav ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP; sd.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; sd.MinLOD = -FLT_MAX; sd.MaxLOD = FLT_MAX; sd.MaxAnisotropy = theApp.GetConfigI("MaxAnisotropy"); sd.ComparisonFunc = D3D11_COMPARISON_NEVER; m_dev->CreateSamplerState(&sd, &ss0); m_ps_ss[ssel] = ss0; } if(sel.fmt >= 3) { ss1 = m_palette_ss; } } PSSetSamplerState(ss0, ss1); PSSetShader(i->second, m_ps_cb); } void GSDevice11::SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, uint8 afix) { auto i = std::as_const(m_om_dss).find(dssel); if(i == m_om_dss.end()) { D3D11_DEPTH_STENCIL_DESC dsd; memset(&dsd, 0, sizeof(dsd)); if(dssel.date) { dsd.StencilEnable = true; dsd.StencilReadMask = 1; dsd.StencilWriteMask = 1; dsd.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL; dsd.FrontFace.StencilPassOp = dssel.date_one ? D3D11_STENCIL_OP_ZERO : D3D11_STENCIL_OP_KEEP; dsd.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; dsd.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; dsd.BackFace.StencilFunc = D3D11_COMPARISON_EQUAL; dsd.BackFace.StencilPassOp = dssel.date_one ? D3D11_STENCIL_OP_ZERO : D3D11_STENCIL_OP_KEEP; dsd.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; dsd.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; } if(dssel.ztst != ZTST_ALWAYS || dssel.zwe) { static const D3D11_COMPARISON_FUNC ztst[] = { D3D11_COMPARISON_NEVER, D3D11_COMPARISON_ALWAYS, D3D11_COMPARISON_GREATER_EQUAL, D3D11_COMPARISON_GREATER }; dsd.DepthEnable = true; dsd.DepthWriteMask = dssel.zwe ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; dsd.DepthFunc = ztst[dssel.ztst]; } CComPtr dss; m_dev->CreateDepthStencilState(&dsd, &dss); m_om_dss[dssel] = dss; i = m_om_dss.find(dssel); } OMSetDepthStencilState(i->second, 1); auto j = std::as_const(m_om_bs).find(bsel); if(j == m_om_bs.end()) { D3D11_BLEND_DESC bd; memset(&bd, 0, sizeof(bd)); bd.RenderTarget[0].BlendEnable = bsel.abe; if(bsel.abe) { int i = ((bsel.a * 3 + bsel.b) * 3 + bsel.c) * 3 + bsel.d; bd.RenderTarget[0].BlendOp = m_blendMapD3D11[i].op; bd.RenderTarget[0].SrcBlend = m_blendMapD3D11[i].src; bd.RenderTarget[0].DestBlend = m_blendMapD3D11[i].dst; bd.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; bd.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; bd.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; // Not very good but I don't wanna write another 81 row table if(bsel.negative) { if(bd.RenderTarget[0].BlendOp == D3D11_BLEND_OP_ADD) { bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_REV_SUBTRACT; } else if(bd.RenderTarget[0].BlendOp == D3D11_BLEND_OP_REV_SUBTRACT) { bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; } else ; // god knows, best just not to mess with it for now } } if(bsel.wr) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_RED; if(bsel.wg) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_GREEN; if(bsel.wb) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_BLUE; if(bsel.wa) bd.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_ALPHA; CComPtr bs; m_dev->CreateBlendState(&bd, &bs); m_om_bs[bsel] = bs; j = m_om_bs.find(bsel); } OMSetBlendState(j->second, (float)(int)afix / 0x80); }