Shader Disk Cache implementation for D3D. Saves generated shaders on disk. Eliminates "freeze jerks" in D3D plugin the _second_ and later times you play something.... not much to do about the first time. The D3D shader compiler is just slow.

Also assorted cleanup around the shader code.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4869 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
hrydgard 2010-01-17 17:44:09 +00:00
parent f599fdcec5
commit 3e01152793
24 changed files with 551 additions and 265 deletions

View File

@ -720,6 +720,14 @@
RelativePath=".\Src\IniFile.h"
>
</File>
<File
RelativePath=".\Src\LinearDiskCache.cpp"
>
</File>
<File
RelativePath=".\Src\LinearDiskCache.h"
>
</File>
<File
RelativePath=".\Src\MathUtil.cpp"
>

View File

@ -66,6 +66,7 @@
#define GAMECONFIG_DIR "GameConfig"
#define MAPS_DIR "Maps"
#define CACHE_DIR "Cache"
#define SHADERCACHE_DIR "ShaderCache"
#define STATESAVES_DIR "StateSaves"
#define SCREENSHOTS_DIR "ScreenShots"
#define DUMP_DIR "Dump"
@ -128,6 +129,7 @@
#define FULL_CONFIG_DIR FULL_USERDATA_DIR CONFIG_DIR DIR_SEP
#define FULL_CACHE_DIR FULL_USERDATA_DIR CACHE_DIR DIR_SEP
#define FULL_SHADERCACHE_DIR FULL_USERDATA_DIR SHADERCACHE_DIR DIR_SEP
#define FULL_STATESAVES_DIR FULL_USERDATA_DIR STATESAVES_DIR DIR_SEP
#define FULL_SCREENSHOTS_DIR FULL_USERDATA_DIR SCREENSHOTS_DIR DIR_SEP
#define FULL_FRAMES_DIR FULL_USERDATA_DIR DUMP_DIR DIR_SEP DUMP_FRAMES_DIR

View File

@ -0,0 +1,149 @@
// 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 "LinearDiskCache.h"
static const char ID[4] = {'D', 'C', 'A', 'C'};
const int version = 1; // TODO: Get from SVN_REV
LinearDiskCache::LinearDiskCache()
: file_(NULL), num_entries_(0) {
}
void LinearDiskCache::WriteHeader() {
fwrite(ID, 4, 1, file_);
fwrite(&version, 4, 1, file_);
}
bool LinearDiskCache::ValidateHeader() {
char header_id[4];
int header_version;
fread(&header_id, 4, 1, file_);
fread(&header_version, 4, 1, file_);
if (memcmp(header_id, ID, 4) != 0)
return false;
if (header_version != version)
return false;
return true;
}
int LinearDiskCache::OpenAndRead(const char *filename, LinearDiskCacheReader *reader) {
int items_read_count = 0;
file_ = fopen(filename, "rb");
int file_size = 0;
if (file_) {
fseek(file_, 0, SEEK_END);
file_size = (int)ftell(file_);
}
bool file_corrupt = false;
if (file_size == 0) {
if (file_)
fclose(file_);
// Reopen for writing.
file_ = fopen(filename, "wb");
// Cache empty, let's initialize a header.
WriteHeader();
num_entries_ = 0;
} else {
// file_ must be != 0 here.
// Back to the start we go.
fseek(file_, 0, SEEK_SET);
// Check that the file is valid
if (!ValidateHeader()) {
// Not valid - delete the file and start over.
fclose(file_);
unlink(filename);
PanicAlert("LinearDiskCache file header broken.");
file_ = fopen(filename, "wb");
WriteHeader();
num_entries_ = 0;
} else {
// Valid - blow through it.
// We're past the header already thanks to ValidateHeader.
while (!feof(file_)) {
int key_size, value_size;
int key_size_size = fread(&key_size, 1, sizeof(key_size), file_);
int value_size_size = fread(&value_size, 1, sizeof(value_size), file_);
if (key_size_size == 0 && value_size_size == 0) {
// I guess feof isn't doing it's job - we're at the end.
break;
}
if (key_size <= 0 || value_size < 0 || key_size_size != 4 || value_size_size != 4) {
PanicAlert("Disk cache file %s corrupted/truncated! ks: %i vs %i kss %i vss %i", filename,
key_size, value_size, key_size_size, value_size_size);
file_corrupt = true;
break;
}
u8 *key = new u8[key_size];
u8 *value = new u8[value_size];
int actual_key_size = (int)fread(key, 1, key_size, file_);
int actual_value_size = (int)fread(value, 1, value_size, file_);
if (actual_key_size != key_size || actual_value_size != value_size) {
PanicAlert("Disk cache file %s corrupted/truncated! ks: %i actual ks: %i vs: %i actual vs: %i", filename,
key_size, actual_key_size, value_size, actual_value_size);
file_corrupt = true;
} else {
reader->Read(key, key_size, value, value_size);
items_read_count++;
}
delete [] key;
delete [] value;
}
fclose(file_);
// Done reading.
// Reopen file for append.
// At this point, ftell() will be at the end of the file,
// which happens to be exactly what we want.
file_ = fopen(filename, "ab");
fseek(file_, 0, SEEK_END);
}
}
if (file_corrupt) {
// Restore sanity, start over.
fclose(file_);
unlink(filename);
file_ = fopen(filename, "wb+");
WriteHeader();
}
return items_read_count;
}
void LinearDiskCache::Append(
const u8 *key, int key_size, const u8 *value, int value_size) {
// Should do a check that we don't already have "key"?
fwrite(&key_size, 1, sizeof(key_size), file_);
fwrite(&value_size, 1, sizeof(value_size), file_);
fwrite(key, 1, key_size, file_);
fwrite(value, 1, value_size, file_);
}
void LinearDiskCache::Sync() {
fflush(file_);
}
void LinearDiskCache::Close() {
fclose(file_);
file_ = 0;
num_entries_ = 0;
}

View File

@ -0,0 +1,67 @@
// 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/
#ifndef _LINEAR_DISKCACHE
#define _LINEAR_DISKCACHE
#include "Common.h"
#include <stdlib.h>
#include <stdio.h>
// On disk format:
// uint32 'DCAC'
// uint32 version; // svn_rev
// uint32 key_length;
// uint32 value_length;
// .... key;
// .... value;
class LinearDiskCacheReader {
public:
virtual void Read(const u8 *key, int key_size, const u8 *value, int value_size) = 0;
};
// Dead simple unsorted key-value store with append functionality.
// No random read functionality, all reading is done in OpenAndRead.
// Keys and values can contain any characters, including \0.
//
// Suitable for caching generated shader bytecode between executions.
// Not tuned for extreme performance but should be reasonably fast.
// Does not support keys or values larger than 2GB, which should be reasonable.
// Keys must have non-zero length; values can have zero length.
class LinearDiskCache {
public:
LinearDiskCache();
// Returns the number of items read from the cache.
int OpenAndRead(const char *filename, LinearDiskCacheReader *reader);
void Close();
void Sync();
// Appends a key-value pair to the store.
void Append(const u8 *key, int key_size, const u8 *value, int value_size);
private:
void WriteHeader();
bool ValidateHeader();
FILE *file_;
int num_entries_;
};
#endif // _LINEAR_DISKCACHE

View File

@ -232,8 +232,8 @@ inline u32 ConvertToSingle(u64 x)
}
else
{
// this is said to be undefined
// based on hardware tests
// This is said to be undefined.
// The code is based on hardware tests.
return ((x >> 32) & 0xc0000000) | ((x >> 29) & 0x3fffffff);
}
}

View File

@ -31,7 +31,7 @@ PIXELSHADERUID last_pixel_shader_uid;
// a unique identifier, basically containing all the bits. Yup, it's a lot ....
// It would likely be a lot more efficient to build this incrementally as the attributes
// are set...
void GetPixelShaderId(PIXELSHADERUID &uid, u32 texturemask, u32 dstAlphaEnable)
void GetPixelShaderId(PIXELSHADERUID *uid, u32 texturemask, u32 dstAlphaEnable)
{
u32 projtexcoords = 0;
for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; i++)
@ -43,7 +43,7 @@ void GetPixelShaderId(PIXELSHADERUID &uid, u32 texturemask, u32 dstAlphaEnable)
projtexcoords |= 1 << texcoord;
}
}
uid.values[0] = (u32)bpmem.genMode.numtevstages |
uid->values[0] = (u32)bpmem.genMode.numtevstages |
((u32)bpmem.genMode.numindstages << 4) |
((u32)bpmem.genMode.numtexgens << 7) |
((u32)dstAlphaEnable << 11) |
@ -51,21 +51,21 @@ void GetPixelShaderId(PIXELSHADERUID &uid, u32 texturemask, u32 dstAlphaEnable)
(projtexcoords << 20) |
((u32)bpmem.ztex2.op << 28);
uid.values[0] = (uid.values[0] & ~0x0ff00000) | (projtexcoords << 20);
uid->values[0] = (uid->values[0] & ~0x0ff00000) | (projtexcoords << 20);
// swap table
for (int i = 0; i < 8; i += 2)
((u8*)&uid.values[1])[i / 2] = (bpmem.tevksel[i].hex & 0xf) | ((bpmem.tevksel[i + 1].hex & 0xf) << 4);
((u8*)&uid->values[1])[i / 2] = (bpmem.tevksel[i].hex & 0xf) | ((bpmem.tevksel[i + 1].hex & 0xf) << 4);
uid.values[2] = texturemask;
uid->values[2] = texturemask;
u32 enableZTexture = (!bpmem.zcontrol.zcomploc && bpmem.zmode.testenable && bpmem.zmode.updateenable)?1:0;
uid.values[3] = (u32)bpmem.fog.c_proj_fsel.fsel |
uid->values[3] = (u32)bpmem.fog.c_proj_fsel.fsel |
((u32)bpmem.fog.c_proj_fsel.proj << 3) |
((u32)enableZTexture << 4);
int hdr = 4;
u32* pcurvalue = &uid.values[hdr];
u32 *pcurvalue = &uid->values[hdr];
for (u32 i = 0; i < (u32)bpmem.genMode.numtevstages + 1; ++i)
{
TevStageCombiner::ColorCombiner &cc = bpmem.combiners[i].colorC;
@ -119,7 +119,7 @@ void GetPixelShaderId(PIXELSHADERUID &uid, u32 texturemask, u32 dstAlphaEnable)
if ((bpmem.genMode.numtevstages % 3) != 2)
++pcurvalue;
uid.tevstages = (u32)(pcurvalue - &uid.values[0] - hdr);
uid->tevstages = (u32)(pcurvalue - &uid->values[0] - hdr);
for (u32 i = 0; i < bpmem.genMode.numindstages; ++i)
{
@ -134,7 +134,7 @@ void GetPixelShaderId(PIXELSHADERUID &uid, u32 texturemask, u32 dstAlphaEnable)
}
// yeah, well ....
uid.indstages = (u32)(pcurvalue - &uid.values[0] - (hdr - 1) - uid.tevstages);
uid->indstages = (u32)(pcurvalue - &uid->values[0] - (hdr - 1) - uid->tevstages);
}
// old tev->pixelshader notes
@ -385,7 +385,7 @@ static void BuildSwapModeTable()
}
}
const char *GeneratePixelShader(u32 texture_mask, bool dstAlphaEnable, u32 HLSL)
const char *GeneratePixelShaderCode(u32 texture_mask, bool dstAlphaEnable, u32 HLSL)
{
setlocale(LC_NUMERIC, "C"); // Reset locale for compilation
text[sizeof(text) - 1] = 0x7C; // canary

View File

@ -42,6 +42,7 @@
#define C_COLORMATRIX (C_FOG + 2)
#define PIXELSHADERUID_MAX_VALUES (5 + 32 + 6 + 11)
// DO NOT make anything in this class virtual.
class PIXELSHADERUID
{
public:
@ -100,8 +101,9 @@ public:
}
};
const char *GeneratePixelShader(u32 texture_mask, bool dstAlphaEnable, u32 HLSL = 0);
void GetPixelShaderId(PIXELSHADERUID &, u32 texturemask, u32 dstAlphaEnable);
const char *GeneratePixelShaderCode(u32 texture_mask, bool dstAlphaEnable, u32 HLSL = 0);
void GetPixelShaderId(PIXELSHADERUID *uid, u32 texturemask, u32 dstAlphaEnable);
extern PIXELSHADERUID last_pixel_shader_uid;
#endif // GCOGL_PIXELSHADER_H

View File

@ -29,27 +29,27 @@ VERTEXSHADERUID last_vertex_shader_uid;
// Mash together all the inputs that contribute to the code of a generated vertex shader into
// a unique identifier, basically containing all the bits. Yup, it's a lot ....
void GetVertexShaderId(VERTEXSHADERUID& vid, u32 components)
void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components)
{
vid.values[0] = components |
uid->values[0] = components |
(xfregs.numTexGens << 23) |
(xfregs.nNumChans << 27) |
((u32)xfregs.bEnableDualTexTransform << 29);
for (int i = 0; i < 2; ++i) {
vid.values[1+i] = xfregs.colChans[i].color.enablelighting ?
uid->values[1+i] = xfregs.colChans[i].color.enablelighting ?
(u32)xfregs.colChans[i].color.hex :
(u32)xfregs.colChans[i].color.matsource;
vid.values[1+i] |= (xfregs.colChans[i].alpha.enablelighting ?
uid->values[1+i] |= (xfregs.colChans[i].alpha.enablelighting ?
(u32)xfregs.colChans[i].alpha.hex :
(u32)xfregs.colChans[i].alpha.matsource) << 15;
}
// fog
vid.values[1] |= (((u32)bpmem.fog.c_proj_fsel.fsel & 3) << 30);
vid.values[2] |= (((u32)bpmem.fog.c_proj_fsel.fsel >> 2) << 30);
uid->values[1] |= (((u32)bpmem.fog.c_proj_fsel.fsel & 3) << 30);
uid->values[2] |= (((u32)bpmem.fog.c_proj_fsel.fsel >> 2) << 30);
u32* pcurvalue = &vid.values[3];
u32 *pcurvalue = &uid->values[3];
for (int i = 0; i < xfregs.numTexGens; ++i) {
TexMtxInfo tinfo = xfregs.texcoords[i].texmtxinfo;
if (tinfo.texgentype != XF_TEXGEN_EMBOSS_MAP)
@ -80,7 +80,7 @@ static char text[16384];
char *GenerateLightShader(char *p, int index, const LitChannel& chan, const char *dest, int coloralpha);
const char *GenerateVertexShader(u32 components, bool D3D)
const char *GenerateVertexShaderCode(u32 components, bool D3D)
{
setlocale(LC_NUMERIC, "C"); // Reset locale for compilation
text[sizeof(text) - 1] = 0x7C; // canary
@ -125,7 +125,6 @@ const char *GenerateVertexShader(u32 components, bool D3D)
WRITE(p, "};\n");
// uniforms
// bool bTexMtx = ((components & VB_HAS_TEXMTXIDXALL)<<VB_HAS_UVTEXMTXSHIFT)!=0; unused TODO: keep?
WRITE(p, "uniform s_"I_TRANSFORMMATRICES" "I_TRANSFORMMATRICES" : register(c%d);\n", C_TRANSFORMMATRICES);
WRITE(p, "uniform s_"I_TEXMATRICES" "I_TEXMATRICES" : register(c%d);\n", C_TEXMATRICES); // also using tex matrices

View File

@ -99,8 +99,10 @@ public:
}
};
const char *GenerateVertexShader(u32 components, bool D3D);
void GetVertexShaderId(VERTEXSHADERUID& vid, u32 components);
// components is included in the uid.
const char *GenerateVertexShaderCode(u32 components, bool D3D);
void GetVertexShaderId(VERTEXSHADERUID *uid, u32 components);
extern VERTEXSHADERUID last_vertex_shader_uid;
#endif // GCOGL_VERTEXSHADER_H

View File

@ -112,7 +112,11 @@ struct Resolution
struct AALevel
{
AALevel(const char *n, D3DMULTISAMPLE_TYPE m, int q) {strcpy(name, n); ms_setting=m; qual_setting=q;}
AALevel(const char *n, D3DMULTISAMPLE_TYPE m, int q) {
strcpy(name, n);
ms_setting = m;
qual_setting = q;
}
char name[32];
D3DMULTISAMPLE_TYPE ms_setting;
int qual_setting;

View File

@ -24,12 +24,22 @@
namespace D3D
{
LPDIRECT3DVERTEXSHADER9 CompileVertexShader(const char *code, int len)
// Bytecode->shader.
LPDIRECT3DVERTEXSHADER9 CreateVertexShaderFromByteCode(const u8 *bytecode, int len)
{
LPDIRECT3DVERTEXSHADER9 v_shader;
HRESULT hr = D3D::dev->CreateVertexShader((DWORD *)bytecode, &v_shader);
if (FAILED(hr))
v_shader = 0;
return v_shader;
}
// Code->bytecode.
bool CompileVertexShader(const char *code, int len, u8 **bytecode, int *bytecodelen)
{
//try to compile
LPD3DXBUFFER shaderBuffer = 0;
LPD3DXBUFFER errorBuffer = 0;
LPDIRECT3DVERTEXSHADER9 vShader = 0;
HRESULT hr = D3DXCompileShader(code, len, 0, 0, "main", D3D::VertexShaderVersionString(),
0, &shaderBuffer, &errorBuffer, 0);
if (FAILED(hr))
@ -39,20 +49,16 @@ LPDIRECT3DVERTEXSHADER9 CompileVertexShader(const char *code, int len)
std::string hello = (char*)errorBuffer->GetBufferPointer();
hello += "\n\n";
hello += code;
MessageBoxA(0, hello.c_str(), "Error assembling vertex shader", MB_ICONERROR);
MessageBoxA(0, hello.c_str(), "Error compiling vertex shader", MB_ICONERROR);
}
vShader = 0;
*bytecode = 0;
*bytecodelen = 0;
}
else if (SUCCEEDED(hr))
{
//create it
HRESULT hr = E_FAIL;
if (shaderBuffer)
hr = D3D::dev->CreateVertexShader((DWORD *)shaderBuffer->GetBufferPointer(), &vShader);
if ((FAILED(hr) || vShader == 0) && g_ActiveConfig.bShowShaderErrors)
{
MessageBoxA(0, code, (char*)errorBuffer->GetBufferPointer(), MB_ICONERROR);
}
*bytecodelen = shaderBuffer->GetBufferSize();
*bytecode = new u8[*bytecodelen];
memcpy(*bytecode, shaderBuffer->GetBufferPointer(), *bytecodelen);
}
//cleanup
@ -60,14 +66,25 @@ LPDIRECT3DVERTEXSHADER9 CompileVertexShader(const char *code, int len)
shaderBuffer->Release();
if (errorBuffer)
errorBuffer->Release();
return vShader;
return SUCCEEDED(hr) ? true : false;
}
LPDIRECT3DPIXELSHADER9 CompilePixelShader(const char *code, int len)
// Bytecode->shader.
LPDIRECT3DPIXELSHADER9 CreatePixelShaderFromByteCode(const u8 *bytecode, int len)
{
LPDIRECT3DPIXELSHADER9 p_shader;
HRESULT hr = D3D::dev->CreatePixelShader((DWORD *)bytecode, &p_shader);
if (FAILED(hr))
p_shader = 0;
return p_shader;
}
bool CompilePixelShader(const char *code, int len, u8 **bytecode, int *bytecodelen)
{
LPD3DXBUFFER shaderBuffer = 0;
LPD3DXBUFFER errorBuffer = 0;
LPDIRECT3DPIXELSHADER9 pShader = 0;
// Someone:
// For some reason, I had this kind of errors : "Shader uses texture addressing operations
@ -81,18 +98,16 @@ LPDIRECT3DPIXELSHADER9 CompilePixelShader(const char *code, int len)
std::string hello = (char*)errorBuffer->GetBufferPointer();
hello += "\n\n";
hello += code;
MessageBoxA(0, hello.c_str(), "Error assembling pixel shader", MB_ICONERROR);
MessageBoxA(0, hello.c_str(), "Error compiling pixel shader", MB_ICONERROR);
}
pShader = 0;
*bytecode = 0;
*bytecodelen = 0;
}
else
else if (SUCCEEDED(hr))
{
//create it
HRESULT hr = D3D::dev->CreatePixelShader((DWORD *)shaderBuffer->GetBufferPointer(), &pShader);
if ((FAILED(hr) || pShader == 0) && g_ActiveConfig.bShowShaderErrors)
{
MessageBoxA(0, "damn", "error creating pixelshader", MB_ICONERROR);
}
*bytecodelen = shaderBuffer->GetBufferSize();
*bytecode = new u8[*bytecodelen];
memcpy(*bytecode, shaderBuffer->GetBufferPointer(), *bytecodelen);
}
//cleanup
@ -100,7 +115,31 @@ LPDIRECT3DPIXELSHADER9 CompilePixelShader(const char *code, int len)
shaderBuffer->Release();
if (errorBuffer)
errorBuffer->Release();
return pShader;
return SUCCEEDED(hr) ? true : false;
}
LPDIRECT3DVERTEXSHADER9 CompileAndCreateVertexShader(const char *code, int len) {
u8 *bytecode;
int bytecodelen;
if (CompileVertexShader(code, len, &bytecode, &bytecodelen)) {
LPDIRECT3DVERTEXSHADER9 v_shader = CreateVertexShaderFromByteCode(bytecode, len);
delete [] bytecode;
return v_shader;
} else {
return 0;
}
}
LPDIRECT3DPIXELSHADER9 CompileAndCreatePixelShader(const char *code, int len) {
u8 *bytecode;
int bytecodelen;
if (CompilePixelShader(code, len, &bytecode, &bytecodelen)) {
LPDIRECT3DPIXELSHADER9 p_shader = CreatePixelShaderFromByteCode(bytecode, len);
delete [] bytecode;
return p_shader;
} else {
return 0;
}
}
} // namespace

View File

@ -21,6 +21,14 @@
namespace D3D
{
LPDIRECT3DVERTEXSHADER9 CompileVertexShader(const char *code, int len);
LPDIRECT3DPIXELSHADER9 CompilePixelShader(const char *code, int len);
LPDIRECT3DVERTEXSHADER9 CreateVertexShaderFromByteCode(const u8 *bytecode, int len);
LPDIRECT3DPIXELSHADER9 CreatePixelShaderFromByteCode(const u8 *bytecode, int len);
// The returned bytecode buffers should be delete[]-d.
bool CompileVertexShader(const char *code, int len, u8 **bytecode, int *bytecodelen);
bool CompilePixelShader(const char *code, int len, u8 **bytecode, int *bytecodelen);
// Utility functions
LPDIRECT3DVERTEXSHADER9 CompileAndCreateVertexShader(const char *code, int len);
LPDIRECT3DPIXELSHADER9 CompileAndCreatePixelShader(const char *code, int len);
}

View File

@ -15,11 +15,15 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Common.h"
#include "FileUtil.h"
#include "LinearDiskCache.h"
#include "Globals.h"
#include "D3DBase.h"
#include "D3DShader.h"
#include "Statistics.h"
#include "Utils.h"
#include "Profiler.h"
#include "VideoConfig.h"
#include "PixelShaderGen.h"
#include "PixelShaderManager.h"
@ -29,10 +33,13 @@
#include "XFMemory.h"
#include "ImageWrite.h"
#include "debugger/debugger.h"
#include "Debugger/Debugger.h"
PixelShaderCache::PSCache PixelShaderCache::PixelShaders;
const PixelShaderCache::PSCacheEntry *PixelShaderCache::last_entry;
LinearDiskCache g_ps_disk_cache;
static float lastPSconstants[C_COLORMATRIX+16][4];
static LPDIRECT3DPIXELSHADER9 s_ColorMatrixProgram = 0;
@ -41,7 +48,6 @@ static LPDIRECT3DPIXELSHADER9 s_ClearProgram = 0;
static LPDIRECT3DPIXELSHADER9 s_ClearZProgram = 0;
static LPDIRECT3DPIXELSHADER9 s_DepthMatrixProgram = 0;
LPDIRECT3DPIXELSHADER9 PixelShaderCache::GetColorMatrixProgram()
{
return s_ColorMatrixProgram;
@ -89,6 +95,20 @@ void SetPSConstant4fv(int const_number, const float *f)
}
}
class PixelShaderCacheInserter : public LinearDiskCacheReader {
public:
void Read(const u8 *key, int key_size, const u8 *value, int value_size)
{
PIXELSHADERUID uid;
if (key_size != sizeof(uid)) {
ERROR_LOG(VIDEO, "Wrong key size in pixel shader cache");
return;
}
memcpy(&uid, key, key_size);
PixelShaderCache::InsertByteCode(uid, value, value_size, false);
}
};
void PixelShaderCache::Init()
{
char pprog[1024];
@ -97,7 +117,7 @@ void PixelShaderCache::Init()
" in float4 incol0 : COLOR0){\n"
"ocol0 = incol0;\n"
"}\n");
s_ClearProgram = D3D::CompilePixelShader(pprog, (int)strlen(pprog));
s_ClearProgram = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog));
sprintf(pprog, "uniform sampler samp0 : register(s0);\n"
"void main(\n"
@ -105,7 +125,7 @@ void PixelShaderCache::Init()
" in float3 uv0 : TEXCOORD0){\n"
"ocol0 = tex2D(samp0,uv0.xy);\n"
"}\n");
s_ColorCopyProgram = D3D::CompilePixelShader(pprog, (int)strlen(pprog));
s_ColorCopyProgram = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog));
sprintf(pprog, "uniform sampler samp0 : register(s0);\n"
"uniform float4 cColMatrix[5] : register(c%d);\n"
@ -115,7 +135,7 @@ void PixelShaderCache::Init()
"float4 texcol = tex2D(samp0,uv0.xy);\n"
"ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n"
"}\n",C_COLORMATRIX);
s_ColorMatrixProgram = D3D::CompilePixelShader(pprog, (int)strlen(pprog));
s_ColorMatrixProgram = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog));
sprintf(pprog, "uniform sampler samp0 : register(s0);\n"
"uniform float4 cColMatrix[5] : register(c%d);\n"
@ -127,10 +147,19 @@ void PixelShaderCache::Init()
"texcol = float4((EncodedDepth.rgb * (16777216.0f/16777215.0f)),1.0f);\n"
"ocol0 = float4(dot(texcol,cColMatrix[0]),dot(texcol,cColMatrix[1]),dot(texcol,cColMatrix[2]),dot(texcol,cColMatrix[3])) + cColMatrix[4];\n"
"}\n",C_COLORMATRIX);
s_DepthMatrixProgram = D3D::CompilePixelShader(pprog, (int)strlen(pprog));
s_DepthMatrixProgram = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog));
Clear();
if (!File::Exists(FULL_SHADERCACHE_DIR))
File::CreateDir(FULL_SHADERCACHE_DIR);
char cache_filename[MAX_PATH];
sprintf(cache_filename, "%s%s-ps.cache", FULL_SHADERCACHE_DIR, globals->unique_id);
PixelShaderCacheInserter inserter;
int read_items = g_ps_disk_cache.OpenAndRead(cache_filename, &inserter);
}
// ONLY to be used during shutdown.
void PixelShaderCache::Clear()
{
PSCache::iterator iter = PixelShaders.begin();
@ -145,39 +174,38 @@ void PixelShaderCache::Clear()
void PixelShaderCache::Shutdown()
{
if(s_ColorMatrixProgram)
s_ColorMatrixProgram->Release();
if (s_ColorMatrixProgram) s_ColorMatrixProgram->Release();
s_ColorMatrixProgram = NULL;
if(s_ColorCopyProgram)
s_ColorCopyProgram->Release();
if (s_ColorCopyProgram) s_ColorCopyProgram->Release();
s_ColorCopyProgram = NULL;
if(s_DepthMatrixProgram)
s_DepthMatrixProgram->Release();
if (s_DepthMatrixProgram) s_DepthMatrixProgram->Release();
s_DepthMatrixProgram = NULL;
if(s_ClearProgram)
s_ClearProgram->Release();
if (s_ClearProgram) s_ClearProgram->Release();
s_ClearProgram = NULL;
Clear();
g_ps_disk_cache.Sync();
g_ps_disk_cache.Close();
}
bool PixelShaderCache::SetShader(bool dstAlpha)
{
DVSTARTPROFILE();
PIXELSHADERUID uid;
GetPixelShaderId(uid, PixelShaderManager::GetTextureMask(), dstAlpha);
GetPixelShaderId(&uid, PixelShaderManager::GetTextureMask(), dstAlpha);
// Is the shader already set?
if (uid == last_pixel_shader_uid && PixelShaders[uid].frameCount == frameCount)
{
PSCache::const_iterator iter = PixelShaders.find(uid);
if (iter != PixelShaders.end() && iter->second.shader)
return true;
return true; // Sure, we're done.
else
return false;
return false; // ?? something is wrong.
}
memcpy(&last_pixel_shader_uid, &uid, sizeof(PIXELSHADERUID));
// Is the shader already in the cache?
PSCache::iterator iter;
iter = PixelShaders.find(uid);
if (iter != PixelShaders.end())
@ -186,7 +214,6 @@ bool PixelShaderCache::SetShader(bool dstAlpha)
const PSCacheEntry &entry = iter->second;
last_entry = &entry;
DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE,true);
if (entry.shader)
@ -198,7 +225,8 @@ bool PixelShaderCache::SetShader(bool dstAlpha)
return false;
}
const char *code = GeneratePixelShader(PixelShaderManager::GetTextureMask(), dstAlpha, 2);
// OK, need to generate and compile it.
const char *code = GeneratePixelShaderCode(PixelShaderManager::GetTextureMask(), dstAlpha, 2);
#if defined(_DEBUG) || defined(DEBUGFAST)
if (g_ActiveConfig.iLog & CONF_SAVESHADERS && code) {
static int counter = 0;
@ -208,7 +236,29 @@ bool PixelShaderCache::SetShader(bool dstAlpha)
SaveData(szTemp, code);
}
#endif
LPDIRECT3DPIXELSHADER9 shader = D3D::CompilePixelShader(code, (int)strlen(code));
u8 *bytecode = 0;
int bytecodelen = 0;
if (!D3D::CompilePixelShader(code, (int)strlen(code), &bytecode, &bytecodelen)) {
if (g_ActiveConfig.bShowShaderErrors)
{
PanicAlert("Failed to compile Pixel Shader:\n\n%s", code);
}
return false;
}
// Here we have the UID and the byte code. Insert it into the disk cache.
g_ps_disk_cache.Append((u8 *)&uid, sizeof(uid), bytecode, bytecodelen);
g_ps_disk_cache.Sync();
// And insert it into the shader cache.
bool result = InsertByteCode(uid, bytecode, bytecodelen, true);
delete [] bytecode;
return result;
}
bool PixelShaderCache::InsertByteCode(const PIXELSHADERUID &uid, const u8 *bytecode, int bytecodelen, bool activate) {
LPDIRECT3DPIXELSHADER9 shader = D3D::CreatePixelShaderFromByteCode(bytecode, bytecodelen);
// Make an entry in the table
PSCacheEntry newentry;
@ -220,43 +270,20 @@ bool PixelShaderCache::SetShader(bool dstAlpha)
PixelShaders[uid] = newentry;
last_entry = &PixelShaders[uid];
INCSTAT(stats.numPixelShadersCreated);
SETSTAT(stats.numPixelShadersAlive, (int)PixelShaders.size());
if (shader)
{
D3D::SetPixelShader(shader);
return true;
}
if (g_ActiveConfig.bShowShaderErrors)
{
PanicAlert("Failed to compile Pixel Shader:\n\n%s", code);
}
if (!shader) {
// INCSTAT(stats.numPixelShadersFailed);
return false;
}
void PixelShaderCache::Cleanup()
{
/*
PSCache::iterator iter;
iter = PixelShaders.begin();
while (iter != PixelShaders.end())
{
PSCacheEntry &entry = iter->second;
if (entry.frameCount < frameCount - 1400)
{
entry.Destroy();
iter = PixelShaders.erase(iter);
}
else
{
iter++;
}
}
INCSTAT(stats.numPixelShadersCreated);
SETSTAT(stats.numPixelShadersAlive, (int)PixelShaders.size());
*/
if (activate)
{
D3D::SetPixelShader(shader);
}
return true;
}
#if defined(_DEBUG) || defined(DEBUGFAST)
std::string PixelShaderCache::GetCurrentShaderCode()

View File

@ -18,6 +18,8 @@
#ifndef _PIXELSHADERCACHE_H
#define _PIXELSHADERCACHE_H
#include "Common.h"
#include "LinearDiskCache.h"
#include "D3DBase.h"
#include <map>
@ -53,12 +55,13 @@ private:
static PSCache PixelShaders;
static const PSCacheEntry *last_entry;
public:
static void Init();
static void Cleanup();
static void Clear();
static void Shutdown();
static bool SetShader(bool dstAlpha);
static bool InsertByteCode(const PIXELSHADERUID &uid, const u8 *bytecode, int bytecodelen, bool activate);
static LPDIRECT3DPIXELSHADER9 GetColorMatrixProgram();
static LPDIRECT3DPIXELSHADER9 GetColorCopyProgram();
static LPDIRECT3DPIXELSHADER9 GetDepthMatrixProgram();

View File

@ -301,6 +301,7 @@ bool Renderer::Init()
for (int stage = 0; stage < 8; stage++)
D3D::SetSamplerState(stage, D3DSAMP_MAXANISOTROPY, g_ActiveConfig.iMaxAnisotropy);
D3DVIEWPORT9 vp;
vp.X = 0;
vp.Y = 0;
@ -978,8 +979,6 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight)
// D3D frame is now over
// Clean out old stuff from caches.
frameCount++;
PixelShaderCache::Cleanup();
VertexShaderCache::Cleanup();
TextureCache::Cleanup();
// Make any new configuration settings active.

View File

@ -75,7 +75,7 @@ void CreateRgbToYuyvProgram()
" float3 c01 = (c0 + c1) * 0.5f;\n"
" ocol0 = float4(dot(c1,y_const),dot(c01,u_const),dot(c0,y_const),dot(c01, v_const)) + const3;\n"
"}\n";
s_rgbToYuyvProgram = D3D::CompilePixelShader(FProgram, (int)strlen(FProgram));
s_rgbToYuyvProgram = D3D::CompileAndCreatePixelShader(FProgram, (int)strlen(FProgram));
if (!s_rgbToYuyvProgram) {
ERROR_LOG(VIDEO, "Failed to create RGB to YUYV fragment program");
}
@ -102,7 +102,7 @@ void CreateYuyvToRgbProgram()
" yComp + (2.018f * uComp),\n"
" 1.0f);\n"
"}\n";
s_yuyvToRgbProgram = D3D::CompilePixelShader(FProgram, (int)strlen(FProgram));
s_yuyvToRgbProgram = D3D::CompileAndCreatePixelShader(FProgram, (int)strlen(FProgram));
if (!s_yuyvToRgbProgram) {
ERROR_LOG(VIDEO, "Failed to create YUYV to RGB fragment program");
}
@ -129,7 +129,7 @@ LPDIRECT3DPIXELSHADER9 GetOrCreateEncodingShader(u32 format)
SaveData(szTemp, shader);
}
#endif
s_encodingPrograms[format] = D3D::CompilePixelShader(shader, (int)strlen(shader));
s_encodingPrograms[format] = D3D::CompileAndCreatePixelShader(shader, (int)strlen(shader));
if (!s_encodingPrograms[format]) {
ERROR_LOG(VIDEO, "Failed to create encoding fragment program");
}

View File

@ -17,6 +17,11 @@
#include <map>
#include "Common.h"
#include "FileUtil.h"
#include "LinearDiskCache.h"
#include "Globals.h"
#include "D3DBase.h"
#include "D3DShader.h"
#include "Statistics.h"
@ -36,6 +41,7 @@ const VertexShaderCache::VSCacheEntry *VertexShaderCache::last_entry;
static float GC_ALIGNED16(lastVSconstants[C_FOGPARAMS + 8][4]);
static LPDIRECT3DVERTEXSHADER9 SimpleVertexShader;
LinearDiskCache g_vs_disk_cache;
LPDIRECT3DVERTEXSHADER9 VertexShaderCache::GetSimpleVertexShader()
{
@ -126,9 +132,23 @@ void SetMultiVSConstant4fv(int const_number, int count, const float *f)
}
}
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);
VertexShaderCache::InsertByteCode(uid, value, value_size, false);
}
};
void VertexShaderCache::Init()
{
char vSimpleProg[1024];
char *vSimpleProg = new char[2048];
sprintf(vSimpleProg,"struct VSOUTPUT\n"
"{\n"
"float4 vPosition : POSITION;\n"
@ -146,8 +166,17 @@ void VertexShaderCache::Init()
"return OUT;\n"
"}\n");
SimpleVertexShader = D3D::CompileVertexShader(vSimpleProg, (int)strlen(vSimpleProg));
SimpleVertexShader = D3D::CompileAndCreateVertexShader(vSimpleProg, (int)strlen(vSimpleProg));
Clear();
delete [] vSimpleProg;
if (!File::Exists(FULL_SHADERCACHE_DIR))
File::CreateDir(FULL_SHADERCACHE_DIR);
char cache_filename[MAX_PATH];
sprintf(cache_filename, "%s%s-vs.cache", FULL_SHADERCACHE_DIR, globals->unique_id);
VertexShaderCacheInserter inserter;
int read_items = g_vs_disk_cache.OpenAndRead(cache_filename, &inserter);
}
void VertexShaderCache::Clear()
@ -167,6 +196,8 @@ void VertexShaderCache::Shutdown()
if (SimpleVertexShader)
SimpleVertexShader->Release();
Clear();
g_vs_disk_cache.Sync();
g_vs_disk_cache.Close();
}
bool VertexShaderCache::SetShader(u32 components)
@ -174,7 +205,7 @@ bool VertexShaderCache::SetShader(u32 components)
DVSTARTPROFILE();
VERTEXSHADERUID uid;
GetVertexShaderId(uid, components);
GetVertexShaderId(&uid, components);
if (uid == last_vertex_shader_uid && vshaders[uid].frameCount == frameCount)
{
if (vshaders[uid].shader)
@ -202,8 +233,27 @@ bool VertexShaderCache::SetShader(u32 components)
return false;
}
const char *code = GenerateVertexShader(components, true);
LPDIRECT3DVERTEXSHADER9 shader = D3D::CompileVertexShader(code, (int)strlen(code));
const char *code = GenerateVertexShaderCode(components, true);
u8 *bytecode;
int bytecodelen;
if (!D3D::CompileVertexShader(code, (int)strlen(code), &bytecode, &bytecodelen))
{
if (g_ActiveConfig.bShowShaderErrors)
{
PanicAlert("Failed to compile Vertex Shader:\n\n%s", code);
}
return false;
}
g_vs_disk_cache.Append((u8 *)&uid, sizeof(uid), bytecode, bytecodelen);
g_vs_disk_cache.Sync();
bool result = InsertByteCode(uid, bytecode, bytecodelen, true);
delete [] bytecode;
return result;
}
bool VertexShaderCache::InsertByteCode(const VERTEXSHADERUID &uid, const u8 *bytecode, int bytecodelen, bool activate) {
LPDIRECT3DVERTEXSHADER9 shader = D3D::CreateVertexShaderFromByteCode(bytecode, bytecodelen);
// Make an entry in the table
VSCacheEntry entry;
@ -214,41 +264,19 @@ bool VertexShaderCache::SetShader(u32 components)
#endif
vshaders[uid] = entry;
last_entry = &vshaders[uid];
if (!shader)
return false;
INCSTAT(stats.numVertexShadersCreated);
SETSTAT(stats.numVertexShadersAlive, (int)vshaders.size());
if (shader)
if (activate)
{
D3D::SetVertexShader(shader);
return true;
}
if (g_ActiveConfig.bShowShaderErrors)
{
PanicAlert("Failed to compile Vertex Shader:\n\n%s", code);
}
return false;
}
void VertexShaderCache::Cleanup()
{
/*
for (VSCache::iterator iter = vshaders.begin(); iter != vshaders.end();)
{
VSCacheEntry &entry = iter->second;
if (entry.frameCount < frameCount - 1400)
{
entry.Destroy();
iter = vshaders.erase(iter);
}
else
{
++iter;
}
}
SETSTAT(stats.numVertexShadersAlive, (int)vshaders.size());*/
}
#if defined(_DEBUG) || defined(DEBUGFAST)
std::string VertexShaderCache::GetCurrentShaderCode()
{

View File

@ -53,10 +53,10 @@ private:
public:
static void Init();
static void Clear();
static void Cleanup();
static void Shutdown();
static bool SetShader(u32 components);
static LPDIRECT3DVERTEXSHADER9 GetSimpleVertexShader();
static bool InsertByteCode(const VERTEXSHADERUID &uid, const u8 *bytecode, int bytecodelen, bool activate);
#if defined(_DEBUG) || defined(DEBUGFAST)
static std::string GetCurrentShaderCode();
#endif

View File

@ -172,8 +172,7 @@ FRAGMENTSHADER* PixelShaderCache::GetShader(bool dstAlphaEnable)
{
DVSTARTPROFILE();
PIXELSHADERUID uid;
u32 dstAlpha = dstAlphaEnable ? 1 : 0;
GetPixelShaderId(uid, PixelShaderManager::GetTextureMask(), dstAlpha);
GetPixelShaderId(&uid, PixelShaderManager::GetTextureMask(), dstAlphaEnable ? 1 : 0);
if (uid == last_pixel_shader_uid && pshaders[uid].frameCount == frameCount)
{
return pShaderLast;
@ -194,13 +193,11 @@ FRAGMENTSHADER* PixelShaderCache::GetShader(bool dstAlphaEnable)
return pShaderLast;
}
//Make an entry in the table
PSCacheEntry& newentry = pshaders[uid];
newentry.frameCount = frameCount;
pShaderLast = &newentry.shader;
const char *code = GeneratePixelShader(PixelShaderManager::GetTextureMask(),
const char *code = GeneratePixelShaderCode(PixelShaderManager::GetTextureMask(),
dstAlphaEnable);
#if defined(_DEBUG) || defined(DEBUGFAST)
@ -219,33 +216,11 @@ FRAGMENTSHADER* PixelShaderCache::GetShader(bool dstAlphaEnable)
return NULL;
}
INCSTAT(stats.numPixelShadersCreated);
SETSTAT(stats.numPixelShadersAlive, pshaders.size());
return pShaderLast;
}
void PixelShaderCache::ProgressiveCleanup()
{
/*
PSCache::iterator iter = pshaders.begin();
while (iter != pshaders.end()) {
PSCacheEntry &entry = iter->second;
if (entry.frameCount < frameCount - 400) {
entry.Destroy();
#ifdef _WIN32
iter = pshaders.erase(iter);
#else
pshaders.erase(iter++); // (this is gcc standard!)
#endif
}
else
iter++;
}
SETSTAT(stats.numPixelShadersAlive, (int)pshaders.size());
*/
}
bool PixelShaderCache::CompilePixelShader(FRAGMENTSHADER& ps, const char* pstrprogram)
{
GLenum err = GL_REPORT_ERROR();

View File

@ -69,7 +69,6 @@ class PixelShaderCache
public:
static void Init();
static void ProgressiveCleanup();
static void Shutdown();
static FRAGMENTSHADER* GetShader(bool dstAlphaEnable);

View File

@ -1054,10 +1054,8 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight)
GL_REPORT_ERRORD();
// Clean out old stuff from caches
// Clean out old stuff from caches. It's not worth it to clean out the shader caches.
DLCache::ProgressiveCleanup();
VertexShaderCache::ProgressiveCleanup();
PixelShaderCache::ProgressiveCleanup();
TextureMngr::ProgressiveCleanup();
frameCount++;

View File

@ -138,7 +138,7 @@ VERTEXSHADER* VertexShaderCache::GetShader(u32 components)
{
DVSTARTPROFILE();
VERTEXSHADERUID uid;
GetVertexShaderId(uid, components);
GetVertexShaderId(&uid, components);
if (uid == last_vertex_shader_uid && vshaders[uid].frameCount == frameCount)
{
@ -163,7 +163,7 @@ VERTEXSHADER* VertexShaderCache::GetShader(u32 components)
VSCacheEntry& entry = vshaders[uid];
entry.frameCount = frameCount;
pShaderLast = &entry.shader;
const char *code = GenerateVertexShader(components, false);
const char *code = GenerateVertexShaderCode(components, false);
#if defined(_DEBUG) || defined(DEBUGFAST)
if (g_ActiveConfig.iLog & CONF_SAVESHADERS && code) {
@ -185,29 +185,6 @@ VERTEXSHADER* VertexShaderCache::GetShader(u32 components)
return pShaderLast;
}
void VertexShaderCache::ProgressiveCleanup()
{
/*
VSCache::iterator iter = vshaders.begin();
while (iter != vshaders.end()) {
VSCacheEntry &entry = iter->second;
if (entry.frameCount < frameCount - 200) {
entry.Destroy();
#ifdef _WIN32
iter = vshaders.erase(iter);
#else
vshaders.erase(iter++);
#endif
}
else {
++iter;
}
}
SETSTAT(stats.numVertexShadersAlive, vshaders.size());
*/
}
bool VertexShaderCache::CompileVertexShader(VERTEXSHADER& vs, const char* pstrprogram)
{
// Reset GL error before compiling shaders. Yeah, we need to investigate the causes of these.
@ -249,8 +226,7 @@ bool VertexShaderCache::CompileVertexShader(VERTEXSHADER& vs, const char* pstrpr
}
glGenProgramsARB(1, &vs.glprogid);
EnableShader(vs.glprogid);
//glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vs.glprogid);
//CurrentShader = vs.glprogid;
glProgramStringARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)strlen(pcompiledprog), pcompiledprog);
err = GL_REPORT_ERROR();
if (err != GL_NO_ERROR) {
@ -278,6 +254,8 @@ void VertexShaderCache::DisableShader()
}
}
// TODO: Why are these if statements commented out?
void VertexShaderCache::SetCurrentShader(GLuint Shader)
{
//if (ShaderEnabled && CurrentShader != Shader)

View File

@ -59,7 +59,6 @@ class VertexShaderCache
public:
static void Init();
static void ProgressiveCleanup();
static void Shutdown();
static VERTEXSHADER* GetShader(u32 components);