521 lines
18 KiB
C++
521 lines
18 KiB
C++
#include "ShaderInfo.h"
|
|
#include "internal_includes/debug.h"
|
|
#include "internal_includes/tokens.h"
|
|
#include "Operand.h"
|
|
#include <stdlib.h>
|
|
#include <sstream>
|
|
#include <cctype>
|
|
|
|
|
|
SHADER_VARIABLE_TYPE ShaderInfo::GetTextureDataType(uint32_t regNo)
|
|
{
|
|
const ResourceBinding* psBinding = 0;
|
|
int found;
|
|
found = GetResourceFromBindingPoint(RGROUP_TEXTURE, regNo, &psBinding);
|
|
ASSERT(found != 0);
|
|
return psBinding->GetDataType();
|
|
}
|
|
|
|
void ShaderInfo::GetConstantBufferFromBindingPoint(const ResourceGroup eGroup, const uint32_t ui32BindPoint, const ConstantBuffer** ppsConstBuf) const
|
|
{
|
|
ASSERT(ui32MajorVersion > 3);
|
|
*ppsConstBuf = &psConstantBuffers[aui32ResourceMap[eGroup][ui32BindPoint]];
|
|
}
|
|
|
|
int ShaderInfo::GetResourceFromBindingPoint(const ResourceGroup eGroup, uint32_t const ui32BindPoint, const ResourceBinding** ppsOutBinding) const
|
|
{
|
|
size_t i;
|
|
const size_t ui32NumBindings = psResourceBindings.size();
|
|
const ResourceBinding* psBindings = &psResourceBindings[0];
|
|
|
|
for (i = 0; i < ui32NumBindings; ++i)
|
|
{
|
|
if (ResourceTypeToResourceGroup(psBindings[i].eType) == eGroup)
|
|
{
|
|
if (ui32BindPoint >= psBindings[i].ui32BindPoint && ui32BindPoint < (psBindings[i].ui32BindPoint + psBindings[i].ui32BindCount))
|
|
{
|
|
*ppsOutBinding = psBindings + i;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ShaderInfo::GetInterfaceVarFromOffset(uint32_t ui32Offset, ShaderVar** ppsShaderVar) const
|
|
{
|
|
size_t i;
|
|
const size_t ui32NumVars = psThisPointerConstBuffer->asVars.size();
|
|
|
|
for (i = 0; i < ui32NumVars; ++i)
|
|
{
|
|
if (ui32Offset >= psThisPointerConstBuffer->asVars[i].ui32StartOffset &&
|
|
ui32Offset < (psThisPointerConstBuffer->asVars[i].ui32StartOffset + psThisPointerConstBuffer->asVars[i].ui32Size))
|
|
{
|
|
*ppsShaderVar = &psThisPointerConstBuffer->asVars[i];
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ShaderInfo::GetInputSignatureFromRegister(const uint32_t ui32Register, const uint32_t ui32Mask, const InOutSignature** ppsOut, bool allowNull /* == false */) const
|
|
{
|
|
size_t i;
|
|
const size_t ui32NumVars = psInputSignatures.size();
|
|
|
|
for (i = 0; i < ui32NumVars; ++i)
|
|
{
|
|
if ((ui32Register == psInputSignatures[i].ui32Register) && (((~psInputSignatures[i].ui32Mask) & ui32Mask) == 0))
|
|
{
|
|
*ppsOut = &psInputSignatures[i];
|
|
return 1;
|
|
}
|
|
}
|
|
ASSERT(allowNull);
|
|
return 0;
|
|
}
|
|
|
|
int ShaderInfo::GetPatchConstantSignatureFromRegister(const uint32_t ui32Register, const uint32_t ui32Mask, const InOutSignature** ppsOut, bool allowNull /* == false */) const
|
|
{
|
|
size_t i;
|
|
const size_t ui32NumVars = psPatchConstantSignatures.size();
|
|
|
|
for (i = 0; i < ui32NumVars; ++i)
|
|
{
|
|
if ((ui32Register == psPatchConstantSignatures[i].ui32Register) && (((~psPatchConstantSignatures[i].ui32Mask) & ui32Mask) == 0))
|
|
{
|
|
*ppsOut = &psPatchConstantSignatures[i];
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// There are situations (especially when using dcl_indexrange) where the compiler happily writes outside the actual masks.
|
|
// In those situations just take the last signature that uses that register (it's typically the "highest" one)
|
|
for (i = ui32NumVars - 1; i-- > 0;)
|
|
{
|
|
if (ui32Register == psPatchConstantSignatures[i].ui32Register)
|
|
{
|
|
*ppsOut = &psPatchConstantSignatures[i];
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
ASSERT(allowNull);
|
|
return 0;
|
|
}
|
|
|
|
int ShaderInfo::GetOutputSignatureFromRegister(const uint32_t ui32Register,
|
|
const uint32_t ui32CompMask,
|
|
const uint32_t ui32Stream,
|
|
const InOutSignature** ppsOut,
|
|
bool allowNull /* = false */) const
|
|
{
|
|
size_t i;
|
|
const size_t ui32NumVars = psOutputSignatures.size();
|
|
ASSERT(ui32CompMask != 0);
|
|
|
|
for (i = 0; i < ui32NumVars; ++i)
|
|
{
|
|
if (ui32Register == psOutputSignatures[i].ui32Register &&
|
|
(ui32CompMask & psOutputSignatures[i].ui32Mask) &&
|
|
ui32Stream == psOutputSignatures[i].ui32Stream)
|
|
{
|
|
*ppsOut = &psOutputSignatures[i];
|
|
return 1;
|
|
}
|
|
}
|
|
ASSERT(allowNull);
|
|
return 0;
|
|
}
|
|
|
|
int ShaderInfo::GetOutputSignatureFromSystemValue(SPECIAL_NAME eSystemValueType, uint32_t ui32SemanticIndex, const InOutSignature** ppsOut) const
|
|
{
|
|
size_t i;
|
|
const size_t ui32NumVars = psOutputSignatures.size();
|
|
|
|
for (i = 0; i < ui32NumVars; ++i)
|
|
{
|
|
if (eSystemValueType == psOutputSignatures[i].eSystemValueType &&
|
|
ui32SemanticIndex == psOutputSignatures[i].ui32SemanticIndex)
|
|
{
|
|
*ppsOut = &psOutputSignatures[i];
|
|
return 1;
|
|
}
|
|
}
|
|
ASSERT(0);
|
|
return 0;
|
|
}
|
|
|
|
uint32_t ShaderInfo::GetCBVarSize(const ShaderVarType* psType, bool matrixAsVectors, bool wholeArraySize)
|
|
{
|
|
// Default is regular matrices, vectors and scalars
|
|
uint32_t size = psType->Columns * psType->Rows * 4;
|
|
|
|
// Struct size is calculated from the offset and size of its last member.
|
|
// Need to take into account that members could be arrays.
|
|
if (psType->Class == SVC_STRUCT)
|
|
{
|
|
size = psType->Members.back().Offset + GetCBVarSize(&psType->Members.back(), matrixAsVectors, true);
|
|
}
|
|
// Matrices represented as vec4 arrays have special size calculation
|
|
else if (matrixAsVectors)
|
|
{
|
|
if (psType->Class == SVC_MATRIX_ROWS)
|
|
{
|
|
size = psType->Rows * 16;
|
|
}
|
|
else if (psType->Class == SVC_MATRIX_COLUMNS)
|
|
{
|
|
size = psType->Columns * 16;
|
|
}
|
|
}
|
|
|
|
if (wholeArraySize && psType->Elements > 1)
|
|
{
|
|
uint32_t paddedSize = ((size + 15) / 16) * 16; // Arrays are padded to float4 size
|
|
size = (psType->Elements - 1) * paddedSize + size; // Except the last element
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static const ShaderVarType* IsOffsetInType(const ShaderVarType* psType,
|
|
uint32_t parentOffset,
|
|
uint32_t offsetToFind,
|
|
bool* isArray,
|
|
std::vector<uint32_t>* arrayIndices,
|
|
int32_t* pi32Rebase,
|
|
uint32_t flags)
|
|
{
|
|
uint32_t thisOffset = parentOffset + psType->Offset;
|
|
uint32_t thisSize = ShaderInfo::GetCBVarSize(psType, (flags & HLSLCC_FLAG_TRANSLATE_MATRICES) != 0);
|
|
uint32_t paddedSize = ((thisSize + 15) / 16) * 16;
|
|
uint32_t arraySize = thisSize;
|
|
|
|
// Array elements are padded to align on vec4 size, except for the last one
|
|
if (psType->Elements)
|
|
arraySize = (paddedSize * (psType->Elements - 1)) + thisSize;
|
|
|
|
if ((offsetToFind >= thisOffset) &&
|
|
offsetToFind < (thisOffset + arraySize))
|
|
{
|
|
*isArray = false;
|
|
if (psType->Class == SVC_STRUCT)
|
|
{
|
|
if (psType->Elements > 1 && arrayIndices != NULL)
|
|
arrayIndices->push_back((offsetToFind - thisOffset) / thisSize);
|
|
|
|
// Need to bring offset back to element zero in case of array of structs
|
|
uint32_t offsetInStruct = (offsetToFind - thisOffset) % paddedSize;
|
|
uint32_t m = 0;
|
|
|
|
for (m = 0; m < psType->MemberCount; ++m)
|
|
{
|
|
const ShaderVarType* psMember = &psType->Members[m];
|
|
|
|
const ShaderVarType* foundType = IsOffsetInType(psMember, thisOffset, thisOffset + offsetInStruct, isArray, arrayIndices, pi32Rebase, flags);
|
|
if (foundType != NULL)
|
|
return foundType;
|
|
}
|
|
}
|
|
// Check for array of scalars or vectors (both take up 16 bytes per element).
|
|
// Matrices are also treated as arrays of vectors.
|
|
else if ((psType->Class == SVC_MATRIX_ROWS || psType->Class == SVC_MATRIX_COLUMNS) ||
|
|
((psType->Class == SVC_SCALAR || psType->Class == SVC_VECTOR) && psType->Elements > 1))
|
|
{
|
|
*isArray = true;
|
|
if (arrayIndices != NULL)
|
|
arrayIndices->push_back((offsetToFind - thisOffset) / 16);
|
|
}
|
|
else if (psType->Class == SVC_VECTOR)
|
|
{
|
|
//Check for vector starting at a non-vec4 offset.
|
|
|
|
// cbuffer $Globals
|
|
// {
|
|
//
|
|
// float angle; // Offset: 0 Size: 4
|
|
// float2 angle2; // Offset: 4 Size: 8
|
|
//
|
|
// }
|
|
|
|
//cb0[0].x = angle
|
|
//cb0[0].yzyy = angle2.xyxx
|
|
|
|
//Rebase angle2 so that .y maps to .x, .z maps to .y
|
|
|
|
pi32Rebase[0] = thisOffset % 16;
|
|
}
|
|
|
|
return psType;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int ShaderInfo::GetShaderVarFromOffset(const uint32_t ui32Vec4Offset,
|
|
const uint32_t(&pui32Swizzle)[4],
|
|
const ConstantBuffer* psCBuf,
|
|
const ShaderVarType** ppsShaderVar, // Output the found var
|
|
bool* isArray, // Output bool that tells if the found var is an array
|
|
std::vector<uint32_t>* arrayIndices, // Output vector of array indices in order from root parent to the found var
|
|
int32_t* pi32Rebase, // Output swizzle rebase
|
|
uint32_t flags)
|
|
{
|
|
size_t i;
|
|
|
|
uint32_t ui32ByteOffset = ui32Vec4Offset * 16;
|
|
|
|
//Swizzle can point to another variable. In the example below
|
|
//cbUIUpdates.g_uMaxFaces would be cb1[2].z. The scalars are combined
|
|
//into vectors. psCBuf->ui32NumVars will be 3.
|
|
|
|
// cbuffer cbUIUpdates
|
|
// {
|
|
// float g_fLifeSpan; // Offset: 0 Size: 4
|
|
// float g_fLifeSpanVar; // Offset: 4 Size: 4 [unused]
|
|
// float g_fRadiusMin; // Offset: 8 Size: 4 [unused]
|
|
// float g_fRadiusMax; // Offset: 12 Size: 4 [unused]
|
|
// float g_fGrowTime; // Offset: 16 Size: 4 [unused]
|
|
// float g_fStepSize; // Offset: 20 Size: 4
|
|
// float g_fTurnRate; // Offset: 24 Size: 4
|
|
// float g_fTurnSpeed; // Offset: 28 Size: 4 [unused]
|
|
// float g_fLeafRate; // Offset: 32 Size: 4
|
|
// float g_fShrinkTime; // Offset: 36 Size: 4 [unused]
|
|
// uint g_uMaxFaces; // Offset: 40 Size: 4
|
|
// }
|
|
if (pui32Swizzle[0] == OPERAND_4_COMPONENT_Y)
|
|
{
|
|
ui32ByteOffset += 4;
|
|
}
|
|
else if (pui32Swizzle[0] == OPERAND_4_COMPONENT_Z)
|
|
{
|
|
ui32ByteOffset += 8;
|
|
}
|
|
else if (pui32Swizzle[0] == OPERAND_4_COMPONENT_W)
|
|
{
|
|
ui32ByteOffset += 12;
|
|
}
|
|
|
|
const size_t ui32NumVars = psCBuf->asVars.size();
|
|
|
|
for (i = 0; i < ui32NumVars; ++i)
|
|
{
|
|
ppsShaderVar[0] = IsOffsetInType(&psCBuf->asVars[i].sType, psCBuf->asVars[i].ui32StartOffset, ui32ByteOffset, isArray, arrayIndices, pi32Rebase, flags);
|
|
|
|
if (ppsShaderVar[0] != NULL)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Patches the fullName of the var with given array indices. Does not insert the indexing for the var itself if it is an array.
|
|
// Searches for brackets and inserts indices one by one.
|
|
std::string ShaderInfo::GetShaderVarIndexedFullName(const ShaderVarType* psShaderVar, const std::vector<uint32_t>& indices, const std::string& dynamicIndex, bool revertDynamicIndexCalc, bool matrixAsVectors)
|
|
{
|
|
std::ostringstream oss;
|
|
size_t prevpos = 0;
|
|
size_t pos = psShaderVar->fullName.find('[', 0);
|
|
uint32_t i = 0;
|
|
while (pos != std::string::npos)
|
|
{
|
|
pos++;
|
|
oss << psShaderVar->fullName.substr(prevpos, pos - prevpos);
|
|
|
|
// Add possibly given dynamic index for the root array.
|
|
if (i == 0 && !dynamicIndex.empty())
|
|
{
|
|
oss << dynamicIndex;
|
|
|
|
// if we couldn't use original index temp, revert the float4 address calc here
|
|
if (revertDynamicIndexCalc)
|
|
{
|
|
const ShaderVarType* psRootVar = psShaderVar;
|
|
while (psRootVar->Parent != NULL)
|
|
psRootVar = psRootVar->Parent;
|
|
|
|
uint32_t thisSize = (GetCBVarSize(psRootVar, matrixAsVectors) + 15) / 16; // size in float4
|
|
oss << " / " << thisSize;
|
|
}
|
|
|
|
if (!indices.empty() && indices[i] != 0)
|
|
oss << " + " << indices[i];
|
|
}
|
|
else if (i < indices.size())
|
|
oss << indices[i];
|
|
|
|
prevpos = pos;
|
|
i++;
|
|
pos = psShaderVar->fullName.find('[', prevpos);
|
|
}
|
|
oss << psShaderVar->fullName.substr(prevpos);
|
|
|
|
return oss.str();
|
|
}
|
|
|
|
ResourceGroup ShaderInfo::ResourceTypeToResourceGroup(ResourceType eType)
|
|
{
|
|
switch (eType)
|
|
{
|
|
case RTYPE_CBUFFER:
|
|
return RGROUP_CBUFFER;
|
|
|
|
case RTYPE_SAMPLER:
|
|
return RGROUP_SAMPLER;
|
|
|
|
case RTYPE_TEXTURE:
|
|
case RTYPE_BYTEADDRESS:
|
|
case RTYPE_STRUCTURED:
|
|
return RGROUP_TEXTURE;
|
|
|
|
case RTYPE_UAV_RWTYPED:
|
|
case RTYPE_UAV_RWSTRUCTURED:
|
|
case RTYPE_UAV_RWBYTEADDRESS:
|
|
case RTYPE_UAV_APPEND_STRUCTURED:
|
|
case RTYPE_UAV_CONSUME_STRUCTURED:
|
|
case RTYPE_UAV_RWSTRUCTURED_WITH_COUNTER:
|
|
return RGROUP_UAV;
|
|
|
|
case RTYPE_TBUFFER:
|
|
ASSERT(0); // Need to find out which group this belongs to
|
|
return RGROUP_TEXTURE;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ASSERT(0);
|
|
return RGROUP_CBUFFER;
|
|
}
|
|
|
|
static inline std::string GetTextureNameFromSamplerName(const std::string& samplerIn)
|
|
{
|
|
ASSERT(samplerIn.compare(0, 7, "sampler") == 0);
|
|
|
|
// please note that we do not have hard rules about how sampler names should be structured
|
|
// what's more they can even skip texture name (but that should be handled separately)
|
|
// how do we try to deduce the texture name: we remove known tokens, and take the leftmost (first) "word"
|
|
// note that we want to support c-style naming (with underscores for spaces)
|
|
// as it is pretty normal to have texture name starting with underscore
|
|
// we bind underscores "to the right"
|
|
|
|
// note that we want sampler state to be case insensitive
|
|
// while checking for a match could be done with strncasecmp/_strnicmp
|
|
// windows is missing case-insensetive "find substring" (strcasestr), so we transform to lowercase instead
|
|
std::string sampler = samplerIn;
|
|
for (std::string::iterator i = sampler.begin(), in = sampler.end(); i != in; ++i)
|
|
*i = std::tolower(*i);
|
|
|
|
struct Token { const char* str; int len; };
|
|
#define TOKEN(s) { s, (int)strlen(s) }
|
|
Token token[] = {
|
|
TOKEN("compare"),
|
|
TOKEN("point"), TOKEN("trilinear"), TOKEN("linear"),
|
|
TOKEN("clamp"), TOKEN("clampu"), TOKEN("clampv"), TOKEN("clampw"),
|
|
TOKEN("repeat"), TOKEN("repeatu"), TOKEN("repeatv"), TOKEN("repeatw"),
|
|
TOKEN("mirror"), TOKEN("mirroru"), TOKEN("mirrorv"), TOKEN("mirrorw"),
|
|
TOKEN("mirroronce"), TOKEN("mirroronceu"), TOKEN("mirroroncev"), TOKEN("mirroroncew"),
|
|
};
|
|
#undef TOKEN
|
|
|
|
const char* s = sampler.c_str();
|
|
for (int texNameStart = 7; s[texNameStart];)
|
|
{
|
|
// skip underscores and find the potential beginning of a token
|
|
int tokenStart = texNameStart, tokenEnd = -1;
|
|
while (s[tokenStart] == '_')
|
|
++tokenStart;
|
|
|
|
// check token list for matches
|
|
for (int i = 0, n = sizeof(token) / sizeof(token[0]); i < n && tokenEnd < 0; ++i)
|
|
if (strncmp(s + tokenStart, token[i].str, token[i].len) == 0)
|
|
tokenEnd = tokenStart + token[i].len;
|
|
|
|
if (tokenEnd < 0)
|
|
{
|
|
// we have found texture name
|
|
|
|
// find next token
|
|
int nextTokenStart = sampler.length();
|
|
for (int i = 0, n = sizeof(token) / sizeof(token[0]); i < n; ++i)
|
|
{
|
|
// again: note that we want to be case insensitive
|
|
const int pos = sampler.find(token[i].str, tokenStart);
|
|
|
|
if (pos != std::string::npos && pos < nextTokenStart)
|
|
nextTokenStart = pos;
|
|
}
|
|
|
|
// check preceeding underscores, but only if we have found an actual token (not the end of the string)
|
|
if (nextTokenStart < sampler.length())
|
|
{
|
|
while (nextTokenStart > tokenStart && s[nextTokenStart - 1] == '_')
|
|
--nextTokenStart;
|
|
}
|
|
|
|
// note that we return the substring of the initial sampler name to preserve case
|
|
return samplerIn.substr(texNameStart, nextTokenStart - texNameStart);
|
|
}
|
|
else
|
|
{
|
|
// we have found known token
|
|
texNameStart = tokenEnd;
|
|
}
|
|
}
|
|
|
|
// if we ended up here, the texture name is missing
|
|
return "";
|
|
}
|
|
|
|
// note that we dont have the means right now to have unit tests in hlslcc, so we do poor man testing below
|
|
// AddSamplerPrecisions is called once for every program, so it is easy to uncomment and test
|
|
static inline void Test_GetTextureNameFromSamplerName()
|
|
{
|
|
#define CHECK(s, t) ASSERT(GetTextureNameFromSamplerName(std::string(s)) == std::string(t))
|
|
|
|
CHECK("sampler_point_clamp", "");
|
|
CHECK("sampler_point_clamp_Tex", "_Tex");
|
|
CHECK("sampler_point_clamp_Tex__", "_Tex__");
|
|
CHECK("sampler_______point_Tex", "_Tex");
|
|
|
|
CHECK("samplerPointClamp", "");
|
|
CHECK("samplerPointClamp_Tex", "_Tex");
|
|
CHECK("samplerPointClamp_Tex__", "_Tex__");
|
|
|
|
CHECK("samplerPointTexClamp", "Tex");
|
|
CHECK("samplerPoint_TexClamp", "_Tex");
|
|
CHECK("samplerPoint_Tex_Clamp", "_Tex");
|
|
|
|
#undef CHECK
|
|
}
|
|
|
|
void ShaderInfo::AddSamplerPrecisions(HLSLccSamplerPrecisionInfo &info)
|
|
{
|
|
if (info.empty())
|
|
return;
|
|
|
|
#if _DEBUG && 0
|
|
Test_GetTextureNameFromSamplerName();
|
|
#endif
|
|
|
|
for (size_t i = 0; i < psResourceBindings.size(); i++)
|
|
{
|
|
ResourceBinding *rb = &psResourceBindings[i];
|
|
if (rb->eType != RTYPE_SAMPLER && rb->eType != RTYPE_TEXTURE && rb->eType != RTYPE_UAV_RWTYPED)
|
|
continue;
|
|
|
|
// Try finding the exact match
|
|
HLSLccSamplerPrecisionInfo::iterator j = info.find(rb->name);
|
|
|
|
// If match not found, check if name has "sampler" prefix (DX11 style sampler case)
|
|
// then we try to recover texture name from sampler name
|
|
if (j == info.end() && rb->name.compare(0, 7, "sampler") == 0)
|
|
j = info.find(GetTextureNameFromSamplerName(rb->name));
|
|
|
|
// note that if we didnt find the respective texture, we cannot say anything about sampler precision
|
|
// currently it will become "unknown" resulting in half format, even if we sample with it the texture explicitly marked as float
|
|
// TODO: should we somehow allow overriding it?
|
|
if (j != info.end())
|
|
rb->ePrecision = j->second;
|
|
}
|
|
}
|