2012-04-03 23:30:07 -04:00
|
|
|
/*
|
|
|
|
* Copyright 2011-2012 Branimir Karadzic. All rights reserved.
|
2012-04-29 17:46:23 -04:00
|
|
|
* License: http://www.opensource.org/licenses/BSD-2-Clause
|
2012-04-03 23:30:07 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "glsl_optimizer.h"
|
|
|
|
|
|
|
|
#define MAX_TAGS 256
|
|
|
|
extern "C" {
|
|
|
|
# include <fpp.h>
|
|
|
|
} // extern "C"
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
# define BX_TRACE(_format, ...) fprintf(stderr, "" _format "\n", ##__VA_ARGS__)
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
#define BX_NAMESPACE 1
|
|
|
|
#include <bx/bx.h>
|
|
|
|
|
|
|
|
#if BX_PLATFORM_LINUX
|
|
|
|
# define _stricmp strcasecmp
|
|
|
|
#endif // BX_PLATFORM_LINUX
|
|
|
|
|
|
|
|
#include <bx/commandline.h>
|
|
|
|
#include <bx/countof.h>
|
|
|
|
#include <bx/endian.h>
|
|
|
|
#include <bx/uint32_t.h>
|
|
|
|
|
|
|
|
#if BX_PLATFORM_WINDOWS
|
|
|
|
# include <d3dx9.h>
|
|
|
|
#endif // BX_PLATFORM_WINDOWS
|
|
|
|
|
|
|
|
long int fsize(FILE* _file)
|
|
|
|
{
|
|
|
|
long int pos = ftell(_file);
|
|
|
|
fseek(_file, 0L, SEEK_END);
|
|
|
|
long int size = ftell(_file);
|
|
|
|
fseek(_file, pos, SEEK_SET);
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ConstantType
|
|
|
|
{
|
|
|
|
enum Enum
|
|
|
|
{
|
|
|
|
Uniform1i,
|
|
|
|
Uniform1f,
|
|
|
|
End,
|
|
|
|
|
|
|
|
Uniform1iv,
|
|
|
|
Uniform1fv,
|
|
|
|
Uniform2fv,
|
|
|
|
Uniform3fv,
|
|
|
|
Uniform4fv,
|
|
|
|
Uniform3x3fv,
|
|
|
|
Uniform4x4fv,
|
|
|
|
|
|
|
|
Count,
|
|
|
|
TypeMask = 0x7f,
|
|
|
|
FragmentBit = 0x80
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char* s_constantTypeName[ConstantType::Count] =
|
|
|
|
{
|
|
|
|
"int",
|
|
|
|
"float",
|
|
|
|
NULL,
|
|
|
|
"int",
|
|
|
|
"float",
|
|
|
|
"float2",
|
|
|
|
"float3",
|
|
|
|
"float4",
|
|
|
|
"float3x3",
|
|
|
|
"float4x4",
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Uniform
|
|
|
|
{
|
|
|
|
std::string name;
|
|
|
|
ConstantType::Enum type;
|
|
|
|
uint8_t num;
|
|
|
|
uint16_t regIndex;
|
|
|
|
uint16_t regCount;
|
|
|
|
};
|
|
|
|
typedef std::vector<Uniform> UniformArray;
|
|
|
|
|
|
|
|
#if BX_PLATFORM_WINDOWS
|
|
|
|
struct ConstRemap
|
|
|
|
{
|
|
|
|
ConstantType::Enum id;
|
|
|
|
D3DXPARAMETER_CLASS paramClass;
|
|
|
|
D3DXPARAMETER_TYPE paramType;
|
|
|
|
uint32_t paramBytes;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const ConstRemap s_constRemap[7] =
|
|
|
|
{
|
|
|
|
{ ConstantType::Uniform1iv, D3DXPC_SCALAR, D3DXPT_INT, 4 },
|
|
|
|
{ ConstantType::Uniform1fv, D3DXPC_SCALAR, D3DXPT_FLOAT, 4 },
|
|
|
|
{ ConstantType::Uniform2fv, D3DXPC_VECTOR, D3DXPT_FLOAT, 8 },
|
|
|
|
{ ConstantType::Uniform3fv, D3DXPC_VECTOR, D3DXPT_FLOAT, 12 },
|
|
|
|
{ ConstantType::Uniform4fv, D3DXPC_VECTOR, D3DXPT_FLOAT, 16 },
|
|
|
|
{ ConstantType::Uniform3x3fv, D3DXPC_MATRIX_COLUMNS, D3DXPT_FLOAT, 36 },
|
|
|
|
{ ConstantType::Uniform4x4fv, D3DXPC_MATRIX_COLUMNS, D3DXPT_FLOAT, 64 },
|
|
|
|
};
|
|
|
|
|
|
|
|
ConstantType::Enum findConstantType(const D3DXCONSTANT_DESC& constDesc)
|
|
|
|
{
|
|
|
|
uint32_t count = sizeof(s_constRemap)/sizeof(ConstRemap);
|
|
|
|
for (uint32_t ii = 0; ii < count; ++ii)
|
|
|
|
{
|
|
|
|
const ConstRemap& remap = s_constRemap[ii];
|
|
|
|
|
|
|
|
if (remap.paramClass == constDesc.Class
|
|
|
|
&& remap.paramType == constDesc.Type
|
|
|
|
&& (constDesc.Bytes%remap.paramBytes) == 0)
|
|
|
|
{
|
|
|
|
return remap.id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ConstantType::Count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t s_optimizationLevel[4] =
|
|
|
|
{
|
|
|
|
D3DXSHADER_OPTIMIZATION_LEVEL0,
|
|
|
|
D3DXSHADER_OPTIMIZATION_LEVEL1,
|
|
|
|
D3DXSHADER_OPTIMIZATION_LEVEL2,
|
|
|
|
D3DXSHADER_OPTIMIZATION_LEVEL3,
|
|
|
|
};
|
|
|
|
#endif // BX_PLATFORM_WINDOWS
|
|
|
|
|
|
|
|
class Stream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Stream(FILE* _file, bool _bigEndian = false)
|
|
|
|
: m_file(_file)
|
|
|
|
, m_bigEndian(_bigEndian)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~Stream()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void close()
|
|
|
|
{
|
|
|
|
m_file = NULL;
|
|
|
|
}
|
|
|
|
|
2012-05-29 21:24:55 -04:00
|
|
|
void writef(const char* _format, ...)
|
|
|
|
{
|
|
|
|
if (NULL != m_file)
|
|
|
|
{
|
|
|
|
va_list argList;
|
|
|
|
va_start(argList, _format);
|
|
|
|
|
|
|
|
char temp[2048];
|
|
|
|
int len = vsnprintf(temp, sizeof(temp), _format, argList);
|
|
|
|
fwrite(temp, len, 1, m_file);
|
|
|
|
|
|
|
|
va_end(argList);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-03 23:30:07 -04:00
|
|
|
void write(const char* _str)
|
|
|
|
{
|
|
|
|
if (NULL != m_file)
|
|
|
|
{
|
|
|
|
fwrite(_str, strlen(_str), 1, m_file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void write(const void* _data, size_t _size)
|
|
|
|
{
|
|
|
|
if (NULL != m_file)
|
|
|
|
{
|
|
|
|
fwrite(_data, _size, 1, m_file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Ty>
|
|
|
|
void write(Ty _value)
|
|
|
|
{
|
|
|
|
Ty temp = m_bigEndian ? bx::bigEndian<Ty>(_value) : _value;
|
|
|
|
write(&temp, sizeof(Ty) );
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
FILE* m_file;
|
|
|
|
bool m_bigEndian;
|
|
|
|
};
|
|
|
|
|
2012-06-05 22:32:56 -04:00
|
|
|
class LineReader
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
LineReader(const char* _str)
|
|
|
|
: m_str(_str)
|
|
|
|
, m_pos(0)
|
|
|
|
, m_size( (uint32_t)strlen(_str) )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getLine()
|
|
|
|
{
|
|
|
|
const char* str = &m_str[m_pos];
|
|
|
|
skipLine();
|
|
|
|
|
|
|
|
const char* eol = &m_str[m_pos];
|
|
|
|
|
|
|
|
std::string tmp;
|
|
|
|
tmp.assign(str, eol-str);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isEof() const
|
|
|
|
{
|
|
|
|
return m_str[m_pos] == '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void skipLine()
|
|
|
|
{
|
|
|
|
const char* str = &m_str[m_pos];
|
2012-06-05 22:44:20 -04:00
|
|
|
const char* eol = strstr(str, "\n\r");
|
2012-06-05 22:32:56 -04:00
|
|
|
if (NULL != eol)
|
|
|
|
{
|
|
|
|
m_pos += eol-str+2;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
eol = strstr(str, "\n");
|
|
|
|
if (NULL != eol)
|
|
|
|
{
|
|
|
|
m_pos += eol-str+1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_pos += (uint32_t)strlen(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* m_str;
|
|
|
|
uint32_t m_pos;
|
|
|
|
uint32_t m_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
void printCode(const char* _code)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Code:\n---\n");
|
|
|
|
|
|
|
|
LineReader lr(_code);
|
|
|
|
for (uint32_t line = 1; !lr.isEof(); ++line)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%3d: %s", line, lr.getLine().c_str() );
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "---\n");
|
|
|
|
}
|
|
|
|
|
2012-04-03 23:30:07 -04:00
|
|
|
bool compileGLSLShader(CommandLine& _cmdLine, const std::string& _code, const char* _outFilePath)
|
|
|
|
{
|
|
|
|
const glslopt_shader_type type = (0 == _stricmp(_cmdLine.findOption('\0', "type"), "fragment") ) ? kGlslOptShaderFragment : kGlslOptShaderVertex;
|
|
|
|
|
|
|
|
glslopt_ctx* ctx = glslopt_initialize(false);
|
|
|
|
|
|
|
|
glslopt_shader* shader = glslopt_optimize(ctx, type, _code.c_str(), 0);
|
|
|
|
|
|
|
|
if( !glslopt_get_status(shader) )
|
|
|
|
{
|
2012-06-05 22:32:56 -04:00
|
|
|
printCode(_code.c_str() );
|
2012-05-15 01:31:40 -04:00
|
|
|
fprintf(stderr, "Error: %s\n", glslopt_get_log(shader) );
|
2012-04-03 23:30:07 -04:00
|
|
|
glslopt_cleanup(ctx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* optimizedShader = glslopt_get_output(shader);
|
|
|
|
|
|
|
|
FILE* out = fopen(_outFilePath, "wb");
|
|
|
|
if (NULL == out)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Unable to open output file '%s'.", _outFilePath);
|
|
|
|
glslopt_cleanup(ctx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream stream(out);
|
2012-05-29 21:24:55 -04:00
|
|
|
|
|
|
|
const char* profile = _cmdLine.findOption('p');
|
|
|
|
if (NULL == profile)
|
|
|
|
{
|
|
|
|
stream.write("#ifdef GL_ES\n");
|
|
|
|
stream.write("precision highp float;\n");
|
|
|
|
stream.write("#endif // GL_ES\n\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
stream.writef("#version %s\n\n", profile);
|
|
|
|
}
|
|
|
|
|
2012-04-03 23:30:07 -04:00
|
|
|
stream.write(optimizedShader, strlen(optimizedShader) );
|
|
|
|
uint8_t nul = 0;
|
|
|
|
stream.write(nul);
|
|
|
|
stream.close();
|
|
|
|
fclose(out);
|
|
|
|
|
|
|
|
glslopt_cleanup(ctx);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool compileHLSLShader(CommandLine& _cmdLine, const std::string& _code, const char* _outFilePath)
|
|
|
|
{
|
|
|
|
#if BX_PLATFORM_WINDOWS
|
|
|
|
const char* profile = _cmdLine.findOption('p');
|
|
|
|
if (NULL == profile)
|
|
|
|
{
|
|
|
|
printf("Shader profile must be specified.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bigEndian = _cmdLine.hasArg('\0', "xbox360");
|
|
|
|
|
|
|
|
uint32_t flags = 0;
|
|
|
|
flags |= _cmdLine.hasArg('\0', "debug") ? D3DXSHADER_DEBUG : 0;
|
|
|
|
flags |= _cmdLine.hasArg('\0', "avoid-flow-control") ? D3DXSHADER_AVOID_FLOW_CONTROL : 0;
|
|
|
|
flags |= _cmdLine.hasArg('\0', "no-preshader") ? D3DXSHADER_NO_PRESHADER : 0;
|
|
|
|
flags |= _cmdLine.hasArg('\0', "partial-precision") ? D3DXSHADER_PARTIALPRECISION : 0;
|
|
|
|
flags |= _cmdLine.hasArg('\0', "prefer-flow-control") ? D3DXSHADER_PREFER_FLOW_CONTROL : 0;
|
2012-06-05 22:32:56 -04:00
|
|
|
flags |= _cmdLine.hasArg('\0', "backwards-compatibility") ? D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY : 0;
|
2012-06-05 22:44:20 -04:00
|
|
|
|
|
|
|
bool werror = _cmdLine.hasArg('\0', "Werror");
|
2012-04-03 23:30:07 -04:00
|
|
|
|
|
|
|
uint32_t optimization = 3;
|
|
|
|
if (_cmdLine.hasArg(optimization, 'O') )
|
|
|
|
{
|
|
|
|
optimization = bx::uint32_min(optimization, countof(s_optimizationLevel)-1);
|
|
|
|
flags |= s_optimizationLevel[optimization];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
flags |= D3DXSHADER_SKIPOPTIMIZATION;
|
|
|
|
}
|
|
|
|
|
|
|
|
BX_TRACE("Profile: %s", profile);
|
|
|
|
BX_TRACE("Flags: 0x%08x", flags);
|
|
|
|
BX_TRACE("Big Endian: %s", bigEndian?"true":"false");
|
|
|
|
|
|
|
|
LPD3DXBUFFER code;
|
|
|
|
LPD3DXBUFFER errorMsg;
|
|
|
|
LPD3DXCONSTANTTABLE constantTable;
|
|
|
|
|
|
|
|
HRESULT hr = D3DXCompileShader(_code.c_str()
|
|
|
|
, _code.size()
|
|
|
|
, NULL
|
|
|
|
, NULL
|
|
|
|
, "main"
|
|
|
|
, profile
|
|
|
|
, flags
|
|
|
|
, &code
|
|
|
|
, &errorMsg
|
|
|
|
, &constantTable
|
|
|
|
);
|
2012-06-05 22:44:20 -04:00
|
|
|
if (FAILED(hr)
|
|
|
|
|| werror && NULL != errorMsg)
|
2012-04-03 23:30:07 -04:00
|
|
|
{
|
2012-06-05 22:32:56 -04:00
|
|
|
printCode(_code.c_str() );
|
2012-05-15 01:31:40 -04:00
|
|
|
fprintf(stderr, "Error: 0x%08x %s\n", hr, errorMsg->GetBufferPointer() );
|
2012-04-03 23:30:07 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
D3DXCONSTANTTABLE_DESC desc;
|
|
|
|
hr = constantTable->GetDesc(&desc);
|
|
|
|
if (FAILED(hr) )
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Error 0x%08x\n", hr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
BX_TRACE("Creator: %s 0x%08x", desc.Creator, desc.Version);
|
|
|
|
BX_TRACE("Num constants: %d", desc.Constants);
|
|
|
|
BX_TRACE("# cl ty RxC S By Name");
|
|
|
|
|
|
|
|
UniformArray uniforms;
|
|
|
|
|
|
|
|
for (uint32_t ii = 0; ii < desc.Constants; ++ii)
|
|
|
|
{
|
|
|
|
D3DXHANDLE handle = constantTable->GetConstant(NULL, ii);
|
|
|
|
D3DXCONSTANT_DESC constDesc;
|
|
|
|
uint32_t count;
|
|
|
|
constantTable->GetConstantDesc(handle, &constDesc, &count);
|
|
|
|
BX_TRACE("%3d %2d %2d [%dx%d] %d %3d %s[%d] c%d (%d)"
|
|
|
|
, ii
|
|
|
|
, constDesc.Class
|
|
|
|
, constDesc.Type
|
|
|
|
, constDesc.Rows
|
|
|
|
, constDesc.Columns
|
|
|
|
, constDesc.StructMembers
|
|
|
|
, constDesc.Bytes
|
|
|
|
, constDesc.Name
|
|
|
|
, constDesc.Elements
|
|
|
|
, constDesc.RegisterIndex
|
|
|
|
, constDesc.RegisterCount
|
|
|
|
);
|
|
|
|
|
|
|
|
ConstantType::Enum type = findConstantType(constDesc);
|
|
|
|
if (ConstantType::Count != type)
|
|
|
|
{
|
|
|
|
Uniform un;
|
|
|
|
un.name = '$' == constDesc.Name[0] ? constDesc.Name+1 : constDesc.Name;
|
|
|
|
un.type = type;
|
|
|
|
un.num = constDesc.Elements;
|
|
|
|
un.regIndex = constDesc.RegisterIndex;
|
|
|
|
un.regCount = constDesc.RegisterCount;
|
|
|
|
uniforms.push_back(un);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE* out = fopen(_outFilePath, "wb");
|
|
|
|
if (NULL == out)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Unable to open output file '%s'.", _outFilePath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream stream(out, bigEndian);
|
|
|
|
|
|
|
|
uint16_t count = (uint16_t)uniforms.size();
|
|
|
|
stream.write(count);
|
|
|
|
|
|
|
|
uint32_t fragmentBit = profile[0] == 'p' ? ConstantType::FragmentBit : 0;
|
|
|
|
for (UniformArray::const_iterator it = uniforms.begin(); it != uniforms.end(); ++it)
|
|
|
|
{
|
|
|
|
const Uniform& un = *it;
|
|
|
|
uint8_t nameSize = (uint8_t)un.name.size();
|
|
|
|
stream.write(nameSize);
|
|
|
|
stream.write(un.name.c_str(), nameSize);
|
|
|
|
stream.write<uint8_t>(un.type|fragmentBit);
|
|
|
|
stream.write(un.num);
|
|
|
|
stream.write(un.regIndex);
|
|
|
|
stream.write(un.regCount);
|
|
|
|
|
|
|
|
BX_TRACE("%s, %s, %d, %d, %d"
|
|
|
|
, un.name.c_str()
|
|
|
|
, s_constantTypeName[un.type]
|
|
|
|
, un.num
|
|
|
|
, un.regIndex
|
|
|
|
, un.regCount
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t shaderSize = (uint16_t)code->GetBufferSize();
|
|
|
|
stream.write(shaderSize);
|
|
|
|
stream.write(code->GetBufferPointer(), shaderSize);
|
|
|
|
uint8_t nul = 0;
|
|
|
|
stream.write(nul);
|
|
|
|
stream.close();
|
|
|
|
fclose(out);
|
|
|
|
|
|
|
|
if (NULL != code)
|
|
|
|
{
|
|
|
|
code->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != errorMsg)
|
|
|
|
{
|
|
|
|
errorMsg->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != constantTable)
|
|
|
|
{
|
|
|
|
constantTable->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
fprintf(stderr, "HLSL compiler is not supported on this platform.\n");
|
|
|
|
return false;
|
|
|
|
#endif // BX_PLATFORM_WINDOWS
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Preprocessor
|
|
|
|
{
|
|
|
|
Preprocessor(const char* _filePath)
|
|
|
|
: m_tagptr(m_tags)
|
|
|
|
, m_scratchPos(0)
|
|
|
|
, m_fgetsPos(0)
|
|
|
|
{
|
|
|
|
m_filePath = scratch(_filePath);
|
|
|
|
|
|
|
|
m_tagptr->tag = FPPTAG_USERDATA;
|
|
|
|
m_tagptr->data = this;
|
|
|
|
m_tagptr++;
|
|
|
|
|
|
|
|
m_tagptr->tag = FPPTAG_INPUT;
|
|
|
|
m_tagptr->data = (void*)fppInput;
|
|
|
|
m_tagptr++;
|
|
|
|
|
|
|
|
m_tagptr->tag = FPPTAG_OUTPUT;
|
|
|
|
m_tagptr->data = (void*)fppOutput;
|
|
|
|
m_tagptr++;
|
|
|
|
|
|
|
|
m_tagptr->tag = FPPTAG_ERROR;
|
|
|
|
m_tagptr->data = (void*)fppError;
|
|
|
|
m_tagptr++;
|
|
|
|
|
|
|
|
m_tagptr->tag = FPPTAG_IGNOREVERSION;
|
|
|
|
m_tagptr->data = (void*)0;
|
|
|
|
m_tagptr++;
|
|
|
|
|
|
|
|
m_tagptr->tag = FPPTAG_LINE;
|
|
|
|
m_tagptr->data = (void*)0;
|
|
|
|
m_tagptr++;
|
|
|
|
|
|
|
|
m_tagptr->tag = FPPTAG_INPUT_NAME;
|
|
|
|
m_tagptr->data = m_filePath;
|
|
|
|
m_tagptr++;
|
|
|
|
|
|
|
|
m_default = "#define lowp\n#define mediump\n#define highp\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
void setDefine(const char* _define)
|
|
|
|
{
|
|
|
|
m_tagptr->tag = FPPTAG_DEFINE;
|
|
|
|
m_tagptr->data = scratch(_define);
|
|
|
|
m_tagptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setDefaultDefine(const char* _name)
|
|
|
|
{
|
|
|
|
char temp[1024];
|
|
|
|
_snprintf(temp, countof(temp)
|
|
|
|
, "#ifndef %s\n"
|
|
|
|
"# define %s 0\n"
|
|
|
|
"#endif // %s\n"
|
|
|
|
"\n"
|
|
|
|
, _name
|
|
|
|
, _name
|
|
|
|
, _name
|
|
|
|
);
|
|
|
|
|
|
|
|
m_default += temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool run()
|
|
|
|
{
|
|
|
|
m_fgetsPos = 0;
|
|
|
|
|
|
|
|
FILE* file = fopen(m_filePath, "r");
|
|
|
|
long int size = fsize(file);
|
|
|
|
char* input = new char[size+1];
|
2012-05-15 01:31:40 -04:00
|
|
|
size = fread(input, 1, size, file);
|
2012-04-03 23:30:07 -04:00
|
|
|
input[size] = '\0';
|
2012-05-15 01:31:40 -04:00
|
|
|
fclose(file);
|
2012-04-03 23:30:07 -04:00
|
|
|
|
|
|
|
m_input = m_default;
|
|
|
|
m_input += input;
|
|
|
|
|
|
|
|
fppTag* tagptr = m_tagptr;
|
|
|
|
|
|
|
|
tagptr->tag = FPPTAG_END;
|
|
|
|
tagptr->data = 0;
|
|
|
|
tagptr++;
|
|
|
|
|
|
|
|
int result = fppPreProcess(m_tags);
|
|
|
|
|
|
|
|
return 0 == result;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* fgets(char* _buffer, int _size)
|
|
|
|
{
|
|
|
|
int ii = 0;
|
|
|
|
for (char ch = m_input[m_fgetsPos]; m_fgetsPos < m_input.size() && ii < _size-1; ch = m_input[++m_fgetsPos])
|
|
|
|
{
|
|
|
|
_buffer[ii++] = ch;
|
|
|
|
|
|
|
|
if (ch == '\n' || ii == _size)
|
|
|
|
{
|
|
|
|
_buffer[ii] = '\0';
|
|
|
|
m_fgetsPos++;
|
|
|
|
return _buffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char* fppInput(char* _buffer, int _size, void* _userData)
|
|
|
|
{
|
|
|
|
Preprocessor* thisClass = (Preprocessor*)_userData;
|
|
|
|
return thisClass->fgets(_buffer, _size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fppOutput(int _ch, void* _userData)
|
|
|
|
{
|
|
|
|
Preprocessor* thisClass = (Preprocessor*)_userData;
|
|
|
|
thisClass->m_preprocessed += _ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fppError(void* _userData, char* _format, va_list _vargs)
|
|
|
|
{
|
|
|
|
vfprintf(stderr, _format, _vargs);
|
|
|
|
}
|
|
|
|
|
|
|
|
char* scratch(const char* _str)
|
|
|
|
{
|
|
|
|
char* result = &m_scratch[m_scratchPos];
|
|
|
|
strcpy(result, _str);
|
|
|
|
m_scratchPos += strlen(_str)+1;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
fppTag m_tags[MAX_TAGS];
|
|
|
|
fppTag* m_tagptr;
|
|
|
|
|
|
|
|
char* m_filePath;
|
|
|
|
std::string m_default;
|
|
|
|
std::string m_input;
|
|
|
|
std::string m_preprocessed;
|
|
|
|
char m_scratch[16<<10];
|
|
|
|
uint32_t m_scratchPos;
|
|
|
|
uint32_t m_fgetsPos;
|
|
|
|
};
|
|
|
|
|
2012-05-29 21:24:55 -04:00
|
|
|
// OpenGL #version Features Direct3D Features Shader Model
|
|
|
|
// 2.1 120 vf 9.0 vf 2.0
|
|
|
|
// 3.0 130
|
|
|
|
// 3.1 140
|
|
|
|
// 3.2 150 vgf
|
|
|
|
// 3.3 330 10.0 vgf 4.0
|
|
|
|
// 4.0 400 vhdgf
|
|
|
|
// 4.1 410
|
|
|
|
// 4.2 420 11.0 vhdgf 5.0
|
|
|
|
|
2012-04-03 23:30:07 -04:00
|
|
|
int main(int _argc, const char* _argv[])
|
|
|
|
{
|
|
|
|
CommandLine cmdLine(_argc, _argv);
|
|
|
|
|
|
|
|
const char* filePath = cmdLine.findOption('f');
|
|
|
|
if (NULL == filePath)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Shader file name must be specified.\n");
|
2012-04-29 17:46:23 -04:00
|
|
|
return EXIT_FAILURE;
|
2012-04-03 23:30:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
const char* outFilePath = cmdLine.findOption('o');
|
|
|
|
if (NULL == outFilePath)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Output file name must be specified.\n");
|
2012-04-29 17:46:23 -04:00
|
|
|
return EXIT_FAILURE;
|
2012-04-03 23:30:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
const char* type = cmdLine.findOption('\0', "type");
|
|
|
|
if (NULL == type)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Must specify shader type.");
|
2012-04-29 17:46:23 -04:00
|
|
|
return EXIT_FAILURE;
|
2012-04-03 23:30:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
const char* platform = cmdLine.findOption('\0', "platform");
|
|
|
|
if (NULL == platform)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Must specify platform.\n");
|
2012-04-29 17:46:23 -04:00
|
|
|
return EXIT_FAILURE;
|
2012-04-03 23:30:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool preprocessOnly = cmdLine.hasArg("preprocess");
|
|
|
|
|
|
|
|
Preprocessor preprocessor(filePath);
|
|
|
|
|
|
|
|
preprocessor.setDefaultDefine("BX_PLATFORM_ANDROID");
|
|
|
|
preprocessor.setDefaultDefine("BX_PLATFORM_NACL");
|
|
|
|
preprocessor.setDefaultDefine("BX_PLATFORM_WINDOWS");
|
|
|
|
preprocessor.setDefaultDefine("BX_PLATFORM_XBOX360");
|
2012-05-29 21:24:55 -04:00
|
|
|
preprocessor.setDefaultDefine("BX_PLATFORM_LINUX");
|
2012-04-03 23:30:07 -04:00
|
|
|
preprocessor.setDefaultDefine("BGFX_SHADER_LANGUAGE_GLSL");
|
|
|
|
preprocessor.setDefaultDefine("BGFX_SHADER_LANGUAGE_HLSL");
|
|
|
|
preprocessor.setDefaultDefine("BGFX_SHADER_TYPE_FRAGMENT");
|
|
|
|
preprocessor.setDefaultDefine("BGFX_SHADER_TYPE_VERTEX");
|
|
|
|
|
|
|
|
bool glsl = false;
|
|
|
|
|
|
|
|
if (0 == _stricmp(platform, "android") )
|
|
|
|
{
|
|
|
|
preprocessor.setDefine("BX_PLATFORM_ANDROID=1");
|
|
|
|
preprocessor.setDefine("BGFX_SHADER_LANGUAGE_GLSL=1");
|
|
|
|
glsl = true;
|
|
|
|
}
|
|
|
|
else if (0 == _stricmp(platform, "nacl") )
|
|
|
|
{
|
|
|
|
preprocessor.setDefine("BX_PLATFORM_NACL=1");
|
|
|
|
preprocessor.setDefine("BGFX_SHADER_LANGUAGE_GLSL=1");
|
|
|
|
glsl = true;
|
|
|
|
}
|
2012-05-29 21:24:55 -04:00
|
|
|
else if (0 == _stricmp(platform, "linux"))
|
|
|
|
{
|
|
|
|
preprocessor.setDefine("BX_PLATFORM_LINUX=1");
|
|
|
|
preprocessor.setDefine("BGFX_SHADER_LANGUAGE_GLSL=1");
|
|
|
|
glsl = true;
|
|
|
|
}
|
2012-04-03 23:30:07 -04:00
|
|
|
else if (0 == _stricmp(platform, "windows") )
|
|
|
|
{
|
|
|
|
preprocessor.setDefine("BX_PLATFORM_WINDOWS=1");
|
|
|
|
preprocessor.setDefine("BGFX_SHADER_LANGUAGE_HLSL=1");
|
|
|
|
}
|
|
|
|
else if (0 == _stricmp(platform, "xbox360") )
|
|
|
|
{
|
|
|
|
preprocessor.setDefine("BX_PLATFORM_XBOX360=1");
|
|
|
|
preprocessor.setDefine("BGFX_SHADER_LANGUAGE_HLSL=1");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Unknown platform %s?!", platform);
|
2012-04-29 17:46:23 -04:00
|
|
|
return EXIT_FAILURE;
|
2012-04-03 23:30:07 -04:00
|
|
|
}
|
|
|
|
|
2012-05-15 01:31:40 -04:00
|
|
|
switch (tolower(type[0]) )
|
2012-04-03 23:30:07 -04:00
|
|
|
{
|
2012-05-15 01:31:40 -04:00
|
|
|
case 'f':
|
2012-04-03 23:30:07 -04:00
|
|
|
preprocessor.setDefine("BGFX_SHADER_TYPE_FRAGMENT=1");
|
2012-05-15 01:31:40 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'v':
|
2012-04-03 23:30:07 -04:00
|
|
|
preprocessor.setDefine("BGFX_SHADER_TYPE_VERTEX=1");
|
2012-05-15 01:31:40 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Unknown type: %s?!", type);
|
|
|
|
return EXIT_FAILURE;
|
2012-04-03 23:30:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (preprocessor.run() )
|
|
|
|
{
|
|
|
|
BX_TRACE("Input file: %s", filePath);
|
|
|
|
BX_TRACE("Output file: %s", outFilePath);
|
|
|
|
|
|
|
|
if (preprocessOnly)
|
|
|
|
{
|
|
|
|
FILE* out = fopen(outFilePath, "wb");
|
|
|
|
if (NULL == out)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Unable to open output file '%s'.", outFilePath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream stream(out);
|
|
|
|
if (glsl)
|
|
|
|
{
|
2012-05-29 21:24:55 -04:00
|
|
|
const char* profile = cmdLine.findOption('p');
|
|
|
|
if (NULL == profile)
|
|
|
|
{
|
|
|
|
stream.write("#ifdef GL_ES\n");
|
|
|
|
stream.write("precision highp float;\n");
|
|
|
|
stream.write("#endif // GL_ES\n\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
stream.writef("#version %s\n\n", profile);
|
|
|
|
}
|
2012-04-03 23:30:07 -04:00
|
|
|
}
|
|
|
|
stream.write(preprocessor.m_preprocessed.c_str(), preprocessor.m_preprocessed.size() );
|
|
|
|
stream.close();
|
|
|
|
fclose(out);
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (glsl)
|
|
|
|
{
|
|
|
|
if (compileGLSLShader(cmdLine, preprocessor.m_preprocessed, outFilePath) )
|
|
|
|
{
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (compileHLSLShader(cmdLine, preprocessor.m_preprocessed, outFilePath) )
|
|
|
|
{
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "Failed to build shader.\n");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|