You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
495 lines
13 KiB
C++
495 lines
13 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
#include "stdafx.h"
|
|
|
|
#pragma comment(lib, "D3Dcompiler.lib")
|
|
|
|
#define CRYFXC_VER "1.01"
|
|
|
|
|
|
enum SwitchType
|
|
{
|
|
FXC_E, FXC_T, FXC_Help, FXC_CmdOptFile, FXC_Cc, FXC_Compress, FXC_D, FXC_Decompress, FXC_Fc,
|
|
FXC_Fh, FXC_Fo, FXC_Fx, FXC_P, FXC_Gch, FXC_Gdp, FXC_Gec, FXC_Ges, FXC_Gfa, FXC_Gfp, FXC_Gis,
|
|
FXC_Gpp, FXC_I, FXC_LD, FXC_Ni, FXC_NoLogo, FXC_Od, FXC_Op, FXC_O0, FXC_O1, FXC_O2, FXC_O3,
|
|
FXC_Vd, FXC_Vi, FXC_Vn, FXC_Zi, FXC_Zpc, FXC_Zpr,
|
|
|
|
FXC_NumArgs
|
|
};
|
|
|
|
|
|
struct SwitchEntry
|
|
{
|
|
SwitchType type;
|
|
const char* text;
|
|
bool hasValue;
|
|
bool supported;
|
|
};
|
|
|
|
|
|
const static SwitchEntry s_switchEntries[] =
|
|
{
|
|
{FXC_E, "/E", 1, true},
|
|
{FXC_T, "/T", 1, true},
|
|
{FXC_Fh, "/Fh", 1, true},
|
|
{FXC_Fo, "/Fo", 1, true},
|
|
|
|
{FXC_Gec, "/Gec", 0, true},
|
|
{FXC_Ges, "/Ges", 0, true},
|
|
{FXC_Gfa, "/Gfa", 0, true},
|
|
{FXC_Gfp, "/Gfp", 0, true},
|
|
{FXC_Gis, "/Gis", 0, true},
|
|
{FXC_Gpp, "/Gpp", 0, true},
|
|
{FXC_Od, "/Od", 0, true},
|
|
{FXC_O0, "/O0", 0, true},
|
|
{FXC_O1, "/O1", 0, true},
|
|
{FXC_O2, "/O2", 0, true},
|
|
{FXC_O3, "/O3", 0, true},
|
|
{FXC_Op, "/Op", 0, true},
|
|
{FXC_Vd, "/Vd", 0, true},
|
|
{FXC_Vn, "/Vn", 1, true},
|
|
{FXC_Zi, "/Zi", 0, true},
|
|
{FXC_Zpc, "/Zpc", 0, true},
|
|
{FXC_Zpr, "/Zpr", 0, true},
|
|
{FXC_NoLogo, "/nologo", 0, true},
|
|
|
|
{FXC_Help, "/?", 0, false},
|
|
{FXC_Help, "/help", 0, false},
|
|
{FXC_Cc, "/Cc", 0, false},
|
|
{FXC_Compress, "/compress", 0, false},
|
|
{FXC_D, "/D", 1, false},
|
|
{FXC_Decompress, "/decompress", 0, false},
|
|
{FXC_Fc, "/Fc", 1, false},
|
|
{FXC_Fx, "/Fx", 1, false},
|
|
{FXC_P, "/P", 1, false},
|
|
{FXC_Gch, "/Gch", 0, false},
|
|
{FXC_Gdp, "/Gdp", 0, false},
|
|
{FXC_I, "/I", 1, false},
|
|
{FXC_LD, "/LD", 0, false},
|
|
{FXC_Ni, "/Ni", 0, false},
|
|
{FXC_Vi, "/Vi", 0, false}
|
|
};
|
|
|
|
|
|
bool IsSwitch(const char* p)
|
|
{
|
|
assert(p);
|
|
return *p == '/' || *p == '@';
|
|
}
|
|
|
|
|
|
const SwitchEntry* GetSwitch(const char* p)
|
|
{
|
|
assert(p);
|
|
for (size_t i = 0; i < sizeof(s_switchEntries) / sizeof(s_switchEntries[0]); ++i)
|
|
{
|
|
if (_stricmp(s_switchEntries[i].text, p) == 0)
|
|
{
|
|
return &s_switchEntries[i];
|
|
}
|
|
}
|
|
|
|
if (*p == '@')
|
|
{
|
|
const static SwitchEntry sw = {FXC_CmdOptFile, "@", 0, false};
|
|
return &sw;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
struct ParserResults
|
|
{
|
|
const char* pProfile;
|
|
const char* pEntry;
|
|
const char* pOutFile;
|
|
const char* pInFile;
|
|
const char* pHeaderVariableName;
|
|
unsigned int compilerFlags;
|
|
bool disassemble;
|
|
|
|
void Init()
|
|
{
|
|
pProfile = 0;
|
|
pEntry = 0;
|
|
pOutFile = 0;
|
|
pInFile = 0;
|
|
pHeaderVariableName = 0;
|
|
compilerFlags = 0;
|
|
disassemble = false;
|
|
}
|
|
};
|
|
|
|
|
|
bool ParseCommandLine(const char* const* args, size_t numargs, ParserResults& parserRes)
|
|
{
|
|
parserRes.Init();
|
|
|
|
if (numargs < 4)
|
|
{
|
|
fprintf(stderr, "Failed to specify all required arguments: infile, outfile, profile and entry point\n");
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 1; i < numargs; ++i)
|
|
{
|
|
if (IsSwitch(args[i]))
|
|
{
|
|
const SwitchEntry* sw = GetSwitch(args[i]);
|
|
if (!sw)
|
|
{
|
|
fprintf(stderr, "Unknown switch: %s\n", args[i]);
|
|
return false;
|
|
}
|
|
|
|
if (!sw->supported)
|
|
{
|
|
fprintf(stderr, "Unsupported switch: %s\n", sw->text);
|
|
return false;
|
|
}
|
|
|
|
if (sw->hasValue)
|
|
{
|
|
if (i + 1 == numargs || IsSwitch(args[i + 1]))
|
|
{
|
|
fprintf(stderr, "Missing value for switch: %s\n", sw->text);
|
|
return false;
|
|
}
|
|
|
|
const char* pValue = args[i + 1];
|
|
switch (sw->type)
|
|
{
|
|
case FXC_E:
|
|
parserRes.pEntry = pValue;
|
|
break;
|
|
case FXC_T:
|
|
parserRes.pProfile = pValue;
|
|
break;
|
|
case FXC_Fh:
|
|
parserRes.pOutFile = pValue;
|
|
parserRes.disassemble = true;
|
|
break;
|
|
case FXC_Fo:
|
|
parserRes.pOutFile = pValue;
|
|
break;
|
|
case FXC_Vn:
|
|
parserRes.pHeaderVariableName = pValue;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Failed assigning switch: %s | value: %s\n", sw->text, pValue);
|
|
return false;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
else
|
|
{
|
|
switch (sw->type)
|
|
{
|
|
case FXC_Gec:
|
|
parserRes.compilerFlags |= D3D10_SHADER_ENABLE_BACKWARDS_COMPATIBILITY;
|
|
break;
|
|
case FXC_Od:
|
|
parserRes.compilerFlags |= D3D10_SHADER_SKIP_OPTIMIZATION;
|
|
break;
|
|
case FXC_O0:
|
|
parserRes.compilerFlags |= D3D10_SHADER_OPTIMIZATION_LEVEL0;
|
|
break;
|
|
case FXC_O1:
|
|
parserRes.compilerFlags |= D3D10_SHADER_OPTIMIZATION_LEVEL1;
|
|
break;
|
|
case FXC_O2:
|
|
parserRes.compilerFlags |= D3D10_SHADER_OPTIMIZATION_LEVEL2;
|
|
break;
|
|
case FXC_O3:
|
|
parserRes.compilerFlags |= D3D10_SHADER_OPTIMIZATION_LEVEL3;
|
|
break;
|
|
case FXC_Zi:
|
|
parserRes.compilerFlags |= D3D10_SHADER_DEBUG;
|
|
break;
|
|
case FXC_Zpc:
|
|
parserRes.compilerFlags |= D3D10_SHADER_PACK_MATRIX_COLUMN_MAJOR;
|
|
break;
|
|
case FXC_Zpr:
|
|
parserRes.compilerFlags |= D3D10_SHADER_PACK_MATRIX_ROW_MAJOR;
|
|
break;
|
|
case FXC_Ges:
|
|
parserRes.compilerFlags |= D3D10_SHADER_ENABLE_STRICTNESS;
|
|
break;
|
|
case FXC_Gfa:
|
|
parserRes.compilerFlags |= D3D10_SHADER_AVOID_FLOW_CONTROL;
|
|
break;
|
|
case FXC_Gfp:
|
|
parserRes.compilerFlags |= D3D10_SHADER_PREFER_FLOW_CONTROL;
|
|
break;
|
|
case FXC_Gis:
|
|
parserRes.compilerFlags |= D3D10_SHADER_IEEE_STRICTNESS;
|
|
break;
|
|
case FXC_Gpp:
|
|
parserRes.compilerFlags |= D3D10_SHADER_PARTIAL_PRECISION;
|
|
break;
|
|
case FXC_Op:
|
|
parserRes.compilerFlags |= D3D10_SHADER_NO_PRESHADER;
|
|
break;
|
|
case FXC_Vd:
|
|
parserRes.compilerFlags |= D3D10_SHADER_SKIP_VALIDATION;
|
|
break;
|
|
case FXC_NoLogo:
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Failed assigning switch: %s\n", sw->text);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if (i == numargs - 1)
|
|
{
|
|
parserRes.pInFile = args[i];
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Error in command line at token: %s\n", args[i]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const bool successful = parserRes.pProfile && parserRes.pEntry && parserRes.pInFile && parserRes.pOutFile;
|
|
if (!successful)
|
|
{
|
|
fprintf(stderr, "Failed to specify all required arguments: infile, outfile, profile and entry point\n");
|
|
}
|
|
|
|
return successful;
|
|
}
|
|
|
|
|
|
bool ReadInFile(const char* pInFile, std::vector<char>& data)
|
|
{
|
|
if (!pInFile)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool read = false;
|
|
|
|
FILE* fin = 0;
|
|
fopen_s(&fin, pInFile, "rb");
|
|
if (fin)
|
|
{
|
|
fseek(fin, 0, SEEK_END);
|
|
const long l = ftell(fin);
|
|
if (l >= 0)
|
|
{
|
|
fseek(fin, 0, SEEK_SET);
|
|
const size_t len = l > 0 ? (size_t) l : 0;
|
|
data.resize(len);
|
|
fread(&data[0], 1, len, fin);
|
|
read = true;
|
|
}
|
|
|
|
fclose(fin);
|
|
}
|
|
|
|
return read;
|
|
}
|
|
|
|
|
|
bool WriteByteCode(const char* pFileName, const void* pCode, size_t codeSize)
|
|
{
|
|
if (!pFileName || !pCode && codeSize)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool written = false;
|
|
|
|
FILE* fout = 0;
|
|
fopen_s(&fout, pFileName, "wb");
|
|
if (fout)
|
|
{
|
|
fwrite(pCode, 1, codeSize, fout);
|
|
fclose(fout);
|
|
written = true;
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
|
|
bool WriteHexListing(const char* pFileName, const char* pHdrVarName, const char* pDisassembly, const void* pCode, size_t codeSize)
|
|
{
|
|
if (!pFileName || !pHdrVarName || !pDisassembly || !pCode && codeSize)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool written = false;
|
|
|
|
FILE* fout = 0;
|
|
fopen_s(&fout, pFileName, "w");
|
|
if (fout)
|
|
{
|
|
fprintf(fout, "#if 0\n%s#endif\n\n", pDisassembly);
|
|
fprintf(fout, "const BYTE g_%s[] = \n{", pHdrVarName);
|
|
|
|
const size_t blockSize = 6;
|
|
const size_t numBlocks = codeSize / blockSize;
|
|
|
|
const unsigned char* p = (const unsigned char*) pCode;
|
|
|
|
size_t i = 0;
|
|
for (; i < numBlocks * blockSize; i += blockSize)
|
|
{
|
|
fprintf(fout, "\n %3d, %3d, %3d, %3d, %3d, %3d", p[i], p[i + 1], p[i + 2], p[i + 3], p[i + 4], p[i + 5]);
|
|
if (i + blockSize < codeSize)
|
|
{
|
|
fprintf(fout, ",");
|
|
}
|
|
}
|
|
|
|
if (i < codeSize)
|
|
{
|
|
fprintf(fout, "\n ");
|
|
|
|
for (; i < codeSize; ++i)
|
|
{
|
|
fprintf(fout, "%3d", p[i]);
|
|
if (i < codeSize - 1)
|
|
{
|
|
fprintf(fout, ", ");
|
|
}
|
|
}
|
|
}
|
|
|
|
fprintf(fout, "\n};\n");
|
|
|
|
fclose(fout);
|
|
written = true;
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
|
|
void DisplayInfo()
|
|
{
|
|
fprintf(stdout, "FXC stub for remote shader compile server\n(C) 2012 Crytek. All rights reserved.\n\nVersion "CRYFXC_VER " for %d bit, linked against D3DCompiler_%d.dll\n\n", sizeof(void*) * 8, D3DX11_SDK_VERSION);
|
|
fprintf(stdout, "Syntax: fxc SwitchOptions Filename\n\n");
|
|
fprintf(stdout, "Supported switches: ");
|
|
|
|
bool firstSw = true;
|
|
for (size_t i = 0; i < sizeof(s_switchEntries) / sizeof(s_switchEntries[0]); ++i)
|
|
{
|
|
if (s_switchEntries[i].supported)
|
|
{
|
|
fprintf(stdout, "%s%s", firstSw ? "" : ", ", s_switchEntries[i].text);
|
|
firstSw = false;
|
|
}
|
|
}
|
|
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
|
|
int _tmain(int argc, _TCHAR* argv[])
|
|
{
|
|
if (argc == 1)
|
|
{
|
|
DisplayInfo();
|
|
return 0;
|
|
}
|
|
|
|
ParserResults parserRes;
|
|
if (!ParseCommandLine(argv, argc, parserRes))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
std::vector<char> program;
|
|
if (!ReadInFile(parserRes.pInFile, program))
|
|
{
|
|
fprintf(stderr, "Failed to read input file: %s\n", parserRes.pInFile);
|
|
return 1;
|
|
}
|
|
|
|
ID3D10Blob* pShader = 0;
|
|
ID3D10Blob* pErr = 0;
|
|
|
|
bool successful = SUCCEEDED(D3DCompile(&program[0], program.size(), parserRes.pInFile, 0, 0, parserRes.pEntry, parserRes.pProfile, parserRes.compilerFlags, 0, &pShader, &pErr)) && pShader;
|
|
|
|
if (successful)
|
|
{
|
|
const unsigned char* pCode = (unsigned char*) pShader->GetBufferPointer();
|
|
const size_t codeSize = pShader->GetBufferSize();
|
|
|
|
if (!parserRes.disassemble)
|
|
{
|
|
successful = WriteByteCode(parserRes.pOutFile, pCode, codeSize);
|
|
if (!successful)
|
|
{
|
|
fprintf(stderr, "Failed to write output file: %s\n", parserRes.pOutFile);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ID3D10Blob* pDisassembled = 0;
|
|
successful = SUCCEEDED(D3DDisassemble(pCode, codeSize, 0, 0, &pDisassembled)) && pDisassembled;
|
|
|
|
if (successful)
|
|
{
|
|
const char* pDisassembly = (char*) pDisassembled->GetBufferPointer();
|
|
const char* pHdrVarName = parserRes.pHeaderVariableName ? parserRes.pHeaderVariableName : parserRes.pEntry;
|
|
successful = WriteHexListing(parserRes.pOutFile, pHdrVarName, pDisassembly, pCode, codeSize);
|
|
if (!successful)
|
|
{
|
|
fprintf(stderr, "Failed to write output file: %s\n", parserRes.pOutFile);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Failed to disassemble shader code\n", parserRes.pOutFile);
|
|
}
|
|
|
|
if (pDisassembled)
|
|
{
|
|
pDisassembled->Release();
|
|
pDisassembled = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pErr)
|
|
{
|
|
const char* pMsg = (const char*) pErr->GetBufferPointer();
|
|
fprintf(stderr, "%s\n", pMsg);
|
|
}
|
|
}
|
|
|
|
if (pShader)
|
|
{
|
|
pShader->Release();
|
|
pShader = 0;
|
|
}
|
|
|
|
if (pErr)
|
|
{
|
|
pErr->Release();
|
|
pErr = 0;
|
|
}
|
|
|
|
return successful ? 0 : 1;
|
|
}
|