blob: 94f1857a5fe3815f73bd22d44ff58a6b6115b51f [file] [log] [blame]
//
// Copyright 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// VaryingPacking:
// Class which describes a mapping from varyings to registers in D3D
// for linking between shader stages.
//
#include "libANGLE/renderer/d3d/VaryingPacking.h"
#include "common/utilities.h"
#include "compiler/translator/blocklayoutHLSL.h"
#include "libANGLE/renderer/d3d/DynamicHLSL.h"
#include "libANGLE/renderer/d3d/ProgramD3D.h"
namespace rx
{
SemanticInfo::BuiltinInfo::BuiltinInfo() : enabled(false), index(0), systemValue(false)
{
}
std::string SemanticInfo::BuiltinInfo::str() const
{
return (systemValue ? semantic : (semantic + Str(index)));
}
void SemanticInfo::BuiltinInfo::enableSystem(const std::string &systemValueSemantic)
{
enabled = true;
semantic = systemValueSemantic;
systemValue = true;
}
void SemanticInfo::BuiltinInfo::enable(const std::string &semanticVal, unsigned int indexVal)
{
enabled = true;
semantic = semanticVal;
index = indexVal;
}
bool PackVarying(PackedVarying *packedVarying, const int maxVaryingVectors, VaryingPacking &packing)
{
// Make sure we use transposed matrix types to count registers correctly.
int registers = 0;
int elements = 0;
const sh::Varying &varying = *packedVarying->varying;
if (varying.isStruct())
{
registers = HLSLVariableRegisterCount(varying, true) * varying.elementCount();
elements = 4;
}
else
{
GLenum transposedType = gl::TransposeMatrixType(varying.type);
registers = gl::VariableRowCount(transposedType) * varying.elementCount();
elements = gl::VariableColumnCount(transposedType);
}
if (elements >= 2 && elements <= 4)
{
for (int r = 0; r <= maxVaryingVectors - registers; r++)
{
bool available = true;
for (int y = 0; y < registers && available; y++)
{
for (int x = 0; x < elements && available; x++)
{
if (packing[r + y][x])
{
available = false;
}
}
}
if (available)
{
packedVarying->registerIndex = r;
packedVarying->columnIndex = 0;
for (int y = 0; y < registers; y++)
{
for (int x = 0; x < elements; x++)
{
packing[r + y][x] = packedVarying;
}
}
return true;
}
}
if (elements == 2)
{
for (int r = maxVaryingVectors - registers; r >= 0; r--)
{
bool available = true;
for (int y = 0; y < registers && available; y++)
{
for (int x = 2; x < 4 && available; x++)
{
if (packing[r + y][x])
{
available = false;
}
}
}
if (available)
{
packedVarying->registerIndex = r;
packedVarying->columnIndex = 2;
for (int y = 0; y < registers; y++)
{
for (int x = 2; x < 4; x++)
{
packing[r + y][x] = packedVarying;
}
}
return true;
}
}
}
}
else if (elements == 1)
{
int space[4] = {0};
for (int y = 0; y < maxVaryingVectors; y++)
{
for (int x = 0; x < 4; x++)
{
space[x] += packing[y][x] ? 0 : 1;
}
}
int column = 0;
for (int x = 0; x < 4; x++)
{
if (space[x] >= registers && (space[column] < registers || space[x] < space[column]))
{
column = x;
}
}
if (space[column] >= registers)
{
for (int r = 0; r < maxVaryingVectors; r++)
{
if (!packing[r][column])
{
packedVarying->registerIndex = r;
packedVarying->columnIndex = column;
for (int y = r; y < r + registers; y++)
{
packing[y][column] = packedVarying;
}
break;
}
}
return true;
}
}
else
UNREACHABLE();
return false;
}
unsigned int PackedVaryingRegister::registerIndex(
const gl::Caps &caps,
const std::vector<PackedVarying> &packedVaryings) const
{
const PackedVarying &packedVarying = packedVaryings[varyingIndex];
const sh::Varying &varying = *packedVarying.varying;
GLenum transposedType = gl::TransposeMatrixType(varying.type);
unsigned int variableRows =
static_cast<unsigned int>(varying.isStruct() ? 1 : gl::VariableRowCount(transposedType));
return (elementIndex * variableRows + (packedVarying.columnIndex * caps.maxVaryingVectors) +
(packedVarying.registerIndex + rowIndex));
}
PackedVaryingIterator::PackedVaryingIterator(const std::vector<PackedVarying> &packedVaryings)
: mPackedVaryings(packedVaryings), mEnd(*this)
{
mEnd.setEnd();
}
PackedVaryingIterator::Iterator PackedVaryingIterator::begin() const
{
return Iterator(*this);
}
const PackedVaryingIterator::Iterator &PackedVaryingIterator::end() const
{
return mEnd;
}
PackedVaryingIterator::Iterator::Iterator(const PackedVaryingIterator &parent) : mParent(parent)
{
while (mRegister.varyingIndex < mParent.mPackedVaryings.size() &&
!mParent.mPackedVaryings[mRegister.varyingIndex].registerAssigned())
{
++mRegister.varyingIndex;
}
}
PackedVaryingIterator::Iterator &PackedVaryingIterator::Iterator::operator++()
{
const sh::Varying *varying = mParent.mPackedVaryings[mRegister.varyingIndex].varying;
GLenum transposedType = gl::TransposeMatrixType(varying->type);
unsigned int variableRows =
static_cast<unsigned int>(varying->isStruct() ? 1 : gl::VariableRowCount(transposedType));
// Innermost iteration: row count
if (mRegister.rowIndex + 1 < variableRows)
{
++mRegister.rowIndex;
return *this;
}
mRegister.rowIndex = 0;
// Middle iteration: element count
if (mRegister.elementIndex + 1 < varying->elementCount())
{
++mRegister.elementIndex;
return *this;
}
mRegister.elementIndex = 0;
// Outer iteration: the varying itself. Once we pass the last varying, this Iterator will
// equal the end Iterator.
do
{
++mRegister.varyingIndex;
} while (mRegister.varyingIndex < mParent.mPackedVaryings.size() &&
!mParent.mPackedVaryings[mRegister.varyingIndex].registerAssigned());
return *this;
}
bool PackedVaryingIterator::Iterator::operator==(const Iterator &other) const
{
return mRegister.elementIndex == other.mRegister.elementIndex &&
mRegister.rowIndex == other.mRegister.rowIndex &&
mRegister.varyingIndex == other.mRegister.varyingIndex;
}
bool PackedVaryingIterator::Iterator::operator!=(const Iterator &other) const
{
return !(*this == other);
}
// Packs varyings into generic varying registers, using the algorithm from [OpenGL ES Shading
// Language 1.00 rev. 17] appendix A section 7 page 111
// Returns the number of used varying registers, or -1 if unsuccesful
bool PackVaryings(const gl::Caps &caps,
gl::InfoLog &infoLog,
std::vector<PackedVarying> *packedVaryings,
const std::vector<std::string> &transformFeedbackVaryings,
unsigned int *registerCountOut)
{
VaryingPacking packing = {};
*registerCountOut = 0;
std::set<std::string> uniqueVaryingNames;
for (PackedVarying &packedVarying : *packedVaryings)
{
const sh::Varying &varying = *packedVarying.varying;
// Do not assign registers to built-in or unreferenced varyings
if (varying.isBuiltIn() || !varying.staticUse)
{
continue;
}
ASSERT(uniqueVaryingNames.count(varying.name) == 0);
if (PackVarying(&packedVarying, caps.maxVaryingVectors, packing))
{
uniqueVaryingNames.insert(varying.name);
}
else
{
infoLog << "Could not pack varying " << varying.name;
return false;
}
}
for (const std::string &transformFeedbackVaryingName : transformFeedbackVaryings)
{
if (transformFeedbackVaryingName.compare(0, 3, "gl_") == 0)
{
// do not pack builtin XFB varyings
continue;
}
for (PackedVarying &packedVarying : *packedVaryings)
{
const sh::Varying &varying = *packedVarying.varying;
// Make sure transform feedback varyings aren't optimized out.
if (uniqueVaryingNames.count(transformFeedbackVaryingName) == 0)
{
bool found = false;
if (transformFeedbackVaryingName == varying.name)
{
if (!PackVarying(&packedVarying, caps.maxVaryingVectors, packing))
{
infoLog << "Could not pack varying " << varying.name;
return false;
}
found = true;
break;
}
if (!found)
{
infoLog << "Transform feedback varying " << transformFeedbackVaryingName
<< " does not exist in the vertex shader.";
return false;
}
}
// Add duplicate transform feedback varyings for 'flat' shaded attributes. This is
// necessary because we write out modified vertex data to correct for the provoking
// vertex in D3D11. This extra transform feedback varying is the unmodified stream.
if (varying.interpolation == sh::INTERPOLATION_FLAT)
{
sh::Varying duplicateVarying(varying);
duplicateVarying.name = "StreamOut_" + duplicateVarying.name;
}
}
}
// Return the number of used registers
for (GLuint r = 0; r < caps.maxVaryingVectors; r++)
{
if (packing[r][0] || packing[r][1] || packing[r][2] || packing[r][3])
{
(*registerCountOut)++;
}
}
return true;
}
SemanticInfo GetSemanticInfo(ShaderType shaderType,
const ProgramD3DMetadata &programMetadata,
unsigned int startRegisters)
{
int majorShaderModel = programMetadata.getRendererMajorShaderModel();
bool position = programMetadata.usesTransformFeedbackGLPosition();
bool fragCoord = programMetadata.usesFragCoord();
bool pointCoord = shaderType == SHADER_VERTEX ? programMetadata.addsPointCoordToVertexShader()
: programMetadata.usesPointCoord();
bool pointSize = programMetadata.usesSystemValuePointSize();
bool hlsl4 = (majorShaderModel >= 4);
const std::string &userSemantic = GetVaryingSemantic(majorShaderModel, pointSize);
unsigned int reservedRegisterIndex = startRegisters;
SemanticInfo info;
if (hlsl4)
{
info.dxPosition.enableSystem("SV_Position");
}
else if (shaderType == SHADER_PIXEL)
{
info.dxPosition.enableSystem("VPOS");
}
else
{
info.dxPosition.enableSystem("POSITION");
}
if (position)
{
info.glPosition.enable(userSemantic, reservedRegisterIndex++);
}
if (fragCoord)
{
info.glFragCoord.enable(userSemantic, reservedRegisterIndex++);
}
if (pointCoord)
{
// SM3 reserves the TEXCOORD semantic for point sprite texcoords (gl_PointCoord)
// In D3D11 we manually compute gl_PointCoord in the GS.
if (hlsl4)
{
info.glPointCoord.enable(userSemantic, reservedRegisterIndex++);
}
else
{
info.glPointCoord.enable("TEXCOORD", 0);
}
}
// Special case: do not include PSIZE semantic in HLSL 3 pixel shaders
if (pointSize && (shaderType != SHADER_PIXEL || hlsl4))
{
info.glPointSize.enableSystem("PSIZE");
}
return info;
}
} // namespace rx