dolphin/Source/Plugins/Plugin_VideoDX11/Src/VertexShaderCache.cpp
NeoBrainX 3ea2a1387c Add a DX11 video plugin.
Might or might not work for you, yet.
Anyway, read the soon to be created forum thread on this plugin before asking any questions.

Huge thanks to rodolfoosvaldobogado for helping me out in various areas.
Also, thanks to everyone on IRC who supported me during development ;)


git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5680 8ced0084-cf51-0410-be5f-012b33b47a6e
2010-06-13 19:50:06 +00:00

268 lines
9 KiB
C++

// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include <map>
#include "Common.h"
#include "FileUtil.h"
#include "LinearDiskCache.h"
#include "Globals.h"
#include "D3DBase.h"
#include "D3DShader.h"
#include "Statistics.h"
#include "Profiler.h"
#include "VideoConfig.h"
#include "VertexShaderCache.h"
#include "VertexLoader.h"
#include "BPMemory.h"
#include "XFMemory.h"
VertexShaderCache::VSCache VertexShaderCache::vshaders;
const VertexShaderCache::VSCacheEntry* VertexShaderCache::last_entry;
static ID3D11VertexShader* SimpleVertexShader = NULL;
static ID3D11VertexShader* ClearVertexShader = NULL;
static ID3D11InputLayout* SimpleLayout = NULL;
static ID3D11InputLayout* ClearLayout = NULL;
LinearDiskCache g_vs_disk_cache;
ID3D11VertexShader* VertexShaderCache::GetSimpleVertexShader() { return SimpleVertexShader; }
ID3D11VertexShader* VertexShaderCache::GetClearVertexShader() { return ClearVertexShader; }
ID3D11InputLayout* VertexShaderCache::GetSimpleInputLayout() { return SimpleLayout; }
ID3D11InputLayout* VertexShaderCache::GetClearInputLayout() { return ClearLayout; }
// maps the constant numbers to float indices in the constant buffer
unsigned int vs_constant_offset_table[238];
void SetVSConstant4f(unsigned int const_number, float f1, float f2, float f3, float f4)
{
D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number] ] = f1;
D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]+1] = f2;
D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]+2] = f3;
D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]+3] = f4;
D3D::gfxstate->vscbufchanged = true;
}
void SetVSConstant4fv(unsigned int const_number, const float* f)
{
memcpy(&D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]], f, sizeof(float)*4);
D3D::gfxstate->vscbufchanged = true;
}
void SetMultiVSConstant3fv(unsigned int const_number, unsigned int count, const float* f)
{
for (unsigned int i = 0; i < count; i++)
{
memcpy(&D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number+i]], f+3*i, sizeof(float)*3);
D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number+i]+3] = 0.f;
}
}
void SetMultiVSConstant4fv(unsigned int const_number, unsigned int count, const float* f)
{
memcpy(&D3D::gfxstate->vsconstants[vs_constant_offset_table[const_number]], f, sizeof(float)*4*count);
D3D::gfxstate->vscbufchanged = true;
}
// this class will load the precompiled shaders into our cache
class VertexShaderCacheInserter : public LinearDiskCacheReader {
public:
void Read(const u8* key, int key_size, const u8* value, int value_size)
{
VERTEXSHADERUID uid;
if (key_size != sizeof(uid))
{
ERROR_LOG(VIDEO, "Wrong key size in vertex shader cache");
return;
}
memcpy(&uid, key, key_size);
ID3D10Blob* blob;
D3D10CreateBlob(value_size, &blob);
memcpy(blob->GetBufferPointer(), value, value_size);
VertexShaderCache::InsertByteCode(uid, blob);
blob->Release();
}
};
const char simple_shader_code[] = {
"struct VSOUTPUT\n"
"{\n"
"float4 vPosition : POSITION;\n"
"float2 vTexCoord : TEXCOORD0;\n"
"};\n"
"VSOUTPUT main(float4 inPosition : POSITION,float2 inTEX0 : TEXCOORD0)\n"
"{\n"
"VSOUTPUT OUT;\n"
"OUT.vPosition = inPosition;\n"
"OUT.vTexCoord = inTEX0;\n"
"return OUT;\n"
"}\n"
};
const char clear_shader_code[] = {
"struct VSOUTPUT\n"
"{\n"
"float4 vPosition : POSITION;\n"
"float4 vColor0 : COLOR0;\n"
"};\n"
"VSOUTPUT main(float4 inPosition : POSITION,float4 inColor0: COLOR0)\n"
"{\n"
"VSOUTPUT OUT;\n"
"OUT.vPosition = inPosition;\n"
"OUT.vColor0 = inColor0;\n"
"return OUT;\n"
"}\n"
};
void VertexShaderCache::Init()
{
const D3D11_INPUT_ELEMENT_DESC simpleelems[2] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
const D3D11_INPUT_ELEMENT_DESC clearelems[2] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
ID3D10Blob* blob;
D3D::CompileVertexShader(simple_shader_code, strlen(simple_shader_code), &blob);
D3D::device->CreateInputLayout(simpleelems, 2, blob->GetBufferPointer(), blob->GetBufferSize(), &SimpleLayout);
SimpleVertexShader = D3D::CreateVertexShaderFromByteCode(blob);
if (SimpleLayout == NULL || SimpleVertexShader == NULL) PanicAlert("Failed to create simple vertex shader or input layout at %s %d\n", __FILE__, __LINE__);
blob->Release();
D3D::CompileVertexShader(clear_shader_code, (int)strlen(clear_shader_code), &blob);
D3D::device->CreateInputLayout(clearelems, 2, blob->GetBufferPointer(), blob->GetBufferSize(), &ClearLayout);
ClearVertexShader = D3D::CreateVertexShaderFromByteCode(blob);
if (ClearLayout == NULL || ClearVertexShader == NULL) PanicAlert("Failed to create clear vertex shader or input layout at %s %d\n", __FILE__, __LINE__);
blob->Release();
Clear();
// these values are hardcoded, they depend on internal D3DCompile behavior
// TODO: Do this with D3DReflect or something instead
unsigned int k;
for (k = 0;k < 64;k++) vs_constant_offset_table[C_TRANSFORMMATRICES+k] = 312+4*k;
for (k = 0;k < 24;k++) vs_constant_offset_table[C_TEXMATRICES+k] = 216+4*k;
for (k = 0;k < 32;k++) vs_constant_offset_table[C_NORMALMATRICES+k] = 568+4*k;
for (k = 0;k < 6;k++) vs_constant_offset_table[C_POSNORMALMATRIX+k] = 0+4*k;
for (k = 0;k < 64;k++) vs_constant_offset_table[C_POSTTRANSFORMMATRICES+k] = 696+4*k;
for (k = 0;k < 40;k++) vs_constant_offset_table[C_LIGHTS+k] = 56+4*k;
for (k = 0;k < 4;k++) vs_constant_offset_table[C_MATERIALS+k] = 40+4*k;
for (k = 0;k < 4;k++) vs_constant_offset_table[C_PROJECTION+k] = 24+4*k;
if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX)))
File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX));
char cache_filename[MAX_PATH];
sprintf(cache_filename, "%sdx11-%s-vs.cache", File::GetUserPath(D_SHADERCACHE_IDX), globals->unique_id);
VertexShaderCacheInserter inserter;
g_vs_disk_cache.OpenAndRead(cache_filename, &inserter);
}
void VertexShaderCache::Clear()
{
VSCache::iterator iter = vshaders.begin();
for (; iter != vshaders.end(); ++iter)
iter->second.Destroy();
vshaders.clear();
}
void VertexShaderCache::Shutdown()
{
SAFE_RELEASE(SimpleVertexShader);
SAFE_RELEASE(ClearVertexShader);
SAFE_RELEASE(SimpleLayout);
SAFE_RELEASE(ClearLayout);
Clear();
g_vs_disk_cache.Sync();
g_vs_disk_cache.Close();
}
bool VertexShaderCache::SetShader(u32 components)
{
DVSTARTPROFILE();
VERTEXSHADERUID uid;
GetVertexShaderId(&uid, components);
if (uid == last_vertex_shader_uid && vshaders[uid].frameCount == frameCount)
return (vshaders[uid].shader != NULL);
memcpy(&last_vertex_shader_uid, &uid, sizeof(VERTEXSHADERUID));
VSCache::iterator iter;
iter = vshaders.find(uid);
if (iter != vshaders.end())
{
iter->second.frameCount = frameCount;
const VSCacheEntry &entry = iter->second;
last_entry = &entry;
if (entry.shader) D3D::gfxstate->SetVShader(entry.shader, iter->second.bytecode);
return (entry.shader != NULL);
}
const char* code = GenerateVertexShaderCode(components, API_D3D11);
ID3D10Blob* pbytecode = NULL;
D3D::CompileVertexShader(code, (int)strlen(code), &pbytecode);
if (pbytecode == NULL)
{
PanicAlert("Failed to compile Vertex Shader %s %d:\n\n%s", __FILE__, __LINE__, code);
return false;
}
g_vs_disk_cache.Append((u8*)&uid, sizeof(uid), (const u8*)pbytecode->GetBufferPointer(), pbytecode->GetBufferSize());
g_vs_disk_cache.Sync();
bool result = InsertByteCode(uid, pbytecode);
D3D::gfxstate->SetVShader(last_entry->shader, last_entry->bytecode);
pbytecode->Release();
return result;
}
bool VertexShaderCache::InsertByteCode(const VERTEXSHADERUID &uid, ID3D10Blob* bcodeblob)
{
ID3D11VertexShader* shader = D3D::CreateVertexShaderFromByteCode(bcodeblob);
if (shader == NULL)
{
PanicAlert("Failed to create vertex shader from %p size %d at %s %d\n", bcodeblob->GetBufferPointer(), bcodeblob->GetBufferSize(), __FILE__, __LINE__);
return false;
}
// Make an entry in the table
VSCacheEntry entry;
entry.shader = shader;
entry.frameCount = frameCount;
entry.SetByteCode(bcodeblob);
vshaders[uid] = entry;
last_entry = &vshaders[uid];
INCSTAT(stats.numVertexShadersCreated);
SETSTAT(stats.numVertexShadersAlive, (int)vshaders.size());
return true;
}