mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
1800 lines
45 KiB
C++
1800 lines
45 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
IceTech GPL Source Code
|
|
Copyright (C) 2026 Justin Marshall
|
|
|
|
This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
|
|
|
|
IceTech Source Code 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
IceTech Source Code 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 IceTech Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "opengl.h"
|
|
#include "gl_d3d12arb.h"
|
|
#include <d3dcompiler.h>
|
|
|
|
#include <windows.h>
|
|
#include <wrl/client.h>
|
|
#include <unordered_map>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cmath>
|
|
|
|
#undef max
|
|
#undef min
|
|
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
namespace
|
|
{
|
|
struct ARBProgram
|
|
{
|
|
GLuint id = 0;
|
|
GLenum target = 0;
|
|
std::string source;
|
|
std::string hlsl;
|
|
std::string errorString;
|
|
GLint errorPosition = -1;
|
|
uint32_t revision = 1;
|
|
bool compileAttempted = false;
|
|
float local[QD3D12_ARB_MAX_PROGRAM_PARAMETERS][4] = {};
|
|
ComPtr<ID3DBlob> blob;
|
|
};
|
|
|
|
static std::unordered_map<GLuint, ARBProgram> g_vertexPrograms;
|
|
static std::unordered_map<GLuint, ARBProgram> g_fragmentPrograms;
|
|
static GLuint g_nextProgramId = 1;
|
|
static GLuint g_boundVertexProgram = 0;
|
|
static GLuint g_boundFragmentProgram = 0;
|
|
static bool g_vertexProgramEnabled = false;
|
|
static bool g_fragmentProgramEnabled = false;
|
|
|
|
static float g_env[QD3D12_ARB_MAX_PROGRAM_PARAMETERS][4] = {};
|
|
static GLenum g_lastError = GL_NO_ERROR;
|
|
static GLint g_lastProgramErrorPosition = -1;
|
|
static std::string g_lastProgramErrorString;
|
|
|
|
static void SetError(GLenum e)
|
|
{
|
|
if (g_lastError == GL_NO_ERROR)
|
|
g_lastError = e;
|
|
}
|
|
|
|
static bool IsProgramTarget(GLenum target)
|
|
{
|
|
return target == GL_VERTEX_PROGRAM_ARB || target == GL_FRAGMENT_PROGRAM_ARB;
|
|
}
|
|
|
|
static std::unordered_map<GLuint, ARBProgram>& ProgramsForTarget(GLenum target)
|
|
{
|
|
return (target == GL_VERTEX_PROGRAM_ARB) ? g_vertexPrograms : g_fragmentPrograms;
|
|
}
|
|
|
|
static GLuint& BoundForTarget(GLenum target)
|
|
{
|
|
return (target == GL_VERTEX_PROGRAM_ARB) ? g_boundVertexProgram : g_boundFragmentProgram;
|
|
}
|
|
|
|
static std::string Trim(const std::string& s)
|
|
{
|
|
size_t b = 0;
|
|
while (b < s.size() && std::isspace((unsigned char)s[b]))
|
|
++b;
|
|
|
|
size_t e = s.size();
|
|
while (e > b && std::isspace((unsigned char)s[e - 1]))
|
|
--e;
|
|
|
|
return s.substr(b, e - b);
|
|
}
|
|
|
|
static std::string Upper(std::string s)
|
|
{
|
|
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return (char)std::toupper(c); });
|
|
return s;
|
|
}
|
|
|
|
static bool StartsWithNoCase(const std::string& s, const char* prefix)
|
|
{
|
|
const size_t n = std::strlen(prefix);
|
|
if (s.size() < n)
|
|
return false;
|
|
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
if (std::toupper((unsigned char)s[i]) != std::toupper((unsigned char)prefix[i]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool EqualsNoCase(const std::string& a, const char* b)
|
|
{
|
|
return Upper(a) == Upper(std::string(b));
|
|
}
|
|
|
|
static std::string StripComments(const std::string& source)
|
|
{
|
|
std::string out;
|
|
out.reserve(source.size());
|
|
|
|
for (size_t i = 0; i < source.size(); ++i)
|
|
{
|
|
if (source[i] == '#')
|
|
{
|
|
while (i < source.size() && source[i] != '\n' && source[i] != '\r')
|
|
++i;
|
|
out.push_back('\n');
|
|
}
|
|
else
|
|
{
|
|
out.push_back(source[i]);
|
|
}
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
static std::vector<std::string> LogicalLines(const std::string& source)
|
|
{
|
|
std::string clean = StripComments(source);
|
|
std::vector<std::string> lines;
|
|
std::string cur;
|
|
int braceDepth = 0;
|
|
|
|
for (char c : clean)
|
|
{
|
|
if (c == '{') ++braceDepth;
|
|
if (c == '}') --braceDepth;
|
|
|
|
if ((c == '\n' || c == '\r' || c == ';') && braceDepth <= 0)
|
|
{
|
|
std::string t = Trim(cur);
|
|
if (!t.empty())
|
|
lines.push_back(t);
|
|
cur.clear();
|
|
}
|
|
else
|
|
{
|
|
cur.push_back(c);
|
|
}
|
|
}
|
|
|
|
std::string t = Trim(cur);
|
|
if (!t.empty())
|
|
lines.push_back(t);
|
|
|
|
return lines;
|
|
}
|
|
|
|
static std::vector<std::string> SplitArgs(const std::string& s)
|
|
{
|
|
std::vector<std::string> args;
|
|
std::string cur;
|
|
int bracketDepth = 0;
|
|
int braceDepth = 0;
|
|
int parenDepth = 0;
|
|
|
|
for (char c : s)
|
|
{
|
|
if (c == '[') ++bracketDepth;
|
|
else if (c == ']') --bracketDepth;
|
|
else if (c == '{') ++braceDepth;
|
|
else if (c == '}') --braceDepth;
|
|
else if (c == '(') ++parenDepth;
|
|
else if (c == ')') --parenDepth;
|
|
|
|
if (c == ',' && bracketDepth == 0 && braceDepth == 0 && parenDepth == 0)
|
|
{
|
|
args.push_back(Trim(cur));
|
|
cur.clear();
|
|
}
|
|
else
|
|
{
|
|
cur.push_back(c);
|
|
}
|
|
}
|
|
|
|
std::string t = Trim(cur);
|
|
if (!t.empty())
|
|
args.push_back(t);
|
|
|
|
return args;
|
|
}
|
|
|
|
static std::string ComponentMap(std::string s)
|
|
{
|
|
for (char& c : s)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'r': case 'R': c = 'x'; break;
|
|
case 'g': case 'G': c = 'y'; break;
|
|
case 'b': case 'B': c = 'z'; break;
|
|
case 'a': case 'A': c = 'w'; break;
|
|
default: c = (char)std::tolower((unsigned char)c); break;
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static bool IsComponentSuffix(const std::string& s)
|
|
{
|
|
if (s.empty() || s.size() > 4)
|
|
return false;
|
|
|
|
for (char c : s)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'x': case 'X':
|
|
case 'y': case 'Y':
|
|
case 'z': case 'Z':
|
|
case 'w': case 'W':
|
|
case 'r': case 'R':
|
|
case 'g': case 'G':
|
|
case 'b': case 'B':
|
|
case 'a': case 'A':
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void SplitARBComponentSuffix(std::string& tokenBase, std::string& outSwizzle)
|
|
{
|
|
outSwizzle.clear();
|
|
|
|
const size_t dot = tokenBase.find_last_of('.');
|
|
if (dot == std::string::npos)
|
|
return;
|
|
|
|
const std::string suffix = tokenBase.substr(dot + 1);
|
|
if (!IsComponentSuffix(suffix))
|
|
return;
|
|
|
|
outSwizzle = ComponentMap(suffix);
|
|
tokenBase = tokenBase.substr(0, dot);
|
|
}
|
|
|
|
static bool IsNumberToken(const std::string& t)
|
|
{
|
|
if (t.empty())
|
|
return false;
|
|
char* end = nullptr;
|
|
std::strtod(t.c_str(), &end);
|
|
return end && *end == 0;
|
|
}
|
|
|
|
static std::string HlslFloat(float f)
|
|
{
|
|
if (!std::isfinite(f))
|
|
return "0.0f";
|
|
|
|
char buf[64];
|
|
std::snprintf(buf, sizeof(buf), "%.9f", f);
|
|
|
|
char* end = buf + std::strlen(buf) - 1;
|
|
while (end > buf && *end == '0' && *(end - 1) != '.')
|
|
{
|
|
*end = '\0';
|
|
--end;
|
|
}
|
|
|
|
if (std::strchr(buf, '.') == nullptr)
|
|
std::strcat(buf, ".0");
|
|
|
|
std::strcat(buf, "f");
|
|
return buf;
|
|
}
|
|
|
|
static bool ParseFloat4Initializer(const std::string& text, float v[4])
|
|
{
|
|
size_t b = text.find('{');
|
|
size_t e = text.find('}', b == std::string::npos ? 0 : b);
|
|
if (b == std::string::npos || e == std::string::npos || e <= b)
|
|
return false;
|
|
|
|
std::vector<std::string> args = SplitArgs(text.substr(b + 1, e - b - 1));
|
|
for (int i = 0; i < 4; ++i)
|
|
v[i] = (i < (int)args.size()) ? (float)std::atof(args[i].c_str()) : 0.0f;
|
|
|
|
return true;
|
|
}
|
|
|
|
static std::string ReplaceNonIdentChars(const std::string& s)
|
|
{
|
|
std::string out;
|
|
out.reserve(s.size() + 8);
|
|
|
|
for (char c : s)
|
|
{
|
|
if (std::isalnum((unsigned char)c) || c == '_')
|
|
out.push_back(c);
|
|
else
|
|
out.push_back('_');
|
|
}
|
|
|
|
if (out.empty())
|
|
out = "arb_id";
|
|
|
|
if (std::isdigit((unsigned char)out[0]))
|
|
out = std::string("arb_") + out;
|
|
|
|
return out;
|
|
}
|
|
|
|
static bool IsHlslKeyword(const std::string& s)
|
|
{
|
|
static const char* kWords[] =
|
|
{
|
|
"asm","asm_fragment","bool","break","Buffer","ByteAddressBuffer","case","cbuffer",
|
|
"centroid","class","column_major","compile","compile_fragment","const","continue",
|
|
"ComputeShader","ConsumeStructuredBuffer","default","discard","do","double","DomainShader",
|
|
"dword","else","export","extern","false","float","for","fxgroup","GeometryShader",
|
|
"groupshared","half","HullShader","if","in","inline","inout","InputPatch","int",
|
|
"interface","line","lineadj","linear","LineStream","matrix","min16float","min10float",
|
|
"min16int","min12int","min16uint","namespace","nointerpolation","noperspective","NULL",
|
|
"out","OutputPatch","packoffset","pass","pixelfragment","PixelShader","point","PointStream",
|
|
"precise","RasterizerOrderedBuffer","RasterizerOrderedByteAddressBuffer",
|
|
"RasterizerOrderedStructuredBuffer","RasterizerOrderedTexture1D",
|
|
"RasterizerOrderedTexture1DArray","RasterizerOrderedTexture2D",
|
|
"RasterizerOrderedTexture2DArray","RasterizerOrderedTexture3D","register",
|
|
"return","row_major","RWBuffer","RWByteAddressBuffer","RWStructuredBuffer",
|
|
"RWTexture1D","RWTexture1DArray","RWTexture2D","RWTexture2DArray","RWTexture3D",
|
|
"sample","sampler","SamplerState","SamplerComparisonState","shared","snorm","stateblock",
|
|
"stateblock_state","static","string","struct","switch","tbuffer","technique","technique10",
|
|
"technique11","texture","Texture1D","Texture1DArray","Texture2D","Texture2DArray","Texture2DMS",
|
|
"Texture2DMSArray","Texture3D","TextureCube","TextureCubeArray","true","typedef","triangle",
|
|
"triangleadj","TriangleStream","uint","uniform","unorm","unsigned","vector","vertexfragment",
|
|
"VertexShader","void","volatile","while"
|
|
};
|
|
|
|
const std::string u = Upper(s);
|
|
for (const char* kw : kWords)
|
|
{
|
|
if (u == Upper(std::string(kw)))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static std::string SanitizeIdentifier(const std::string& s)
|
|
{
|
|
std::string out = ReplaceNonIdentChars(s);
|
|
if (IsHlslKeyword(out))
|
|
out = "arb_" + out;
|
|
return out;
|
|
}
|
|
|
|
class ARBTranslator
|
|
{
|
|
public:
|
|
explicit ARBTranslator(GLenum target) : m_target(target) {}
|
|
|
|
bool Translate(const std::string& source, std::string& outHlsl, std::string& outError, GLint& outErrorPosition)
|
|
{
|
|
m_tempDecls.clear();
|
|
m_paramDecls.clear();
|
|
m_instructions.clear();
|
|
m_error.clear();
|
|
m_errorPosition = -1;
|
|
m_symbolMap.clear();
|
|
|
|
std::vector<std::string> lines = LogicalLines(source);
|
|
for (size_t lineIndex = 0; lineIndex < lines.size(); ++lineIndex)
|
|
{
|
|
if (!ParseLine(lines[lineIndex], (GLint)lineIndex))
|
|
{
|
|
outError = m_error;
|
|
outErrorPosition = m_errorPosition;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
outHlsl = (m_target == GL_VERTEX_PROGRAM_ARB) ? BuildVertexHLSL() : BuildFragmentHLSL();
|
|
outError.clear();
|
|
outErrorPosition = -1;
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
GLenum m_target = 0;
|
|
std::vector<std::string> m_tempDecls;
|
|
std::vector<std::string> m_paramDecls;
|
|
std::vector<std::string> m_instructions;
|
|
std::string m_error;
|
|
GLint m_errorPosition = -1;
|
|
std::unordered_map<std::string, std::string> m_symbolMap;
|
|
|
|
bool Fail(const std::string& msg, GLint pos)
|
|
{
|
|
m_error = msg;
|
|
m_errorPosition = pos;
|
|
return false;
|
|
}
|
|
|
|
std::string MapUserSymbol(const std::string& raw)
|
|
{
|
|
const std::string key = Trim(raw);
|
|
auto it = m_symbolMap.find(key);
|
|
if (it != m_symbolMap.end())
|
|
return it->second;
|
|
|
|
const std::string safe = SanitizeIdentifier(key);
|
|
m_symbolMap.emplace(key, safe);
|
|
return safe;
|
|
}
|
|
|
|
static bool ParseBracketIndex(const std::string& s, const char* prefix, int& outIdx)
|
|
{
|
|
if (!StartsWithNoCase(s, prefix))
|
|
return false;
|
|
|
|
const size_t p = std::strlen(prefix);
|
|
const size_t end = s.find(']', p);
|
|
if (end == std::string::npos)
|
|
return false;
|
|
|
|
outIdx = std::atoi(s.c_str() + p);
|
|
return true;
|
|
}
|
|
|
|
std::string MatrixRowExpr(const char* matName, int row)
|
|
{
|
|
row = std::max(0, std::min(3, row));
|
|
std::ostringstream ss;
|
|
ss << "float4(" << matName << "[" << row << "][0], "
|
|
<< matName << "[" << row << "][1], "
|
|
<< matName << "[" << row << "][2], "
|
|
<< matName << "[" << row << "][3])";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string MatrixNameFromARB(const std::string& token)
|
|
{
|
|
if (StartsWithNoCase(token, "state.matrix.mvp"))
|
|
return "gMVP";
|
|
if (StartsWithNoCase(token, "state.matrix.modelview"))
|
|
return "gModelMatrix";
|
|
if (StartsWithNoCase(token, "state.matrix.texture[0]"))
|
|
return "gTexMat0";
|
|
if (StartsWithNoCase(token, "state.matrix.texture[1]"))
|
|
return "gTexMat1";
|
|
return std::string();
|
|
}
|
|
|
|
bool ExpandParamInitializerItems(const std::string& init, std::vector<std::string>& outItems, GLint pos)
|
|
{
|
|
size_t b = init.find('{');
|
|
size_t e = init.rfind('}');
|
|
if (b == std::string::npos || e == std::string::npos || e <= b)
|
|
return false;
|
|
|
|
std::vector<std::string> items = SplitArgs(init.substr(b + 1, e - b - 1));
|
|
for (const std::string& raw : items)
|
|
{
|
|
std::string t = Trim(raw);
|
|
if (t.empty())
|
|
continue;
|
|
|
|
float v[4];
|
|
if (ParseFloat4Initializer("{" + t + "}", v))
|
|
{
|
|
std::ostringstream fs;
|
|
fs << "float4(" << HlslFloat(v[0]) << ", " << HlslFloat(v[1]) << ", "
|
|
<< HlslFloat(v[2]) << ", " << HlslFloat(v[3]) << ")";
|
|
outItems.push_back(fs.str());
|
|
continue;
|
|
}
|
|
|
|
if (IsNumberToken(t))
|
|
{
|
|
outItems.push_back("float4(" + t + ", " + t + ", " + t + ", " + t + ")");
|
|
continue;
|
|
}
|
|
|
|
// PARAM mvp[4] = { state.matrix.mvp };
|
|
std::string mat = MatrixNameFromARB(t);
|
|
if (!mat.empty())
|
|
{
|
|
for (int i = 0; i < 4; ++i)
|
|
outItems.push_back(MatrixRowExpr(mat.c_str(), i));
|
|
continue;
|
|
}
|
|
|
|
// PARAM foo[4] = { state.matrix.mvp.row[0], ... }
|
|
if (StartsWithNoCase(t, "state.matrix.mvp.row["))
|
|
{
|
|
int row = 0;
|
|
if (ParseBracketIndex(t, "state.matrix.mvp.row[", row))
|
|
{
|
|
outItems.push_back(MatrixRowExpr("gMVP", row));
|
|
continue;
|
|
}
|
|
}
|
|
if (StartsWithNoCase(t, "state.matrix.modelview.row["))
|
|
{
|
|
int row = 0;
|
|
if (ParseBracketIndex(t, "state.matrix.modelview.row[", row))
|
|
{
|
|
outItems.push_back(MatrixRowExpr("gModelMatrix", row));
|
|
continue;
|
|
}
|
|
}
|
|
if (StartsWithNoCase(t, "state.matrix.texture[0].row["))
|
|
{
|
|
int row = 0;
|
|
if (ParseBracketIndex(t, "state.matrix.texture[0].row[", row))
|
|
{
|
|
outItems.push_back(MatrixRowExpr("gTexMat0", row));
|
|
continue;
|
|
}
|
|
}
|
|
if (StartsWithNoCase(t, "state.matrix.texture[1].row["))
|
|
{
|
|
int row = 0;
|
|
if (ParseBracketIndex(t, "state.matrix.texture[1].row[", row))
|
|
{
|
|
outItems.push_back(MatrixRowExpr("gTexMat1", row));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// PARAM a[4] = { program.local[0..3] }
|
|
if (StartsWithNoCase(t, "program.local["))
|
|
{
|
|
size_t dotdot = t.find("..");
|
|
size_t lbr = t.find('[');
|
|
size_t rbr = t.find(']');
|
|
if (dotdot != std::string::npos && lbr != std::string::npos && rbr != std::string::npos)
|
|
{
|
|
int a = std::atoi(t.c_str() + lbr + 1);
|
|
int bidx = std::atoi(t.c_str() + dotdot + 2);
|
|
if (bidx < a)
|
|
std::swap(a, bidx);
|
|
|
|
for (int i = a; i <= bidx; ++i)
|
|
{
|
|
char buf[64];
|
|
std::snprintf(buf, sizeof(buf), (m_target == GL_VERTEX_PROGRAM_ARB) ? "gARBLocalVP[%d]" : "gARBLocalFP[%d]", i);
|
|
outItems.push_back(buf);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (StartsWithNoCase(t, "program.env["))
|
|
{
|
|
size_t dotdot = t.find("..");
|
|
size_t lbr = t.find('[');
|
|
size_t rbr = t.find(']');
|
|
if (dotdot != std::string::npos && lbr != std::string::npos && rbr != std::string::npos)
|
|
{
|
|
int a = std::atoi(t.c_str() + lbr + 1);
|
|
int bidx = std::atoi(t.c_str() + dotdot + 2);
|
|
if (bidx < a)
|
|
std::swap(a, bidx);
|
|
|
|
for (int i = a; i <= bidx; ++i)
|
|
{
|
|
char buf[64];
|
|
std::snprintf(buf, sizeof(buf), "gARBEnv[%d]", i);
|
|
outItems.push_back(buf);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
outItems.push_back(Src(t));
|
|
}
|
|
|
|
if (outItems.empty())
|
|
return Fail("PARAM initializer produced no items", pos);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ParseLine(const std::string& rawLine, GLint pos)
|
|
{
|
|
std::string line = Trim(rawLine);
|
|
if (line.empty())
|
|
return true;
|
|
|
|
if (StartsWithNoCase(line, "!!ARBvp") || StartsWithNoCase(line, "!!ARBfp")) return true;
|
|
if (StartsWithNoCase(line, "OPTION")) return true;
|
|
if (StartsWithNoCase(line, "END")) return true;
|
|
if (StartsWithNoCase(line, "OUTPUT") || StartsWithNoCase(line, "ATTRIB")) return true;
|
|
if (StartsWithNoCase(line, "ALIAS") || StartsWithNoCase(line, "ADDRESS")) return true;
|
|
|
|
size_t sp = line.find_first_of(" \t");
|
|
std::string op = (sp == std::string::npos) ? Upper(line) : Upper(line.substr(0, sp));
|
|
std::string rest = (sp == std::string::npos) ? std::string() : Trim(line.substr(sp + 1));
|
|
|
|
if (op == "TEMP")
|
|
{
|
|
std::vector<std::string> names = SplitArgs(rest);
|
|
for (const std::string& nRaw : names)
|
|
{
|
|
std::string n = Trim(nRaw);
|
|
if (!n.empty())
|
|
{
|
|
const std::string safe = MapUserSymbol(n);
|
|
m_tempDecls.push_back(" float4 " + safe + " = float4(0.0, 0.0, 0.0, 0.0);\n");
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (op == "PARAM")
|
|
return ParseParam(rest, pos);
|
|
|
|
std::vector<std::string> args = SplitArgs(rest);
|
|
|
|
if (op == "MOV" && args.size() == 2)
|
|
{
|
|
m_instructions.push_back(Assign(args[0], Src(args[1])));
|
|
return true;
|
|
}
|
|
|
|
if ((op == "ADD" || op == "SUB" || op == "MUL" || op == "MIN" || op == "MAX") && args.size() == 3)
|
|
{
|
|
std::string expr;
|
|
if (op == "ADD") expr = "(" + Src(args[1]) + " + " + Src(args[2]) + ")";
|
|
else if (op == "SUB") expr = "(" + Src(args[1]) + " - " + Src(args[2]) + ")";
|
|
else if (op == "MUL") expr = "(" + Src(args[1]) + " * " + Src(args[2]) + ")";
|
|
else if (op == "MIN") expr = "min(" + Src(args[1]) + ", " + Src(args[2]) + ")";
|
|
else expr = "max(" + Src(args[1]) + ", " + Src(args[2]) + ")";
|
|
|
|
m_instructions.push_back(Assign(args[0], expr));
|
|
return true;
|
|
}
|
|
|
|
if (op == "MAD" && args.size() == 4)
|
|
{
|
|
m_instructions.push_back(Assign(args[0], "(" + Src(args[1]) + " * " + Src(args[2]) + " + " + Src(args[3]) + ")"));
|
|
return true;
|
|
}
|
|
|
|
if (op == "DP3" && args.size() == 3)
|
|
{
|
|
std::string d = "dot((" + Src(args[1]) + ").xyz, (" + Src(args[2]) + ").xyz)";
|
|
m_instructions.push_back(Assign(args[0], "float4(" + d + ", " + d + ", " + d + ", " + d + ")"));
|
|
return true;
|
|
}
|
|
|
|
if (op == "DP4" && args.size() == 3)
|
|
{
|
|
std::string d = "dot((" + Src(args[1]) + "), (" + Src(args[2]) + "))";
|
|
m_instructions.push_back(Assign(args[0], "float4(" + d + ", " + d + ", " + d + ", " + d + ")"));
|
|
return true;
|
|
}
|
|
|
|
if (op == "RSQ" && args.size() == 2)
|
|
{
|
|
std::string r = "rsqrt(max(abs((" + Src(args[1]) + ").x), 1e-20))";
|
|
m_instructions.push_back(Assign(args[0], "float4(" + r + ", " + r + ", " + r + ", " + r + ")"));
|
|
return true;
|
|
}
|
|
|
|
if (op == "RCP" && args.size() == 2)
|
|
{
|
|
std::string r = "(1.0 / max(abs((" + Src(args[1]) + ").x), 1e-20))";
|
|
m_instructions.push_back(Assign(args[0], "float4(" + r + ", " + r + ", " + r + ", " + r + ")"));
|
|
return true;
|
|
}
|
|
|
|
if (op == "ABS" && args.size() == 2)
|
|
{
|
|
m_instructions.push_back(Assign(args[0], "abs(" + Src(args[1]) + ")"));
|
|
return true;
|
|
}
|
|
|
|
if (op == "LRP" && args.size() == 4)
|
|
{
|
|
m_instructions.push_back(Assign(args[0], "lerp(" + Src(args[3]) + ", " + Src(args[2]) + ", " + Src(args[1]) + ")"));
|
|
return true;
|
|
}
|
|
|
|
if (op == "TEX" || op == "TXP" || op == "TXB")
|
|
{
|
|
if (args.size() >= 4)
|
|
{
|
|
if (m_target != GL_FRAGMENT_PROGRAM_ARB)
|
|
return Fail("TEX/TXP/TXB only supported in ARBfp for this shim", pos);
|
|
|
|
m_instructions.push_back(EmitTex(op == "TXP", args[0], args[1], args[2], args[3]));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (op == "KIL" && args.size() == 1)
|
|
{
|
|
m_instructions.push_back(" clip((" + Src(args[0]) + ").x);\n");
|
|
return true;
|
|
}
|
|
|
|
// Keep build going; leave a comment instead of hard failure for unhandled ops.
|
|
m_instructions.push_back(" // Unsupported ARB op ignored by translator: " + line + "\n");
|
|
return true;
|
|
}
|
|
|
|
bool ParseParam(const std::string& rest, GLint pos)
|
|
{
|
|
size_t eq = rest.find('=');
|
|
if (eq == std::string::npos)
|
|
return Fail("PARAM without initializer is not supported", pos);
|
|
|
|
std::string namePart = Trim(rest.substr(0, eq));
|
|
std::string init = Trim(rest.substr(eq + 1));
|
|
|
|
// Array form: PARAM foo[4] = { ... };
|
|
size_t lbr = namePart.find('[');
|
|
size_t rbr = namePart.find(']', lbr == std::string::npos ? 0 : lbr);
|
|
if (lbr != std::string::npos && rbr != std::string::npos && rbr > lbr)
|
|
{
|
|
std::string baseName = Trim(namePart.substr(0, lbr));
|
|
std::string safeBase = MapUserSymbol(baseName);
|
|
int declaredCount = std::atoi(namePart.c_str() + lbr + 1);
|
|
if (declaredCount <= 0)
|
|
return Fail("PARAM array has invalid size", pos);
|
|
|
|
std::vector<std::string> items;
|
|
if (!ExpandParamInitializerItems(init, items, pos))
|
|
return false;
|
|
|
|
std::ostringstream ss;
|
|
ss << " const float4 " << safeBase << "[" << declaredCount << "] = {\n";
|
|
for (int i = 0; i < declaredCount; ++i)
|
|
{
|
|
const std::string item = (i < (int)items.size()) ? items[i] : "float4(0.0, 0.0, 0.0, 0.0)";
|
|
ss << " " << item;
|
|
if (i + 1 != declaredCount)
|
|
ss << ",";
|
|
ss << "\n";
|
|
}
|
|
ss << " };\n";
|
|
m_paramDecls.push_back(ss.str());
|
|
return true;
|
|
}
|
|
|
|
// Scalar float4 form
|
|
float v[4] = {};
|
|
if (ParseFloat4Initializer(init, v))
|
|
{
|
|
std::ostringstream ss;
|
|
ss << " const float4 " << MapUserSymbol(namePart) << " = float4("
|
|
<< HlslFloat(v[0]) << ", "
|
|
<< HlslFloat(v[1]) << ", "
|
|
<< HlslFloat(v[2]) << ", "
|
|
<< HlslFloat(v[3]) << ");\n";
|
|
m_paramDecls.push_back(ss.str());
|
|
return true;
|
|
}
|
|
|
|
// Single symbolic initializer, e.g. PARAM foo = state.matrix.mvp.row[0];
|
|
std::ostringstream ss;
|
|
ss << " const float4 " << MapUserSymbol(namePart) << " = " << Src(init) << ";\n";
|
|
m_paramDecls.push_back(ss.str());
|
|
return true;
|
|
}
|
|
|
|
std::string Dest(const std::string& token, std::string* outMask)
|
|
{
|
|
std::string t = Trim(token);
|
|
std::string mask;
|
|
SplitARBComponentSuffix(t, mask);
|
|
|
|
if (outMask)
|
|
*outMask = mask;
|
|
|
|
if (StartsWithNoCase(t, "result.color"))
|
|
return (m_target == GL_VERTEX_PROGRAM_ARB) ? "o.col" : "result_color";
|
|
|
|
if (StartsWithNoCase(t, "result.position"))
|
|
return "o.pos";
|
|
|
|
if (StartsWithNoCase(t, "result.fogcoord"))
|
|
return "o.fog";
|
|
|
|
if (StartsWithNoCase(t, "result.texcoord["))
|
|
{
|
|
int idx = 0;
|
|
ParseBracketIndex(t, "result.texcoord[", idx);
|
|
idx = std::max(0, std::min((int)QD3D12_ARB_MAX_TEXTURE_UNITS - 1, idx));
|
|
char buf[32];
|
|
std::snprintf(buf, sizeof(buf), "o.tc%d", idx);
|
|
return buf;
|
|
}
|
|
|
|
// Generic named temporaries/params
|
|
size_t arr = t.find('[');
|
|
if (arr != std::string::npos)
|
|
{
|
|
std::string base = Trim(t.substr(0, arr));
|
|
std::string tail = t.substr(arr);
|
|
return MapUserSymbol(base) + tail;
|
|
}
|
|
return MapUserSymbol(t);
|
|
}
|
|
|
|
std::string Src(const std::string& token)
|
|
{
|
|
std::string t = Trim(token);
|
|
if (t.empty())
|
|
return "float4(0.0, 0.0, 0.0, 0.0)";
|
|
|
|
bool negate = false;
|
|
if (t[0] == '-')
|
|
{
|
|
negate = true;
|
|
t = Trim(t.substr(1));
|
|
}
|
|
|
|
std::string swizzle;
|
|
SplitARBComponentSuffix(t, swizzle);
|
|
|
|
std::string base;
|
|
|
|
// Vertex inputs / aliases
|
|
if (StartsWithNoCase(t, "vertex.position"))
|
|
{
|
|
base = (m_target == GL_VERTEX_PROGRAM_ARB) ? "vertex_position" : "i.pos";
|
|
}
|
|
else if (StartsWithNoCase(t, "vertex.color"))
|
|
{
|
|
base = (m_target == GL_VERTEX_PROGRAM_ARB) ? "vertex_color" : "fragment_color";
|
|
}
|
|
else if (StartsWithNoCase(t, "vertex.normal"))
|
|
{
|
|
base = (m_target == GL_VERTEX_PROGRAM_ARB) ? "float4(i.normal, 0.0)" : "float4(0.0, 0.0, 1.0, 0.0)";
|
|
}
|
|
else if (StartsWithNoCase(t, "vertex.texcoord["))
|
|
{
|
|
int idx = 0;
|
|
ParseBracketIndex(t, "vertex.texcoord[", idx);
|
|
idx = std::max(0, std::min((int)QD3D12_ARB_MAX_TEXTURE_UNITS - 1, idx));
|
|
|
|
if (m_target == GL_VERTEX_PROGRAM_ARB)
|
|
{
|
|
if (idx == 0) base = "float4(i.uv0, 0.0, 1.0)";
|
|
else if (idx == 1) base = "float4(i.uv1, 0.0, 1.0)";
|
|
else base = "float4(0.0, 0.0, 0.0, 1.0)";
|
|
}
|
|
else
|
|
{
|
|
char buf[32];
|
|
std::snprintf(buf, sizeof(buf), "fragment_tc%d", idx);
|
|
base = buf;
|
|
}
|
|
}
|
|
else if (StartsWithNoCase(t, "vertex.texcoord"))
|
|
{
|
|
base = (m_target == GL_VERTEX_PROGRAM_ARB) ? "float4(i.uv0, 0.0, 1.0)" : "fragment_tc0";
|
|
}
|
|
else if (StartsWithNoCase(t, "vertex.attrib["))
|
|
{
|
|
int idx = 0;
|
|
ParseBracketIndex(t, "vertex.attrib[", idx);
|
|
idx = std::max(0, std::min((int)QD3D12_ARB_MAX_VERTEX_ATTRIBS - 1, idx));
|
|
char buf[32];
|
|
std::snprintf(buf, sizeof(buf), "vertex_attrib%d", idx);
|
|
base = buf;
|
|
}
|
|
// Program constants
|
|
else if (StartsWithNoCase(t, "program.env["))
|
|
{
|
|
int idx = 0;
|
|
ParseBracketIndex(t, "program.env[", idx);
|
|
idx = std::max(0, std::min((int)QD3D12_ARB_MAX_PROGRAM_PARAMETERS - 1, idx));
|
|
char buf[48];
|
|
std::snprintf(buf, sizeof(buf), "gARBEnv[%d]", idx);
|
|
base = buf;
|
|
}
|
|
else if (StartsWithNoCase(t, "program.local["))
|
|
{
|
|
int idx = 0;
|
|
ParseBracketIndex(t, "program.local[", idx);
|
|
idx = std::max(0, std::min((int)QD3D12_ARB_MAX_PROGRAM_PARAMETERS - 1, idx));
|
|
char buf[64];
|
|
std::snprintf(buf, sizeof(buf), (m_target == GL_VERTEX_PROGRAM_ARB) ? "gARBLocalVP[%d]" : "gARBLocalFP[%d]", idx);
|
|
base = buf;
|
|
}
|
|
// State matrices
|
|
else if (StartsWithNoCase(t, "state.matrix.mvp.row["))
|
|
{
|
|
int row = 0;
|
|
ParseBracketIndex(t, "state.matrix.mvp.row[", row);
|
|
base = MatrixRowExpr("gMVP", row);
|
|
}
|
|
else if (StartsWithNoCase(t, "state.matrix.modelview.row["))
|
|
{
|
|
int row = 0;
|
|
ParseBracketIndex(t, "state.matrix.modelview.row[", row);
|
|
base = MatrixRowExpr("gModelMatrix", row);
|
|
}
|
|
else if (StartsWithNoCase(t, "state.matrix.texture[0].row["))
|
|
{
|
|
int row = 0;
|
|
ParseBracketIndex(t, "state.matrix.texture[0].row[", row);
|
|
base = MatrixRowExpr("gTexMat0", row);
|
|
}
|
|
else if (StartsWithNoCase(t, "state.matrix.texture[1].row["))
|
|
{
|
|
int row = 0;
|
|
ParseBracketIndex(t, "state.matrix.texture[1].row[", row);
|
|
base = MatrixRowExpr("gTexMat1", row);
|
|
}
|
|
else if (StartsWithNoCase(t, "state.matrix.mvp"))
|
|
{
|
|
base = MatrixRowExpr("gMVP", 0);
|
|
}
|
|
else if (StartsWithNoCase(t, "state.matrix.modelview"))
|
|
{
|
|
base = MatrixRowExpr("gModelMatrix", 0);
|
|
}
|
|
else if (StartsWithNoCase(t, "state.matrix.texture[0]"))
|
|
{
|
|
base = MatrixRowExpr("gTexMat0", 0);
|
|
}
|
|
else if (StartsWithNoCase(t, "state.matrix.texture[1]"))
|
|
{
|
|
base = MatrixRowExpr("gTexMat1", 0);
|
|
}
|
|
// Fragment inputs
|
|
else if (StartsWithNoCase(t, "fragment.color"))
|
|
{
|
|
base = "fragment_color";
|
|
}
|
|
else if (StartsWithNoCase(t, "fragment.position"))
|
|
{
|
|
base = "i.pos";
|
|
}
|
|
else if (StartsWithNoCase(t, "fragment.texcoord["))
|
|
{
|
|
int idx = 0;
|
|
ParseBracketIndex(t, "fragment.texcoord[", idx);
|
|
idx = std::max(0, std::min((int)QD3D12_ARB_MAX_TEXTURE_UNITS - 1, idx));
|
|
char buf[32];
|
|
std::snprintf(buf, sizeof(buf), "fragment_tc%d", idx);
|
|
base = buf;
|
|
}
|
|
else if (StartsWithNoCase(t, "result.color"))
|
|
{
|
|
base = (m_target == GL_VERTEX_PROGRAM_ARB) ? "o.col" : "result_color";
|
|
}
|
|
else if (StartsWithNoCase(t, "result.position"))
|
|
{
|
|
base = "o.pos";
|
|
}
|
|
else if (IsNumberToken(t))
|
|
{
|
|
base = "float4(" + t + ", " + t + ", " + t + ", " + t + ")";
|
|
}
|
|
else
|
|
{
|
|
size_t arr = t.find('[');
|
|
if (arr != std::string::npos)
|
|
{
|
|
std::string baseName = Trim(t.substr(0, arr));
|
|
std::string tail = t.substr(arr);
|
|
base = MapUserSymbol(baseName) + tail;
|
|
}
|
|
else
|
|
{
|
|
base = MapUserSymbol(t);
|
|
}
|
|
}
|
|
|
|
if (!swizzle.empty())
|
|
{
|
|
if (swizzle.size() == 1)
|
|
base += "." + swizzle + swizzle + swizzle + swizzle;
|
|
else
|
|
base += "." + swizzle;
|
|
}
|
|
|
|
if (negate)
|
|
base = "(-" + base + ")";
|
|
|
|
return base;
|
|
}
|
|
|
|
std::string Assign(const std::string& dstToken, const std::string& rhs)
|
|
{
|
|
std::string mask;
|
|
std::string d = Dest(dstToken, &mask);
|
|
std::ostringstream ss;
|
|
|
|
if (mask.empty())
|
|
{
|
|
ss << " " << d << " = " << rhs << ";\n";
|
|
}
|
|
else if (mask.size() == 1)
|
|
{
|
|
// Scalar masked write; use .x from RHS scalarized/splatted value.
|
|
ss << " " << d << "." << mask << " = (" << rhs << ").x;\n";
|
|
}
|
|
else
|
|
{
|
|
ss << " " << d << "." << mask << " = (" << rhs << ")." << mask << ";\n";
|
|
}
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
int TextureIndex(const std::string& token)
|
|
{
|
|
int idx = 0;
|
|
if (StartsWithNoCase(token, "texture["))
|
|
ParseBracketIndex(token, "texture[", idx);
|
|
return std::max(0, std::min((int)QD3D12_ARB_MAX_TEXTURE_UNITS - 1, idx));
|
|
}
|
|
|
|
std::string EmitTex(bool projected, const std::string& dst, const std::string& coordToken, const std::string& texToken, const std::string& targetToken)
|
|
{
|
|
std::string coord = Src(coordToken);
|
|
std::string target = Upper(targetToken);
|
|
std::string sample;
|
|
|
|
if (target.find("CUBE") != std::string::npos)
|
|
{
|
|
sample = "float4(QD3D12ARB_NormalizeSafe((" + coord + ").xyz) * 0.5 + 0.5, 1.0)";
|
|
}
|
|
else
|
|
{
|
|
const int idx = TextureIndex(texToken);
|
|
char texName[32];
|
|
char sampName[32];
|
|
std::snprintf(texName, sizeof(texName), "gTex%d", idx);
|
|
std::snprintf(sampName, sizeof(sampName), "gSamp%d", idx);
|
|
|
|
std::string uv;
|
|
if (projected)
|
|
uv = "((" + coord + ").xy / max(abs((" + coord + ").w), 1e-6))";
|
|
else
|
|
uv = "(" + coord + ").xy";
|
|
|
|
sample = std::string(texName) + ".Sample(" + sampName + ", " + uv + ")";
|
|
}
|
|
|
|
return Assign(dst, sample);
|
|
}
|
|
|
|
std::string CommonHLSLPrefix()
|
|
{
|
|
return R"HLSL(
|
|
cbuffer DrawCB : register(b0)
|
|
{
|
|
float4x4 gMVP;
|
|
float4x4 gPrevMVP;
|
|
float4x4 gModelMatrix;
|
|
float4x4 gTexMat0;
|
|
float4x4 gTexMat1;
|
|
|
|
float gAlphaRef;
|
|
float gUseTex0;
|
|
float gUseTex1;
|
|
float gTex1IsLightmap;
|
|
float gTexEnvMode0;
|
|
float gTexEnvMode1;
|
|
float geometryFlag;
|
|
float gRoughness;
|
|
|
|
float gFogEnabled;
|
|
float gFogMode;
|
|
float gFogDensity;
|
|
float gFogStart;
|
|
|
|
float gFogEnd;
|
|
float gPointSize;
|
|
float gMaterialType;
|
|
float gAlphaFunc;
|
|
|
|
float4 gFogColor;
|
|
|
|
float2 gRenderSize;
|
|
float2 gInvRenderSize;
|
|
float2 gJitterPixels;
|
|
float2 gPrevJitterPixels;
|
|
float4 gMotionPad;
|
|
float4 gMaterialMapPad;
|
|
float4 gTexComb0RGB;
|
|
float4 gTexComb0Alpha;
|
|
float4 gTexComb0Operand;
|
|
float4 gTexEnvColor0;
|
|
float4 gTexComb1RGB;
|
|
float4 gTexComb1Alpha;
|
|
float4 gTexComb1Operand;
|
|
float4 gTexEnvColor1;
|
|
float4 gCameraPomPad;
|
|
float4 gNeuralPomPad;
|
|
|
|
float4 gARBEnv[128];
|
|
float4 gARBLocalVP[128];
|
|
float4 gARBLocalFP[128];
|
|
};
|
|
|
|
#define gCameraWorldPos gCameraPomPad.xyz
|
|
#define gCameraPomValid gCameraPomPad.w
|
|
|
|
float3 QD3D12ARB_NormalizeSafe(float3 v)
|
|
{
|
|
return v * rsqrt(max(dot(v, v), 1e-20));
|
|
}
|
|
|
|
float QD3D12ARB_ComputeFogFactor(float fogCoord)
|
|
{
|
|
if (gFogEnabled < 0.5)
|
|
return 1.0;
|
|
|
|
float f;
|
|
if (gFogMode < 0.5)
|
|
{
|
|
float denom = max(gFogEnd - gFogStart, 0.00001);
|
|
f = (gFogEnd - fogCoord) / denom;
|
|
}
|
|
else if (gFogMode < 1.5)
|
|
{
|
|
f = exp(-gFogDensity * fogCoord);
|
|
}
|
|
else
|
|
{
|
|
float d = gFogDensity * fogCoord;
|
|
f = exp(-(d * d));
|
|
}
|
|
return saturate(f);
|
|
}
|
|
|
|
float4 QD3D12ARB_ApplyFog(float4 color, float fogCoord)
|
|
{
|
|
float fogFactor = QD3D12ARB_ComputeFogFactor(fogCoord);
|
|
color.rgb = lerp(gFogColor.rgb, color.rgb, fogFactor);
|
|
return color;
|
|
}
|
|
|
|
)HLSL";
|
|
}
|
|
|
|
std::string BuildVertexHLSL()
|
|
{
|
|
std::ostringstream ss;
|
|
ss << CommonHLSLPrefix();
|
|
ss << R"HLSL(
|
|
struct VSIn
|
|
{
|
|
float3 pos : POSITION;
|
|
float3 normal : NORMAL;
|
|
float2 uv0 : TEXCOORD0;
|
|
float2 uv1 : TEXCOORD1;
|
|
float4 col : COLOR0;
|
|
float3 tangent : TANGENT;
|
|
float3 bitangent : BINORMAL;
|
|
};
|
|
|
|
struct VSOut
|
|
{
|
|
float4 pos : SV_Position;
|
|
float4 tc0 : TEXCOORD0;
|
|
float4 tc1 : TEXCOORD1;
|
|
float4 tc2 : TEXCOORD2;
|
|
float4 tc3 : TEXCOORD3;
|
|
float4 tc4 : TEXCOORD4;
|
|
float4 tc5 : TEXCOORD5;
|
|
float4 tc6 : TEXCOORD6;
|
|
float4 tc7 : TEXCOORD7;
|
|
float4 col : COLOR0;
|
|
float4 fog : TEXCOORD8;
|
|
};
|
|
|
|
VSOut VSMain(VSIn i)
|
|
{
|
|
VSOut o = (VSOut)0;
|
|
o.pos = mul(gMVP, float4(i.pos, 1.0));
|
|
float4 worldPos = mul(gModelMatrix, float4(i.pos, 1.0));
|
|
o.tc0 = float4(i.uv0, 0.0, 1.0);
|
|
o.tc1 = float4(i.uv1, 0.0, 1.0);
|
|
o.tc2 = float4(0.0, 0.0, 0.0, 1.0);
|
|
o.tc3 = float4(0.0, 0.0, 0.0, 1.0);
|
|
o.tc4 = float4(0.0, 0.0, 0.0, 1.0);
|
|
o.tc5 = float4(0.0, 0.0, 0.0, 1.0);
|
|
o.tc6 = float4(0.0, 0.0, 0.0, 1.0);
|
|
o.tc7 = float4(0.0, 0.0, 0.0, 1.0);
|
|
o.col = i.col;
|
|
o.fog = float4((gCameraPomValid > 0.5)
|
|
? length(worldPos.xyz - gCameraWorldPos)
|
|
: abs(o.pos.z / max(abs(o.pos.w), 0.00001)), 0.0, 0.0, 1.0);
|
|
|
|
float4 vertex_position = float4(i.pos, 1.0);
|
|
float4 vertex_color = i.col;
|
|
float4 vertex_attrib0 = vertex_position;
|
|
float4 vertex_attrib1 = float4(0.0, 0.0, 0.0, 1.0);
|
|
float4 vertex_attrib2 = float4(i.normal, 0.0);
|
|
float4 vertex_attrib3 = i.col;
|
|
float4 vertex_attrib4 = float4(0.0, 0.0, 0.0, 1.0);
|
|
float4 vertex_attrib5 = float4(0.0, 0.0, 0.0, 1.0);
|
|
float4 vertex_attrib6 = float4(0.0, 0.0, 0.0, 1.0);
|
|
float4 vertex_attrib7 = float4(0.0, 0.0, 0.0, 1.0);
|
|
float4 vertex_attrib8 = float4(i.uv0, 0.0, 1.0);
|
|
float4 vertex_attrib9 = float4(i.normal, 0.0);
|
|
float4 vertex_attrib10 = float4(i.tangent, 0.0);
|
|
float4 vertex_attrib11 = float4(i.bitangent, 0.0);
|
|
float4 vertex_attrib12 = float4(0.0, 0.0, 0.0, 1.0);
|
|
float4 vertex_attrib13 = float4(0.0, 0.0, 0.0, 1.0);
|
|
float4 vertex_attrib14 = float4(0.0, 0.0, 0.0, 1.0);
|
|
float4 vertex_attrib15 = float4(0.0, 0.0, 0.0, 1.0);
|
|
)HLSL";
|
|
for (const std::string& d : m_tempDecls) ss << d;
|
|
for (const std::string& d : m_paramDecls) ss << d;
|
|
for (const std::string& i : m_instructions) ss << i;
|
|
ss << " return o;\n}\n";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string BuildFragmentHLSL()
|
|
{
|
|
std::ostringstream ss;
|
|
ss << CommonHLSLPrefix();
|
|
|
|
for (UINT i = 0; i < QD3D12_ARB_MAX_TEXTURE_UNITS; ++i)
|
|
ss << "Texture2D gTex" << i << " : register(t" << i << ");\n";
|
|
for (UINT i = 0; i < QD3D12_ARB_MAX_TEXTURE_UNITS; ++i)
|
|
ss << "SamplerState gSamp" << i << " : register(s" << i << ");\n";
|
|
|
|
ss << R"HLSL(
|
|
struct PSIn
|
|
{
|
|
float4 pos : SV_Position;
|
|
float4 tc0 : TEXCOORD0;
|
|
float4 tc1 : TEXCOORD1;
|
|
float4 tc2 : TEXCOORD2;
|
|
float4 tc3 : TEXCOORD3;
|
|
float4 tc4 : TEXCOORD4;
|
|
float4 tc5 : TEXCOORD5;
|
|
float4 tc6 : TEXCOORD6;
|
|
float4 tc7 : TEXCOORD7;
|
|
float4 col : COLOR0;
|
|
float4 fog : TEXCOORD8;
|
|
};
|
|
|
|
float4 PSMain(PSIn i) : SV_Target0
|
|
{
|
|
float4 result_color = float4(0.0, 0.0, 0.0, 1.0);
|
|
float4 fragment_color = i.col;
|
|
float4 fragment_tc0 = i.tc0;
|
|
float4 fragment_tc1 = i.tc1;
|
|
float4 fragment_tc2 = i.tc2;
|
|
float4 fragment_tc3 = i.tc3;
|
|
float4 fragment_tc4 = i.tc4;
|
|
float4 fragment_tc5 = i.tc5;
|
|
float4 fragment_tc6 = i.tc6;
|
|
float4 fragment_tc7 = i.tc7;
|
|
)HLSL";
|
|
for (const std::string& d : m_tempDecls) ss << d;
|
|
for (const std::string& d : m_paramDecls) ss << d;
|
|
for (const std::string& i : m_instructions) ss << i;
|
|
ss << " return QD3D12ARB_ApplyFog(result_color, i.fog.x);\n}\n";
|
|
return ss.str();
|
|
}
|
|
};
|
|
|
|
static ComPtr<ID3DBlob> CompileHLSL(const char* source, const char* entry, const char* target, std::string& error)
|
|
{
|
|
UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
|
|
#if defined(_DEBUG)
|
|
flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
|
|
#endif
|
|
|
|
ComPtr<ID3DBlob> blob;
|
|
ComPtr<ID3DBlob> err;
|
|
HRESULT hr = D3DCompile(source, std::strlen(source), nullptr, nullptr, nullptr, entry, target, flags, 0, &blob, &err);
|
|
if (FAILED(hr))
|
|
{
|
|
error = err ? (const char*)err->GetBufferPointer() : "unknown HLSL compile error";
|
|
return nullptr;
|
|
}
|
|
|
|
error.clear();
|
|
return blob;
|
|
}
|
|
|
|
static ARBProgram* GetBoundProgram(GLenum target)
|
|
{
|
|
if (!IsProgramTarget(target))
|
|
return nullptr;
|
|
|
|
const GLuint id = BoundForTarget(target);
|
|
if (id == 0)
|
|
return nullptr;
|
|
|
|
auto& map = ProgramsForTarget(target);
|
|
auto it = map.find(id);
|
|
if (it == map.end())
|
|
return nullptr;
|
|
|
|
return &it->second;
|
|
}
|
|
|
|
static ID3DBlob* EnsureCompiled(GLenum target)
|
|
{
|
|
ARBProgram* p = GetBoundProgram(target);
|
|
if (!p)
|
|
return nullptr;
|
|
|
|
if (p->blob)
|
|
return p->blob.Get();
|
|
|
|
if (p->compileAttempted && !p->blob)
|
|
return nullptr;
|
|
|
|
p->compileAttempted = true;
|
|
p->hlsl.clear();
|
|
p->errorString.clear();
|
|
p->errorPosition = -1;
|
|
|
|
ARBTranslator translator(target);
|
|
std::string hlsl;
|
|
std::string translateError;
|
|
GLint translateErrorPosition = -1;
|
|
if (!translator.Translate(p->source, hlsl, translateError, translateErrorPosition))
|
|
{
|
|
p->errorString = translateError;
|
|
p->errorPosition = translateErrorPosition;
|
|
g_lastProgramErrorString = translateError;
|
|
g_lastProgramErrorPosition = translateErrorPosition;
|
|
SetError(GL_INVALID_OPERATION);
|
|
return nullptr;
|
|
}
|
|
|
|
p->hlsl = hlsl;
|
|
std::string compileError;
|
|
p->blob = CompileHLSL(
|
|
hlsl.c_str(),
|
|
(target == GL_VERTEX_PROGRAM_ARB) ? "VSMain" : "PSMain",
|
|
(target == GL_VERTEX_PROGRAM_ARB) ? "vs_5_0" : "ps_5_0",
|
|
compileError);
|
|
|
|
if (!p->blob)
|
|
{
|
|
p->errorString = compileError;
|
|
p->errorPosition = -1;
|
|
g_lastProgramErrorString = compileError;
|
|
g_lastProgramErrorPosition = -1;
|
|
SetError(GL_INVALID_OPERATION);
|
|
return nullptr;
|
|
}
|
|
|
|
return p->blob.Get();
|
|
}
|
|
|
|
static void ResetEnvDefaults()
|
|
{
|
|
for (UINT i = 0; i < QD3D12_ARB_MAX_PROGRAM_PARAMETERS; ++i)
|
|
{
|
|
g_env[i][0] = 0.0f;
|
|
g_env[i][1] = 0.0f;
|
|
g_env[i][2] = 0.0f;
|
|
g_env[i][3] = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
void QD3D12ARB_Init(void)
|
|
{
|
|
ResetEnvDefaults();
|
|
}
|
|
|
|
void QD3D12ARB_Shutdown(void)
|
|
{
|
|
g_vertexPrograms.clear();
|
|
g_fragmentPrograms.clear();
|
|
g_nextProgramId = 1;
|
|
g_boundVertexProgram = 0;
|
|
g_boundFragmentProgram = 0;
|
|
g_vertexProgramEnabled = false;
|
|
g_fragmentProgramEnabled = false;
|
|
g_lastError = GL_NO_ERROR;
|
|
g_lastProgramErrorPosition = -1;
|
|
g_lastProgramErrorString.clear();
|
|
ResetEnvDefaults();
|
|
}
|
|
|
|
void QD3D12ARB_DeviceLost(void)
|
|
{
|
|
for (auto& kv : g_vertexPrograms) kv.second.blob.Reset();
|
|
for (auto& kv : g_fragmentPrograms) kv.second.blob.Reset();
|
|
}
|
|
|
|
void QD3D12ARB_SetEnabled(GLenum cap, bool enabled)
|
|
{
|
|
if (cap == GL_VERTEX_PROGRAM_ARB)
|
|
g_vertexProgramEnabled = enabled;
|
|
else if (cap == GL_FRAGMENT_PROGRAM_ARB)
|
|
g_fragmentProgramEnabled = enabled;
|
|
}
|
|
|
|
bool QD3D12ARB_IsVertexEnabled(void) { return g_vertexProgramEnabled; }
|
|
bool QD3D12ARB_IsFragmentEnabled(void) { return g_fragmentProgramEnabled; }
|
|
|
|
bool QD3D12ARB_IsActive(void)
|
|
{
|
|
return g_vertexProgramEnabled && g_fragmentProgramEnabled && g_boundVertexProgram != 0 && g_boundFragmentProgram != 0;
|
|
}
|
|
|
|
GLuint QD3D12ARB_GetBoundVertexProgram(void) { return g_boundVertexProgram; }
|
|
GLuint QD3D12ARB_GetBoundFragmentProgram(void) { return g_boundFragmentProgram; }
|
|
|
|
uint32_t QD3D12ARB_GetBoundVertexRevision(void)
|
|
{
|
|
ARBProgram* p = GetBoundProgram(GL_VERTEX_PROGRAM_ARB);
|
|
return p ? p->revision : 0;
|
|
}
|
|
|
|
uint32_t QD3D12ARB_GetBoundFragmentRevision(void)
|
|
{
|
|
ARBProgram* p = GetBoundProgram(GL_FRAGMENT_PROGRAM_ARB);
|
|
return p ? p->revision : 0;
|
|
}
|
|
|
|
ID3DBlob* QD3D12ARB_GetVertexShaderBlob(void)
|
|
{
|
|
return EnsureCompiled(GL_VERTEX_PROGRAM_ARB);
|
|
}
|
|
|
|
ID3DBlob* QD3D12ARB_GetFragmentShaderBlob(void)
|
|
{
|
|
return EnsureCompiled(GL_FRAGMENT_PROGRAM_ARB);
|
|
}
|
|
|
|
void QD3D12ARB_FillDrawConstantArrays(QD3D12ARBDrawConstantArrays* outArrays)
|
|
{
|
|
if (!outArrays)
|
|
return;
|
|
|
|
std::memset(outArrays, 0, sizeof(*outArrays));
|
|
std::memcpy(outArrays->env, g_env, sizeof(outArrays->env));
|
|
|
|
if (ARBProgram* vp = GetBoundProgram(GL_VERTEX_PROGRAM_ARB))
|
|
std::memcpy(outArrays->vertexLocal, vp->local, sizeof(outArrays->vertexLocal));
|
|
|
|
if (ARBProgram* fp = GetBoundProgram(GL_FRAGMENT_PROGRAM_ARB))
|
|
std::memcpy(outArrays->fragmentLocal, fp->local, sizeof(outArrays->fragmentLocal));
|
|
}
|
|
|
|
GLenum QD3D12ARB_ConsumeError(void)
|
|
{
|
|
GLenum e = g_lastError;
|
|
g_lastError = GL_NO_ERROR;
|
|
return e;
|
|
}
|
|
|
|
GLint QD3D12ARB_GetProgramErrorPosition(void)
|
|
{
|
|
return g_lastProgramErrorPosition;
|
|
}
|
|
|
|
const char* QD3D12ARB_GetProgramErrorString(void)
|
|
{
|
|
return g_lastProgramErrorString.c_str();
|
|
}
|
|
|
|
void APIENTRY glGenProgramsARB(GLsizei n, GLuint* programs)
|
|
{
|
|
if (n < 0)
|
|
{
|
|
SetError(GL_INVALID_VALUE);
|
|
return;
|
|
}
|
|
|
|
if (!programs)
|
|
return;
|
|
|
|
for (GLsizei i = 0; i < n; ++i)
|
|
programs[i] = g_nextProgramId++;
|
|
}
|
|
|
|
void APIENTRY glDeleteProgramsARB(GLsizei n, const GLuint* programs)
|
|
{
|
|
if (n < 0)
|
|
{
|
|
SetError(GL_INVALID_VALUE);
|
|
return;
|
|
}
|
|
|
|
if (!programs)
|
|
return;
|
|
|
|
for (GLsizei i = 0; i < n; ++i)
|
|
{
|
|
const GLuint id = programs[i];
|
|
if (id == 0)
|
|
continue;
|
|
|
|
if (g_boundVertexProgram == id) g_boundVertexProgram = 0;
|
|
if (g_boundFragmentProgram == id) g_boundFragmentProgram = 0;
|
|
|
|
g_vertexPrograms.erase(id);
|
|
g_fragmentPrograms.erase(id);
|
|
}
|
|
}
|
|
|
|
void APIENTRY glBindProgramARB(GLenum target, GLuint program)
|
|
{
|
|
if (!IsProgramTarget(target))
|
|
{
|
|
SetError(GL_INVALID_ENUM);
|
|
return;
|
|
}
|
|
|
|
BoundForTarget(target) = program;
|
|
if (program == 0)
|
|
return;
|
|
|
|
auto& map = ProgramsForTarget(target);
|
|
auto it = map.find(program);
|
|
if (it == map.end())
|
|
{
|
|
ARBProgram p{};
|
|
p.id = program;
|
|
p.target = target;
|
|
map.emplace(program, std::move(p));
|
|
}
|
|
}
|
|
|
|
void APIENTRY glProgramStringARB(GLenum target, GLenum format, GLsizei len, const GLvoid* string)
|
|
{
|
|
if (!IsProgramTarget(target))
|
|
{
|
|
SetError(GL_INVALID_ENUM);
|
|
return;
|
|
}
|
|
|
|
if (format != GL_PROGRAM_FORMAT_ASCII_ARB)
|
|
{
|
|
SetError(GL_INVALID_ENUM);
|
|
return;
|
|
}
|
|
|
|
if (len < 0 || !string)
|
|
{
|
|
SetError(GL_INVALID_VALUE);
|
|
return;
|
|
}
|
|
|
|
GLuint id = BoundForTarget(target);
|
|
if (id == 0)
|
|
{
|
|
id = g_nextProgramId++;
|
|
BoundForTarget(target) = id;
|
|
}
|
|
|
|
ARBProgram& p = ProgramsForTarget(target)[id];
|
|
p.id = id;
|
|
p.target = target;
|
|
p.source.assign((const char*)string, (size_t)len);
|
|
p.hlsl.clear();
|
|
p.errorString.clear();
|
|
p.errorPosition = -1;
|
|
p.compileAttempted = false;
|
|
p.blob.Reset();
|
|
++p.revision;
|
|
|
|
EnsureCompiled(target);
|
|
}
|
|
|
|
void APIENTRY glProgramEnvParameter4fARB(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
|
|
{
|
|
if (!IsProgramTarget(target))
|
|
{
|
|
SetError(GL_INVALID_ENUM);
|
|
return;
|
|
}
|
|
|
|
if (index >= QD3D12_ARB_MAX_PROGRAM_PARAMETERS)
|
|
{
|
|
SetError(GL_INVALID_VALUE);
|
|
return;
|
|
}
|
|
|
|
g_env[index][0] = x;
|
|
g_env[index][1] = y;
|
|
g_env[index][2] = z;
|
|
g_env[index][3] = w;
|
|
}
|
|
|
|
void APIENTRY glProgramEnvParameter4fvARB(GLenum target, GLuint index, const GLfloat* params)
|
|
{
|
|
if (!params)
|
|
{
|
|
SetError(GL_INVALID_VALUE);
|
|
return;
|
|
}
|
|
|
|
glProgramEnvParameter4fARB(target, index, params[0], params[1], params[2], params[3]);
|
|
}
|
|
|
|
void APIENTRY glProgramLocalParameter4fARB(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
|
|
{
|
|
if (!IsProgramTarget(target))
|
|
{
|
|
SetError(GL_INVALID_ENUM);
|
|
return;
|
|
}
|
|
|
|
if (index >= QD3D12_ARB_MAX_PROGRAM_PARAMETERS)
|
|
{
|
|
SetError(GL_INVALID_VALUE);
|
|
return;
|
|
}
|
|
|
|
ARBProgram* p = GetBoundProgram(target);
|
|
if (!p)
|
|
{
|
|
SetError(GL_INVALID_OPERATION);
|
|
return;
|
|
}
|
|
|
|
p->local[index][0] = x;
|
|
p->local[index][1] = y;
|
|
p->local[index][2] = z;
|
|
p->local[index][3] = w;
|
|
}
|
|
|
|
void APIENTRY glProgramLocalParameter4fvARB(GLenum target, GLuint index, const GLfloat* params)
|
|
{
|
|
if (!params)
|
|
{
|
|
SetError(GL_INVALID_VALUE);
|
|
return;
|
|
}
|
|
|
|
glProgramLocalParameter4fARB(target, index, params[0], params[1], params[2], params[3]);
|
|
}
|
|
|
|
void APIENTRY glGetProgramEnvParameterfvARB(GLenum target, GLuint index, GLfloat* params)
|
|
{
|
|
if (!params)
|
|
return;
|
|
|
|
if (!IsProgramTarget(target))
|
|
{
|
|
SetError(GL_INVALID_ENUM);
|
|
return;
|
|
}
|
|
|
|
if (index >= QD3D12_ARB_MAX_PROGRAM_PARAMETERS)
|
|
{
|
|
SetError(GL_INVALID_VALUE);
|
|
return;
|
|
}
|
|
|
|
std::memcpy(params, g_env[index], sizeof(float) * 4);
|
|
}
|
|
|
|
void APIENTRY glGetProgramLocalParameterfvARB(GLenum target, GLuint index, GLfloat* params)
|
|
{
|
|
if (!params)
|
|
return;
|
|
|
|
if (!IsProgramTarget(target))
|
|
{
|
|
SetError(GL_INVALID_ENUM);
|
|
return;
|
|
}
|
|
|
|
if (index >= QD3D12_ARB_MAX_PROGRAM_PARAMETERS)
|
|
{
|
|
SetError(GL_INVALID_VALUE);
|
|
return;
|
|
}
|
|
|
|
ARBProgram* p = GetBoundProgram(target);
|
|
if (!p)
|
|
{
|
|
SetError(GL_INVALID_OPERATION);
|
|
return;
|
|
}
|
|
|
|
std::memcpy(params, p->local[index], sizeof(float) * 4);
|
|
}
|
|
|
|
void APIENTRY glGetProgramivARB(GLenum target, GLenum pname, GLint* params)
|
|
{
|
|
if (!params)
|
|
return;
|
|
|
|
if (!IsProgramTarget(target))
|
|
{
|
|
SetError(GL_INVALID_ENUM);
|
|
return;
|
|
}
|
|
|
|
ARBProgram* p = GetBoundProgram(target);
|
|
switch (pname)
|
|
{
|
|
case GL_PROGRAM_ERROR_POSITION_ARB:
|
|
*params = p ? p->errorPosition : g_lastProgramErrorPosition;
|
|
break;
|
|
|
|
case GL_PROGRAM_LENGTH_ARB:
|
|
*params = p ? (GLint)p->source.size() : 0;
|
|
break;
|
|
|
|
case GL_PROGRAM_BINDING_ARB:
|
|
case GL_VERTEX_PROGRAM_BINDING_ARB:
|
|
case GL_FRAGMENT_PROGRAM_BINDING_ARB:
|
|
*params = (GLint)BoundForTarget(target);
|
|
break;
|
|
|
|
case GL_PROGRAM_FORMAT_ARB:
|
|
*params = GL_PROGRAM_FORMAT_ASCII_ARB;
|
|
break;
|
|
|
|
case GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB:
|
|
*params = GL_TRUE;
|
|
break;
|
|
|
|
default:
|
|
*params = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void APIENTRY glGetProgramStringARB(GLenum target, GLenum pname, GLvoid* string)
|
|
{
|
|
if (!string)
|
|
return;
|
|
|
|
if (!IsProgramTarget(target))
|
|
{
|
|
SetError(GL_INVALID_ENUM);
|
|
return;
|
|
}
|
|
|
|
ARBProgram* p = GetBoundProgram(target);
|
|
if (!p)
|
|
{
|
|
((char*)string)[0] = 0;
|
|
return;
|
|
}
|
|
|
|
if (pname == GL_PROGRAM_STRING_ARB)
|
|
std::memcpy(string, p->source.c_str(), p->source.size() + 1);
|
|
else
|
|
SetError(GL_INVALID_ENUM);
|
|
}
|
|
|
|
GLboolean APIENTRY glIsProgramARB(GLuint program)
|
|
{
|
|
if (program == 0)
|
|
return GL_FALSE;
|
|
|
|
return (g_vertexPrograms.find(program) != g_vertexPrograms.end() ||
|
|
g_fragmentPrograms.find(program) != g_fragmentPrograms.end()) ? GL_TRUE : GL_FALSE;
|
|
}
|