2013-04-18 03:29:41 +00:00
|
|
|
// Copyright 2013 Dolphin Emulator Project
|
|
|
|
// Licensed under GPLv2
|
|
|
|
// Refer to the license.txt file included.
|
2011-03-14 09:38:29 +00:00
|
|
|
|
|
|
|
#include <sstream>
|
2014-02-17 10:18:15 +00:00
|
|
|
|
|
|
|
#include "VideoBackends/D3D/D3DBase.h"
|
|
|
|
#include "VideoBackends/D3D/D3DShader.h"
|
2014-02-19 11:14:09 +00:00
|
|
|
#include "VideoBackends/D3D/LineGeometryShader.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoCommon/VertexShaderGen.h"
|
2011-03-14 09:38:29 +00:00
|
|
|
|
|
|
|
namespace DX11
|
|
|
|
{
|
|
|
|
|
2011-04-15 22:34:54 +00:00
|
|
|
struct LineGSParams
|
2011-03-14 09:38:29 +00:00
|
|
|
{
|
2011-04-15 22:34:54 +00:00
|
|
|
FLOAT LineWidth; // In units of 1/6 of an EFB pixel
|
|
|
|
FLOAT TexOffset;
|
|
|
|
FLOAT VpWidth; // Width and height of the viewport in EFB pixels
|
|
|
|
FLOAT VpHeight;
|
|
|
|
FLOAT TexOffsetEnable[8]; // For each tex coordinate, whether to apply offset to it (1 on, 0 off)
|
|
|
|
};
|
|
|
|
|
|
|
|
union LineGSParams_Padded
|
|
|
|
{
|
|
|
|
LineGSParams params;
|
2011-03-14 09:38:29 +00:00
|
|
|
// Constant buffers must be a multiple of 16 bytes in size.
|
2011-04-15 22:34:54 +00:00
|
|
|
u8 pad[(sizeof(LineGSParams) + 15) & ~15];
|
2011-03-14 09:38:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const char LINE_GS_COMMON[] =
|
|
|
|
// The struct VS_OUTPUT used by the vertex shader goes here.
|
|
|
|
"// dolphin-emu line geometry shader common part\n"
|
|
|
|
|
|
|
|
"cbuffer cbParams : register(b0)\n"
|
|
|
|
"{\n"
|
|
|
|
"struct\n" // Should match LineGSParams above
|
|
|
|
"{\n"
|
|
|
|
"float LineWidth;\n"
|
|
|
|
"float TexOffset;\n"
|
2011-03-28 05:42:21 +00:00
|
|
|
"float VpWidth;\n"
|
|
|
|
"float VpHeight;\n"
|
2011-04-15 22:34:54 +00:00
|
|
|
"float TexOffsetEnable[8];\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
"} Params;\n"
|
|
|
|
"}\n"
|
|
|
|
|
|
|
|
"[maxvertexcount(4)]\n"
|
|
|
|
"void main(line VS_OUTPUT input[2], inout TriangleStream<VS_OUTPUT> outStream)\n"
|
|
|
|
"{\n"
|
|
|
|
// Pretend input[0] is on the bottom and input[1] is on top.
|
|
|
|
// We generate vertices to the left and right.
|
|
|
|
|
|
|
|
"VS_OUTPUT l0 = input[0];\n"
|
|
|
|
"VS_OUTPUT r0 = l0;\n"
|
|
|
|
"VS_OUTPUT l1 = input[1];\n"
|
|
|
|
"VS_OUTPUT r1 = l1;\n"
|
|
|
|
|
|
|
|
// GameCube/Wii's line drawing algorithm is a little quirky. It does not
|
|
|
|
// use the correct line caps. Instead, the line caps are vertical or
|
|
|
|
// horizontal depending the slope of the line.
|
|
|
|
|
|
|
|
"float2 offset;\n"
|
2011-03-28 05:42:21 +00:00
|
|
|
"float2 to = abs(input[1].pos.xy - input[0].pos.xy);\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
// FIXME: What does real hardware do when line is at a 45-degree angle?
|
|
|
|
// FIXME: Lines aren't drawn at the correct width. See Twilight Princess map.
|
2011-03-28 05:42:21 +00:00
|
|
|
"if (Params.VpHeight*to.y > Params.VpWidth*to.x) {\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
// Line is more tall. Extend geometry left and right.
|
2011-03-28 05:42:21 +00:00
|
|
|
// Lerp Params.LineWidth/2 from [0..VpWidth] to [-1..1]
|
|
|
|
"offset = float2(Params.LineWidth/Params.VpWidth, 0);\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
"} else {\n"
|
|
|
|
// Line is more wide. Extend geometry up and down.
|
2011-03-28 05:42:21 +00:00
|
|
|
// Lerp Params.LineWidth/2 from [0..VpHeight] to [1..-1]
|
|
|
|
"offset = float2(0, -Params.LineWidth/Params.VpHeight);\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
"}\n"
|
|
|
|
|
2011-03-16 03:02:25 +00:00
|
|
|
"l0.pos.xy -= offset * input[0].pos.w;\n"
|
|
|
|
"r0.pos.xy += offset * input[0].pos.w;\n"
|
|
|
|
"l1.pos.xy -= offset * input[1].pos.w;\n"
|
|
|
|
"r1.pos.xy += offset * input[1].pos.w;\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
|
|
|
|
"#ifndef NUM_TEXCOORDS\n"
|
|
|
|
"#error NUM_TEXCOORDS not defined\n"
|
|
|
|
"#endif\n"
|
|
|
|
|
2011-04-15 22:34:54 +00:00
|
|
|
// Apply TexOffset to all tex coordinates in the vertex.
|
|
|
|
// They can each be enabled seperately.
|
2011-03-14 09:38:29 +00:00
|
|
|
"#if NUM_TEXCOORDS >= 1\n"
|
2011-04-15 22:34:54 +00:00
|
|
|
"r0.tex0.x += Params.TexOffset * Params.TexOffsetEnable[0];\n"
|
|
|
|
"r1.tex0.x += Params.TexOffset * Params.TexOffsetEnable[0];\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
"#endif\n"
|
|
|
|
"#if NUM_TEXCOORDS >= 2\n"
|
2011-04-15 22:34:54 +00:00
|
|
|
"r0.tex1.x += Params.TexOffset * Params.TexOffsetEnable[1];\n"
|
|
|
|
"r1.tex1.x += Params.TexOffset * Params.TexOffsetEnable[1];\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
"#endif\n"
|
|
|
|
"#if NUM_TEXCOORDS >= 3\n"
|
2011-04-15 22:34:54 +00:00
|
|
|
"r0.tex2.x += Params.TexOffset * Params.TexOffsetEnable[2];\n"
|
|
|
|
"r1.tex2.x += Params.TexOffset * Params.TexOffsetEnable[2];\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
"#endif\n"
|
|
|
|
"#if NUM_TEXCOORDS >= 4\n"
|
2011-04-15 22:34:54 +00:00
|
|
|
"r0.tex3.x += Params.TexOffset * Params.TexOffsetEnable[3];\n"
|
|
|
|
"r1.tex3.x += Params.TexOffset * Params.TexOffsetEnable[3];\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
"#endif\n"
|
|
|
|
"#if NUM_TEXCOORDS >= 5\n"
|
2011-04-15 22:34:54 +00:00
|
|
|
"r0.tex4.x += Params.TexOffset * Params.TexOffsetEnable[4];\n"
|
|
|
|
"r1.tex4.x += Params.TexOffset * Params.TexOffsetEnable[4];\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
"#endif\n"
|
|
|
|
"#if NUM_TEXCOORDS >= 6\n"
|
2011-04-15 22:34:54 +00:00
|
|
|
"r0.tex5.x += Params.TexOffset * Params.TexOffsetEnable[5];\n"
|
|
|
|
"r1.tex5.x += Params.TexOffset * Params.TexOffsetEnable[5];\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
"#endif\n"
|
|
|
|
"#if NUM_TEXCOORDS >= 7\n"
|
2011-04-15 22:34:54 +00:00
|
|
|
"r0.tex6.x += Params.TexOffset * Params.TexOffsetEnable[6];\n"
|
|
|
|
"r1.tex6.x += Params.TexOffset * Params.TexOffsetEnable[6];\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
"#endif\n"
|
|
|
|
"#if NUM_TEXCOORDS >= 8\n"
|
2011-04-15 22:34:54 +00:00
|
|
|
"r0.tex7.x += Params.TexOffset * Params.TexOffsetEnable[7];\n"
|
|
|
|
"r1.tex7.x += Params.TexOffset * Params.TexOffsetEnable[7];\n"
|
2011-03-14 09:38:29 +00:00
|
|
|
"#endif\n"
|
|
|
|
|
|
|
|
"outStream.Append(l0);\n"
|
|
|
|
"outStream.Append(r0);\n"
|
|
|
|
"outStream.Append(l1);\n"
|
|
|
|
"outStream.Append(r1);\n"
|
|
|
|
"}\n"
|
|
|
|
;
|
|
|
|
|
|
|
|
LineGeometryShader::LineGeometryShader()
|
2014-03-09 20:14:26 +00:00
|
|
|
: m_ready(false), m_paramsBuffer(nullptr)
|
2011-06-11 19:37:21 +00:00
|
|
|
{ }
|
|
|
|
|
|
|
|
void LineGeometryShader::Init()
|
2011-03-14 09:38:29 +00:00
|
|
|
{
|
2011-06-11 19:37:21 +00:00
|
|
|
m_ready = false;
|
|
|
|
|
|
|
|
HRESULT hr;
|
|
|
|
|
2011-03-14 09:38:29 +00:00
|
|
|
// Create constant buffer for uploading data to geometry shader
|
2011-06-11 19:37:21 +00:00
|
|
|
|
2011-04-15 22:34:54 +00:00
|
|
|
D3D11_BUFFER_DESC bd = CD3D11_BUFFER_DESC(sizeof(LineGSParams_Padded),
|
|
|
|
D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);
|
2014-03-09 20:14:26 +00:00
|
|
|
hr = D3D::device->CreateBuffer(&bd, nullptr, &m_paramsBuffer);
|
2011-06-11 19:37:21 +00:00
|
|
|
CHECK(SUCCEEDED(hr), "create line geometry shader params buffer");
|
2011-03-14 09:38:29 +00:00
|
|
|
D3D::SetDebugObjectName(m_paramsBuffer, "line geometry shader params buffer");
|
|
|
|
|
|
|
|
m_ready = true;
|
|
|
|
}
|
|
|
|
|
2011-06-11 19:37:21 +00:00
|
|
|
void LineGeometryShader::Shutdown()
|
|
|
|
{
|
|
|
|
m_ready = false;
|
|
|
|
|
2014-02-12 15:00:34 +00:00
|
|
|
for (auto& it : m_shaders)
|
2011-06-11 19:37:21 +00:00
|
|
|
{
|
2014-02-12 15:00:34 +00:00
|
|
|
SAFE_RELEASE(it.second);
|
2011-06-11 19:37:21 +00:00
|
|
|
}
|
|
|
|
m_shaders.clear();
|
|
|
|
|
|
|
|
SAFE_RELEASE(m_paramsBuffer);
|
|
|
|
}
|
|
|
|
|
2011-03-28 05:42:21 +00:00
|
|
|
bool LineGeometryShader::SetShader(u32 components, float lineWidth,
|
2011-04-15 22:34:54 +00:00
|
|
|
float texOffset, float vpWidth, float vpHeight, const bool* texOffsetEnable)
|
2011-03-14 09:38:29 +00:00
|
|
|
{
|
|
|
|
if (!m_ready)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Make sure geometry shader for "components" is available
|
|
|
|
ComboMap::iterator shaderIt = m_shaders.find(components);
|
|
|
|
if (shaderIt == m_shaders.end())
|
|
|
|
{
|
|
|
|
// Generate new shader. Warning: not thread-safe.
|
2013-03-26 23:13:23 +00:00
|
|
|
static char buffer[16384];
|
2013-03-29 21:24:49 +00:00
|
|
|
ShaderCode code;
|
2013-03-26 23:13:23 +00:00
|
|
|
code.SetBuffer(buffer);
|
2014-01-15 15:58:36 +00:00
|
|
|
GenerateVSOutputStructForGS(code, API_D3D);
|
2013-03-26 23:13:23 +00:00
|
|
|
code.Write("\n%s", LINE_GS_COMMON);
|
|
|
|
|
2011-10-02 02:03:51 +00:00
|
|
|
std::stringstream numTexCoordsStream;
|
2014-04-27 18:59:04 +00:00
|
|
|
numTexCoordsStream << xfmem.numTexGen.numTexGens;
|
2013-10-29 05:23:17 +00:00
|
|
|
|
2011-03-14 09:38:29 +00:00
|
|
|
INFO_LOG(VIDEO, "Compiling line geometry shader for components 0x%.08X (num texcoords %d)",
|
2014-04-27 18:59:04 +00:00
|
|
|
components, xfmem.numTexGen.numTexGens);
|
2011-03-14 09:38:29 +00:00
|
|
|
|
2011-10-02 02:03:51 +00:00
|
|
|
const std::string& numTexCoordsStr = numTexCoordsStream.str();
|
2011-03-14 09:38:29 +00:00
|
|
|
D3D_SHADER_MACRO macros[] = {
|
2011-10-02 02:03:51 +00:00
|
|
|
{ "NUM_TEXCOORDS", numTexCoordsStr.c_str() },
|
2014-03-09 20:14:26 +00:00
|
|
|
{ nullptr, nullptr }
|
2011-03-14 09:38:29 +00:00
|
|
|
};
|
2014-07-07 23:28:12 +00:00
|
|
|
ID3D11GeometryShader* newShader = D3D::CompileAndCreateGeometryShader(code.GetBuffer(), macros);
|
2011-03-14 09:38:29 +00:00
|
|
|
if (!newShader)
|
|
|
|
{
|
|
|
|
WARN_LOG(VIDEO, "Line geometry shader for components 0x%.08X failed to compile", components);
|
|
|
|
// Add dummy shader to prevent trying to compile again
|
2014-03-09 20:14:26 +00:00
|
|
|
m_shaders[components] = nullptr;
|
2011-03-14 09:38:29 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
shaderIt = m_shaders.insert(std::make_pair(components, newShader)).first;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shaderIt != m_shaders.end())
|
|
|
|
{
|
|
|
|
if (shaderIt->second)
|
|
|
|
{
|
2011-04-15 22:34:54 +00:00
|
|
|
D3D11_MAPPED_SUBRESOURCE map;
|
2011-06-11 19:37:21 +00:00
|
|
|
HRESULT hr = D3D::context->Map(m_paramsBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
|
2011-04-15 22:34:54 +00:00
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
LineGSParams* params = (LineGSParams*)map.pData;
|
|
|
|
params->LineWidth = lineWidth;
|
2011-06-11 19:37:21 +00:00
|
|
|
|
2011-04-15 22:34:54 +00:00
|
|
|
params->TexOffset = texOffset;
|
|
|
|
params->VpWidth = vpWidth;
|
|
|
|
params->VpHeight = vpHeight;
|
|
|
|
for (int i = 0; i < 8; ++i)
|
|
|
|
params->TexOffsetEnable[i] = texOffsetEnable[i] ? 1.f : 0.f;
|
|
|
|
|
2011-06-11 19:37:21 +00:00
|
|
|
D3D::context->Unmap(m_paramsBuffer, 0);
|
2011-04-15 22:34:54 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
ERROR_LOG(VIDEO, "Failed to map line gs params buffer");
|
2011-03-14 09:38:29 +00:00
|
|
|
|
2011-03-28 05:42:21 +00:00
|
|
|
DEBUG_LOG(VIDEO, "Line params: width %f, texOffset %f, vpWidth %f, vpHeight %f",
|
|
|
|
lineWidth, texOffset, vpWidth, vpHeight);
|
|
|
|
|
2014-03-09 20:14:26 +00:00
|
|
|
D3D::context->GSSetShader(shaderIt->second, nullptr, 0);
|
2011-06-11 19:37:21 +00:00
|
|
|
D3D::context->GSSetConstantBuffers(0, 1, &m_paramsBuffer);
|
2011-03-14 09:38:29 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|