obs/Source/D3D10Shader.cpp
2013-09-04 21:25:55 +02:00

502 lines
15 KiB
C++

/********************************************************************************
Copyright (C) 2012 Hugh Bailey <obs.jim@gmail.com>
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 of the License, 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
********************************************************************************/
#include "Main.h"
void D3D10Shader::LoadDefaults()
{
for(UINT i=0; i<Params.Num(); i++)
{
ShaderParam &param = Params[i];
if(param.defaultValue.Num())
{
param.bChanged = TRUE;
param.curValue.CopyList(param.defaultValue);
}
}
}
bool D3D10Shader::ProcessData(ShaderProcessor &processor, CTSTR lpFileName)
{
Params.TransferFrom(processor.Params);
Samplers.TransferFrom(processor.Samplers);
constantSize = 0;
for(UINT i=0; i<Params.Num(); i++)
{
ShaderParam &param = Params[i];
switch(param.type)
{
case Parameter_Bool:
case Parameter_Float:
case Parameter_Int: constantSize += sizeof(float); break;
case Parameter_Vector2: constantSize += sizeof(float)*2; break;
case Parameter_Vector: constantSize += sizeof(float)*3; break;
case Parameter_Vector4: constantSize += sizeof(float)*4; break;
case Parameter_Matrix3x3: constantSize += sizeof(float)*3*3; break;
case Parameter_Matrix: constantSize += sizeof(float)*4*4; break;
}
}
if(constantSize)
{
D3D10_BUFFER_DESC bd;
zero(&bd, sizeof(bd));
bd.ByteWidth = (constantSize+15)&0xFFFFFFF0; //align to 128bit boundry
bd.Usage = D3D10_USAGE_DYNAMIC;
bd.BindFlags = D3D10_BIND_CONSTANT_BUFFER;
bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
HRESULT err = GetD3D()->CreateBuffer(&bd, NULL, &constantBuffer);
if(FAILED(err))
{
AppWarning(TEXT("Unable to create constant buffer for shdaer '%s', result = %08lX"), lpFileName, err);
return false;
}
}
LoadDefaults();
return true;
}
Shader* D3D10VertexShader::CreateVertexShader(CTSTR lpShader, CTSTR lpFileName)
{
String errorString;
ShaderProcessor shaderProcessor;
if(!shaderProcessor.ProcessShader(lpShader, lpFileName))
AppWarning(TEXT("Unable to process vertex shader '%s'"), lpFileName); //don't exit, leave it to the actual shader compiler to tell the errors
//-----------------------------------------------
D3D10System *d3d10Sys = static_cast<D3D10System*>(GS);
LPCSTR lpVSType = d3d10Sys->bDisableCompatibilityMode ? "vs_4_0" : "vs_4_0_level_9_3";
String cacheFilename = FormattedString(TEXT("%s/shaderCache/%s.blob"), OBSGetAppDataPath(), lpFileName).FindReplace(TEXT("\\"), TEXT("/"));
List<BYTE> shaderBuffer;
LPVOID shaderData;
SIZE_T shaderDataSize;
ID3D10Blob *errorMessages = NULL, *shaderBlob = NULL;
HRESULT err;
if(!OSFileExists(cacheFilename) || OSGetFileModificationTime(lpFileName) > OSGetFileModificationTime(cacheFilename))
{
LPSTR lpAnsiShader = tstr_createUTF8(lpShader);
LPSTR lpAnsiFileName = tstr_createUTF8(lpFileName);
err = D3DX10CompileFromMemory(lpAnsiShader, strlen(lpAnsiShader), lpAnsiFileName, NULL, NULL, "main", lpVSType, D3D10_SHADER_OPTIMIZATION_LEVEL3, 0, NULL, &shaderBlob, &errorMessages, NULL);
Free(lpAnsiFileName);
Free(lpAnsiShader);
if(FAILED(err))
{
if(errorMessages)
{
if(errorMessages->GetBufferSize())
{
LPSTR lpErrors = (LPSTR)errorMessages->GetBufferPointer();
Log(TEXT("Error compiling vertex shader '%s':\r\n\r\n%S\r\n"), lpFileName, lpErrors);
}
errorMessages->Release();
}
CrashError(TEXT("Compilation of vertex shader '%s' failed, result = %08lX"), lpFileName, err);
return NULL;
}
shaderData = shaderBlob->GetBufferPointer();
shaderDataSize = shaderBlob->GetBufferSize();
CreatePath(GetPathDirectory(cacheFilename));
XFile cacheFile(cacheFilename, XFILE_WRITE, XFILE_CREATEALWAYS);
cacheFile.Write(shaderData, (DWORD)shaderDataSize);
}
else
{
XFile cacheFile(cacheFilename, XFILE_READ | XFILE_SHARED, XFILE_OPENEXISTING);
shaderBuffer.SetSize((unsigned)cacheFile.GetFileSize());
cacheFile.Read(shaderBuffer.Array(), shaderBuffer.Num());
shaderData = shaderBuffer.Array();
shaderDataSize = shaderBuffer.Num();
}
//-----------------------------------------------
ID3D10VertexShader *vShader;
ID3D10InputLayout *vShaderLayout;
err = GetD3D()->CreateVertexShader(shaderData, shaderDataSize, &vShader);
if(FAILED(err))
{
CrashError(TEXT("Unable to create vertex shader '%s', result = %08lX"), lpFileName, err);
SafeRelease(shaderBlob);
return NULL;
}
err = GetD3D()->CreateInputLayout(shaderProcessor.generatedLayout.Array(), shaderProcessor.generatedLayout.Num(), shaderData, shaderDataSize, &vShaderLayout);
if(FAILED(err))
{
CrashError(TEXT("Unable to create vertex layout for vertex shader '%s', result = %08lX"), lpFileName, err);
SafeRelease(shaderBlob);
SafeRelease(vShader);
return NULL;
}
SafeRelease(shaderBlob);
//-----------------------------------------------
D3D10VertexShader *shader = new D3D10VertexShader;
shader->vertexShader = vShader;
shader->inputLayout = vShaderLayout;
if(!shader->ProcessData(shaderProcessor, lpFileName))
{
delete shader;
return NULL;
}
shader->bHasNormals = shaderProcessor.bHasNormals;
shader->bHasColors = shaderProcessor.bHasColors;
shader->bHasTangents = shaderProcessor.bHasTangents;
shader->nTextureCoords = shaderProcessor.numTextureCoords;
shader->hViewProj = shader->GetParameterByName(TEXT("ViewProj"));
return shader;
}
Shader* D3D10PixelShader::CreatePixelShader(CTSTR lpShader, CTSTR lpFileName)
{
String errorString;
ShaderProcessor shaderProcessor;
if(!shaderProcessor.ProcessShader(lpShader, lpFileName))
AppWarning(TEXT("Unable to process pixel shader '%s'"), lpFileName); //don't exit, leave it to the actual shader compiler to tell the errors
//-----------------------------------------------
D3D10System *d3d10Sys = static_cast<D3D10System*>(GS);
LPCSTR lpPSType = d3d10Sys->bDisableCompatibilityMode ? "ps_4_0" : "ps_4_0_level_9_3";
String cacheFilename = FormattedString(TEXT("%s/shaderCache/%s.blob"), OBSGetAppDataPath(), lpFileName).FindReplace(TEXT("\\"), TEXT("/"));
List<BYTE> shaderBuffer;
LPVOID shaderData;
SIZE_T shaderDataSize;
ID3D10Blob *errorMessages = NULL, *shaderBlob = NULL;
HRESULT err;
if(!OSFileExists(cacheFilename) || OSGetFileModificationTime(lpFileName) > OSGetFileModificationTime(cacheFilename))
{
LPSTR lpAnsiShader = tstr_createUTF8(lpShader);
LPSTR lpAnsiFileName = tstr_createUTF8(lpFileName);
err = D3DX10CompileFromMemory(lpAnsiShader, strlen(lpAnsiShader), lpAnsiFileName, NULL, NULL, "main", lpPSType, D3D10_SHADER_OPTIMIZATION_LEVEL3, 0, NULL, &shaderBlob, &errorMessages, NULL);
Free(lpAnsiFileName);
Free(lpAnsiShader);
if(FAILED(err))
{
if(errorMessages)
{
if(errorMessages->GetBufferSize())
{
LPSTR lpErrors = (LPSTR)errorMessages->GetBufferPointer();
Log(TEXT("Error compiling pixel shader '%s':\r\n\r\n%S\r\n"), lpFileName, lpErrors);
}
errorMessages->Release();
}
CrashError(TEXT("Compilation of pixel shader '%s' failed, result = %08lX"), lpFileName, err);
return NULL;
}
shaderData = shaderBlob->GetBufferPointer();
shaderDataSize = shaderBlob->GetBufferSize();
CreatePath(GetPathDirectory(cacheFilename));
XFile cacheFile(cacheFilename, XFILE_WRITE, XFILE_CREATEALWAYS);
cacheFile.Write(shaderData, (DWORD)shaderDataSize);
}
else
{
XFile cacheFile(cacheFilename, XFILE_READ | XFILE_SHARED, XFILE_OPENEXISTING);
shaderBuffer.SetSize((unsigned)cacheFile.GetFileSize());
cacheFile.Read(shaderBuffer.Array(), shaderBuffer.Num());
shaderData = shaderBuffer.Array();
shaderDataSize = shaderBuffer.Num();
}
//-----------------------------------------------
ID3D10PixelShader *pShader;
err = GetD3D()->CreatePixelShader(shaderData, shaderDataSize, &pShader);
if(FAILED(err))
{
CrashError(TEXT("Unable to create pixel shader '%s', result = %08lX"), lpFileName, err);
SafeRelease(shaderBlob);
return NULL;
}
SafeRelease(shaderBlob);
//-----------------------------------------------
D3D10PixelShader *shader = new D3D10PixelShader;
shader->pixelShader = pShader;
if(!shader->ProcessData(shaderProcessor, lpFileName))
{
delete shader;
return NULL;
}
return shader;
}
D3D10Shader::~D3D10Shader()
{
for(UINT i=0; i<Samplers.Num(); i++)
Samplers[i].FreeData();
for(UINT i=0; i<Params.Num(); i++)
Params[i].FreeData();
SafeRelease(constantBuffer);
}
D3D10VertexShader::~D3D10VertexShader()
{
SafeRelease(vertexShader);
SafeRelease(inputLayout);
}
D3D10PixelShader::~D3D10PixelShader()
{
SafeRelease(pixelShader);
}
int D3D10Shader::NumParams() const
{
return Params.Num();
}
HANDLE D3D10Shader::GetParameter(UINT parameter) const
{
if(parameter >= Params.Num())
return NULL;
return (HANDLE)(Params+parameter);
}
HANDLE D3D10Shader::GetParameterByName(CTSTR lpName) const
{
for(UINT i=0; i<Params.Num(); i++)
{
ShaderParam &param = Params[i];
if(param.name == lpName)
return (HANDLE)&param;
}
return NULL;
}
/*#define GetValidHandle() \
ShaderParam *param = (ShaderParam*)hObject; \
if(!hObject) \
{ \
AppWarning(TEXT("Invalid handle input as shader parameter")); \
return; \
}*/
#define GetValidHandle() \
ShaderParam *param = (ShaderParam*)hObject; \
if(!hObject) \
return;
void D3D10Shader::GetParameterInfo(HANDLE hObject, ShaderParameterInfo &paramInfo) const
{
GetValidHandle();
paramInfo.type = param->type;
paramInfo.name = param->name;
}
void D3D10Shader::SetBool(HANDLE hObject, BOOL bValue)
{
GetValidHandle();
BOOL bSizeChanged = param->curValue.SetSize(sizeof(BOOL));
BOOL &curVal = *(BOOL*)param->curValue.Array();
if(bSizeChanged || curVal != bValue)
{
curVal = bValue;
param->bChanged = TRUE;
}
}
void D3D10Shader::SetFloat(HANDLE hObject, float fValue)
{
GetValidHandle();
BOOL bSizeChanged = param->curValue.SetSize(sizeof(float));
float &curVal = *(float*)param->curValue.Array();
if(bSizeChanged || curVal != fValue)
{
curVal = fValue;
param->bChanged = TRUE;
}
}
void D3D10Shader::SetInt(HANDLE hObject, int iValue)
{
GetValidHandle();
BOOL bSizeChanged = param->curValue.SetSize(sizeof(int));
int &curVal = *(int*)param->curValue.Array();
if(bSizeChanged || curVal != iValue)
{
curVal = iValue;
param->bChanged = TRUE;
}
}
void D3D10Shader::SetMatrix(HANDLE hObject, float *matrix)
{
SetValue(hObject, matrix, sizeof(float)*4*4);
}
void D3D10Shader::SetVector(HANDLE hObject, const Vect &value)
{
SetValue(hObject, value.ptr, sizeof(float)*3);
}
void D3D10Shader::SetVector2(HANDLE hObject, const Vect2 &value)
{
SetValue(hObject, value.ptr, sizeof(Vect2));
}
void D3D10Shader::SetVector4(HANDLE hObject, const Vect4 &value)
{
SetValue(hObject, value.ptr, sizeof(Vect4));
}
void D3D10Shader::SetTexture(HANDLE hObject, BaseTexture *texture)
{
GetValidHandle();
BOOL bSizeChanged = param->curValue.SetSize(sizeof(const BaseTexture*));
const BaseTexture *&curVal = *(const BaseTexture**)param->curValue.Array();
if(bSizeChanged || curVal != texture)
{
curVal = texture;
param->bChanged = TRUE;
}
}
void D3D10Shader::SetValue(HANDLE hObject, const void *val, DWORD dwSize)
{
GetValidHandle();
BOOL bSizeChanged = param->curValue.SetSize(dwSize);
if(bSizeChanged || !mcmp(param->curValue.Array(), val, dwSize))
{
mcpy(param->curValue.Array(), val, dwSize);
param->bChanged = TRUE;
}
}
void D3D10Shader::UpdateParams()
{
List<BYTE> shaderConstantData;
bool bUpload = false;
for(UINT i=0; i<Params.Num(); i++)
{
ShaderParam &param = Params[i];
if(param.type != Parameter_Texture)
{
if(!param.curValue.Num())
{
AppWarning(TEXT("D3D10Shader::UpdateParams: shader parameter '%s' not set"), param.name.Array());
bUpload = false;
break;
}
shaderConstantData.AppendList(param.curValue);
if(param.bChanged)
{
bUpload = true;
param.bChanged = false;
}
}
else
{
if(param.curValue.Num())
{
Texture *texture = *(Texture**)param.curValue.Array();
LoadTexture(texture, param.textureID);
}
}
}
if(shaderConstantData.Num() != constantSize)
{
AppWarning(TEXT("D3D10Shader::UpdateParams: invalid parameter specifications, constant size given: %d, constant size expected: %d"), shaderConstantData.Num(), constantSize);
bUpload = false;
}
if(bUpload)
{
BYTE *outData;
HRESULT err;
if(FAILED(err = constantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&outData)))
{
AppWarning(TEXT("D3D10Shader::UpdateParams: could not map constant buffer, result = %08lX"), err);
return;
}
mcpy(outData, shaderConstantData.Array(), shaderConstantData.Num());
constantBuffer->Unmap();
}
}