/* * 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(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 = 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.MaxLOD = FLT_MAX; sd.MaxAnisotropy = 16; 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); // return true; } void GSDevice11::SetupVS(VSSelector sel, const VSConstantBuffer* cb) { hash_map::const_iterator i = m_vs.find(sel); if(i == m_vs.end()) { string str[4]; str[0] = format("%d", sel.bppz); str[1] = format("%d", sel.tme); str[2] = format("%d", sel.fst); str[3] = format("%d", sel.rtcopy); D3D11_SHADER_MACRO macro[] = { {"VS_BPPZ", str[0].c_str()}, {"VS_TME", str[1].c_str()}, {"VS_FST", str[2].c_str()}, {"VS_RTCOPY", str[3].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; CompileShader(IDR_TFX_FX, "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) { CComPtr gs; if(sel.prim > 0 && (sel.iip == 0 || sel.prim == 3)) // geometry shader works in every case, but not needed { hash_map >::const_iterator i = m_gs.find(sel); if(i != m_gs.end()) { gs = i->second; } else { string str[2]; str[0] = format("%d", sel.iip); str[1] = format("%d", sel.prim); D3D11_SHADER_MACRO macro[] = { {"GS_IIP", str[0].c_str()}, {"GS_PRIM", str[1].c_str()}, {NULL, NULL}, }; CompileShader(IDR_TFX_FX, "gs_main", macro, &gs); m_gs[sel] = gs; } } GSSetShader(gs); } void GSDevice11::SetupPS(PSSelector sel, const PSConstantBuffer* cb, PSSamplerSelector ssel) { hash_map >::const_iterator i = m_ps.find(sel); if(i == m_ps.end()) { string str[17]; 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.date); str[15] = format("%d", sel.spritehack); str[16] = format("%d", sel.point_sampler); D3D11_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_DATE", str[14].c_str()}, {"PS_SPRITEHACK", str[15].c_str()}, {"PS_POINT_SAMPLER", str[16].c_str()}, {NULL, NULL}, }; CComPtr ps; CompileShader(IDR_TFX_FX, "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; } hash_map >::const_iterator i = m_ps_ss.find(ssel); if(i != m_ps_ss.end()) { ss0 = i->second; } else { D3D11_SAMPLER_DESC sd; memset(&sd, 0, sizeof(sd)); sd.Filter = ssel.ltf ? D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT : 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.MaxLOD = FLT_MAX; sd.MaxAnisotropy = 16; 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, sel.date ? m_rt_ss : NULL); PSSetShader(i->second, m_ps_cb); } void GSDevice11::SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, uint8 afix) { hash_map >::const_iterator i = 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.alpha_stencil ? 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.alpha_stencil ? 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); hash_map >::const_iterator j = 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 = (D3D11_BLEND_OP)m_blendMapD3D9[i].op; bd.RenderTarget[0].SrcBlend = (D3D11_BLEND)m_blendMapD3D9[i].src; bd.RenderTarget[0].DestBlend = (D3D11_BLEND)m_blendMapD3D9[i].dst; bd.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; bd.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; bd.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; // SRC* -> SRC1* // Yes, this casting mess really is needed. I want to go back to C if(bd.RenderTarget[0].SrcBlend >= 3 && bd.RenderTarget[0].SrcBlend <= 6) { bd.RenderTarget[0].SrcBlend = (D3D11_BLEND)((int)bd.RenderTarget[0].SrcBlend + 13); } if(bd.RenderTarget[0].DestBlend >= 3 && bd.RenderTarget[0].DestBlend <= 6) { bd.RenderTarget[0].DestBlend = (D3D11_BLEND)((int)bd.RenderTarget[0].DestBlend + 13); } // 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(m_blendMapD3D9[i].bogus == 1) { (bsel.a == 0 ? bd.RenderTarget[0].SrcBlend : bd.RenderTarget[0].DestBlend) = D3D11_BLEND_ONE; const string afixstr = format("%d >> 7", afix); const char *col[3] = {"Cs", "Cd", "0"}; const char *alpha[3] = {"As", "Ad", afixstr.c_str()}; printf("Impossible blend for D3D: (%s - %s) * %s + %s\n", col[bsel.a], col[bsel.b], alpha[bsel.c], col[bsel.d]); } } 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); }