Commit 192745a7 by Jamie Madill Committed by Commit Bot

Add varying packing validation for WebGL.

This CL moves the varying packing from the D3D layer up to Program. This is necessary for WebGL validation, and gives us consistency for the various back-ends. There may be some additional cleanup work on the VaryingPacking class, because it does some work that is D3D- specific. WebGL requires strict varying packing. Instead of allowing success unconditionally, it's an explicit error to succeed to pack a set of varyings that the sample algorithm would fail to pack. Introduce a new packing mode option to the varying packing class to handle this different packing style, while keeping our old more relaxed packing method for ES code. BUG=angleproject:1675 Change-Id: I674ae685ba573cc2ad7d9dfb7441efa8cb2d55fc Reviewed-on: https://chromium-review.googlesource.com/423254 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent 4eee74de
......@@ -313,10 +313,6 @@ static_library("libANGLE") {
defines += [ "ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES={ " + "\"d3dcompiler_47.dll\", \"d3dcompiler_46.dll\", \"d3dcompiler_43.dll\" }" ]
}
if (angle_enable_hlsl) {
sources += rebase_path(gles_gypi.libangle_d3d_hlsl_sources, ".", "src")
}
if (angle_enable_d3d9) {
sources += rebase_path(gles_gypi.libangle_d3d9_sources, ".", "src")
libs += [ "d3d9.lib" ]
......
......@@ -22,6 +22,7 @@
#include "libANGLE/features.h"
#include "libANGLE/renderer/GLImplFactory.h"
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/VaryingPacking.h"
#include "libANGLE/queryconversions.h"
#include "libANGLE/Uniform.h"
......@@ -135,6 +136,12 @@ bool UniformInList(const std::vector<LinkedUniform> &list, const std::string &na
return false;
}
// true if varying x has a higher priority in packing than y
bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y)
{
return gl::CompareShaderVar(*x.varying, *y.varying);
}
} // anonymous namespace
const char *const g_fakepath = "C:\\fakepath";
......@@ -556,9 +563,12 @@ Error Program::link(const ContextState &data)
const Caps &caps = data.getCaps();
bool isComputeShaderAttached = (mState.mAttachedComputeShader != nullptr);
bool nonComputeShadersAttached =
(mState.mAttachedVertexShader != nullptr || mState.mAttachedFragmentShader != nullptr);
auto vertexShader = mState.mAttachedVertexShader;
auto fragmentShader = mState.mAttachedFragmentShader;
auto computeShader = mState.mAttachedComputeShader;
bool isComputeShaderAttached = (computeShader != nullptr);
bool nonComputeShadersAttached = (vertexShader != nullptr || fragmentShader != nullptr);
// Check whether we both have a compute and non-compute shaders attached.
// If there are of both types attached, then linking should fail.
// OpenGL ES 3.10, 7.3 Program Objects, under LinkProgram
......@@ -568,16 +578,16 @@ Error Program::link(const ContextState &data)
return NoError();
}
if (mState.mAttachedComputeShader)
if (computeShader)
{
if (!mState.mAttachedComputeShader->isCompiled())
if (!computeShader->isCompiled())
{
mInfoLog << "Attached compute shader is not compiled.";
return NoError();
}
ASSERT(mState.mAttachedComputeShader->getType() == GL_COMPUTE_SHADER);
ASSERT(computeShader->getType() == GL_COMPUTE_SHADER);
mState.mComputeShaderLocalSize = mState.mAttachedComputeShader->getWorkGroupSize();
mState.mComputeShaderLocalSize = computeShader->getWorkGroupSize();
// GLSL ES 3.10, 4.4.1.1 Compute Shader Inputs
// If the work group size is not specified, a link time error should occur.
......@@ -597,7 +607,8 @@ Error Program::link(const ContextState &data)
return NoError();
}
ANGLE_TRY_RESULT(mProgram->link(data, mInfoLog), mLinked);
gl::VaryingPacking noVaryingPacking(0, PackMode::ANGLE_RELAXED);
ANGLE_TRY_RESULT(mProgram->link(data, noVaryingPacking, mInfoLog), mLinked);
if (!mLinked)
{
return NoError();
......@@ -605,20 +616,19 @@ Error Program::link(const ContextState &data)
}
else
{
if (!mState.mAttachedFragmentShader || !mState.mAttachedFragmentShader->isCompiled())
if (!fragmentShader || !fragmentShader->isCompiled())
{
return NoError();
}
ASSERT(mState.mAttachedFragmentShader->getType() == GL_FRAGMENT_SHADER);
ASSERT(fragmentShader->getType() == GL_FRAGMENT_SHADER);
if (!mState.mAttachedVertexShader || !mState.mAttachedVertexShader->isCompiled())
if (!vertexShader || !vertexShader->isCompiled())
{
return NoError();
}
ASSERT(mState.mAttachedVertexShader->getType() == GL_VERTEX_SHADER);
ASSERT(vertexShader->getType() == GL_VERTEX_SHADER);
if (mState.mAttachedFragmentShader->getShaderVersion() !=
mState.mAttachedVertexShader->getShaderVersion())
if (fragmentShader->getShaderVersion() != vertexShader->getShaderVersion())
{
mInfoLog << "Fragment shader version does not match vertex shader version.";
return NoError();
......@@ -629,7 +639,7 @@ Error Program::link(const ContextState &data)
return NoError();
}
if (!linkVaryings(mInfoLog, mState.mAttachedVertexShader, mState.mAttachedFragmentShader))
if (!linkVaryings(mInfoLog))
{
return NoError();
}
......@@ -653,7 +663,21 @@ Error Program::link(const ContextState &data)
linkOutputVariables();
ANGLE_TRY_RESULT(mProgram->link(data, mInfoLog), mLinked);
// Validate we can pack the varyings.
std::vector<PackedVarying> packedVaryings = getPackedVaryings(mergedVaryings);
// Map the varyings to the register file
// In WebGL, we use a slightly different handling for packing variables.
auto packMode = data.getExtensions().webglCompatibility ? PackMode::WEBGL_STRICT
: PackMode::ANGLE_RELAXED;
VaryingPacking varyingPacking(data.getCaps().maxVaryingVectors, packMode);
if (!varyingPacking.packUserVaryings(mInfoLog, packedVaryings,
mState.getTransformFeedbackVaryingNames()))
{
return NoError();
}
ANGLE_TRY_RESULT(mProgram->link(data, varyingPacking, mInfoLog), mLinked);
if (!mLinked)
{
return NoError();
......@@ -1789,10 +1813,11 @@ GLenum Program::getTransformFeedbackBufferMode() const
return mState.mTransformFeedbackBufferMode;
}
bool Program::linkVaryings(InfoLog &infoLog,
const Shader *vertexShader,
const Shader *fragmentShader) const
bool Program::linkVaryings(InfoLog &infoLog) const
{
const Shader *vertexShader = mState.mAttachedVertexShader;
const Shader *fragmentShader = mState.mAttachedFragmentShader;
ASSERT(vertexShader->getShaderVersion() == fragmentShader->getShaderVersion());
const std::vector<sh::Varying> &vertexVaryings = vertexShader->getVaryings();
......@@ -2372,7 +2397,7 @@ bool Program::linkValidateVaryings(InfoLog &infoLog,
}
bool Program::linkValidateTransformFeedback(InfoLog &infoLog,
const std::vector<const sh::Varying *> &varyings,
const Program::MergedVaryings &varyings,
const Caps &caps) const
{
size_t totalComponents = 0;
......@@ -2382,8 +2407,10 @@ bool Program::linkValidateTransformFeedback(InfoLog &infoLog,
for (const std::string &tfVaryingName : mState.mTransformFeedbackVaryingNames)
{
bool found = false;
for (const sh::Varying *varying : varyings)
for (const auto &ref : varyings)
{
const sh::Varying *varying = ref.second.get();
if (tfVaryingName == varying->name)
{
if (uniqueNames.count(tfVaryingName) > 0)
......@@ -2439,14 +2466,15 @@ bool Program::linkValidateTransformFeedback(InfoLog &infoLog,
return true;
}
void Program::gatherTransformFeedbackVaryings(const std::vector<const sh::Varying *> &varyings)
void Program::gatherTransformFeedbackVaryings(const Program::MergedVaryings &varyings)
{
// Gather the linked varyings that are used for transform feedback, they should all exist.
mState.mTransformFeedbackVaryingVars.clear();
for (const std::string &tfVaryingName : mState.mTransformFeedbackVaryingNames)
{
for (const sh::Varying *varying : varyings)
for (const auto &ref : varyings)
{
const sh::Varying *varying = ref.second.get();
if (tfVaryingName == varying->name)
{
mState.mTransformFeedbackVaryingVars.push_back(*varying);
......@@ -2456,30 +2484,83 @@ void Program::gatherTransformFeedbackVaryings(const std::vector<const sh::Varyin
}
}
std::vector<const sh::Varying *> Program::getMergedVaryings() const
Program::MergedVaryings Program::getMergedVaryings() const
{
std::set<std::string> uniqueNames;
std::vector<const sh::Varying *> varyings;
MergedVaryings merged;
for (const sh::Varying &varying : mState.mAttachedVertexShader->getVaryings())
{
if (uniqueNames.count(varying.name) == 0)
{
uniqueNames.insert(varying.name);
varyings.push_back(&varying);
}
merged[varying.name].vertex = &varying;
}
for (const sh::Varying &varying : mState.mAttachedFragmentShader->getVaryings())
{
if (uniqueNames.count(varying.name) == 0)
merged[varying.name].fragment = &varying;
}
return merged;
}
std::vector<PackedVarying> Program::getPackedVaryings(
const Program::MergedVaryings &mergedVaryings) const
{
const std::vector<std::string> &tfVaryings = mState.getTransformFeedbackVaryingNames();
std::vector<PackedVarying> packedVaryings;
for (const auto &ref : mergedVaryings)
{
const sh::Varying *input = ref.second.vertex;
const sh::Varying *output = ref.second.fragment;
// Only pack varyings that have a matched input or output, plus special builtins.
if ((input && output) || (output && output->isBuiltIn()))
{
uniqueNames.insert(varying.name);
varyings.push_back(&varying);
// Will get the vertex shader interpolation by default.
auto interpolation = ref.second.get()->interpolation;
// Interpolation qualifiers must match.
if (output->isStruct())
{
ASSERT(!output->isArray());
for (const auto &field : output->fields)
{
ASSERT(!field.isStruct() && !field.isArray());
packedVaryings.push_back(PackedVarying(field, interpolation, output->name));
}
}
else
{
packedVaryings.push_back(PackedVarying(*output, interpolation));
}
continue;
}
// Keep Transform FB varyings in the merged list always.
if (!input)
{
continue;
}
for (const std::string &tfVarying : tfVaryings)
{
if (tfVarying == input->name)
{
// Transform feedback for varying structs is underspecified.
// See Khronos bug 9856.
// TODO(jmadill): Figure out how to be spec-compliant here.
if (!input->isStruct())
{
packedVaryings.push_back(PackedVarying(*input, input->interpolation));
packedVaryings.back().vertexOnly = true;
}
break;
}
}
}
return varyings;
std::sort(packedVaryings.begin(), packedVaryings.end(), ComparePackedVarying);
return packedVaryings;
}
void Program::linkOutputVariables()
......
......@@ -49,6 +49,7 @@ class Buffer;
class Framebuffer;
struct UniformBlock;
struct LinkedUniform;
struct PackedVarying;
extern const char * const g_fakepath;
......@@ -386,6 +387,16 @@ class Program final : angle::NonCopyable, public LabeledObject
std::unordered_map<std::string, GLuint> mBindings;
};
struct VaryingRef
{
const sh::Varying *get() const { return vertex ? vertex : fragment; }
const sh::Varying *vertex = nullptr;
const sh::Varying *fragment = nullptr;
};
using MergedVaryings = std::map<std::string, VaryingRef>;
void unlink(bool destroy = false);
void resetUniformBlockBindings();
......@@ -399,7 +410,7 @@ class Program final : angle::NonCopyable, public LabeledObject
const std::vector<sh::InterfaceBlock> &fragmentInterfaceBlocks,
InfoLog &infoLog) const;
bool linkUniformBlocks(InfoLog &infoLog, const Caps &caps);
bool linkVaryings(InfoLog &infoLog, const Shader *vertexShader, const Shader *fragmentShader) const;
bool linkVaryings(InfoLog &infoLog) const;
bool validateVertexAndFragmentUniforms(InfoLog &infoLog) const;
bool linkUniforms(InfoLog &infoLog, const Caps &caps, const Bindings &uniformBindings);
bool indexUniforms(InfoLog &infoLog, const Caps &caps, const Bindings &uniformBindings);
......@@ -419,14 +430,15 @@ class Program final : angle::NonCopyable, public LabeledObject
const sh::Varying &fragmentVarying,
int shaderVersion);
bool linkValidateTransformFeedback(InfoLog &infoLog,
const std::vector<const sh::Varying *> &linkedVaryings,
const MergedVaryings &linkedVaryings,
const Caps &caps) const;
void gatherTransformFeedbackVaryings(const std::vector<const sh::Varying *> &varyings);
void gatherTransformFeedbackVaryings(const MergedVaryings &varyings);
bool assignUniformBlockRegister(InfoLog &infoLog, UniformBlock *uniformBlock, GLenum shader, unsigned int registerIndex, const Caps &caps);
void defineOutputVariables(Shader *fragmentShader);
std::vector<const sh::Varying *> getMergedVaryings() const;
MergedVaryings getMergedVaryings() const;
std::vector<PackedVarying> getPackedVaryings(const MergedVaryings &mergedVaryings) const;
void linkOutputVariables();
bool flattenUniformsAndCheckCapsForShader(const Shader &shader,
......
......@@ -4,46 +4,22 @@
// found in the LICENSE file.
//
// VaryingPacking:
// Class which describes a mapping from varyings to registers in D3D
// for linking between shader stages.
// Class which describes a mapping from varyings to registers, according
// to the spec, or using custom packing algorithms. We also keep a register
// allocation list for the D3D renderer.
//
#include "libANGLE/renderer/d3d/hlsl/VaryingPacking.h"
#include "libANGLE/VaryingPacking.h"
#include "common/utilities.h"
#include "compiler/translator/blocklayoutHLSL.h"
#include "libANGLE/Program.h"
namespace rx
namespace gl
{
// Implementation of VaryingPacking::BuiltinVarying
VaryingPacking::BuiltinVarying::BuiltinVarying() : enabled(false), index(0), systemValue(false)
{
}
std::string VaryingPacking::BuiltinVarying::str() const
{
return (systemValue ? semantic : (semantic + Str(index)));
}
void VaryingPacking::BuiltinVarying::enableSystem(const std::string &systemValueSemantic)
{
enabled = true;
semantic = systemValueSemantic;
systemValue = true;
}
void VaryingPacking::BuiltinVarying::enable(const std::string &semanticVal, unsigned int indexVal)
{
enabled = true;
semantic = semanticVal;
index = indexVal;
}
// Implementation of VaryingPacking
VaryingPacking::VaryingPacking(GLuint maxVaryingVectors)
: mRegisterMap(maxVaryingVectors), mBuiltinInfo(SHADER_TYPE_MAX)
VaryingPacking::VaryingPacking(GLuint maxVaryingVectors, PackMode packMode)
: mRegisterMap(maxVaryingVectors), mPackMode(packMode)
{
}
......@@ -56,7 +32,7 @@ bool VaryingPacking::packVarying(const PackedVarying &packedVarying)
const auto &varying = *packedVarying.varying;
// "Non - square matrices of type matCxR consume the same space as a square matrix of type matN
// where N is the greater of C and R.Variables of type mat2 occupies 2 complete rows."
// where N is the greater of C and R."
// Here we are a bit more conservative and allow packing non-square matrices more tightly.
// Make sure we use transposed matrix types to count registers correctly.
ASSERT(!varying.isStruct());
......@@ -64,6 +40,13 @@ bool VaryingPacking::packVarying(const PackedVarying &packedVarying)
unsigned int varyingRows = gl::VariableRowCount(transposedType);
unsigned int varyingColumns = gl::VariableColumnCount(transposedType);
// "Variables of type mat2 occupies 2 complete rows."
// For non-WebGL contexts, we allow mat2 to occupy only two columns per row.
if (mPackMode == PackMode::WEBGL_STRICT && varying.type == GL_FLOAT_MAT2)
{
varyingColumns = 4;
}
// "Arrays of size N are assumed to take N times the size of the base type"
varyingRows *= varying.elementCount();
......@@ -161,7 +144,12 @@ bool VaryingPacking::packVarying(const PackedVarying &packedVarying)
registerInfo.registerColumn = bestColumn;
registerInfo.varyingArrayIndex = arrayIndex;
registerInfo.varyingRowIndex = 0;
mRegisterList.push_back(registerInfo);
// Do not record register info for builtins.
// TODO(jmadill): Clean this up.
if (!packedVarying.varying->isBuiltIn())
{
mRegisterList.push_back(registerInfo);
}
mRegisterMap[row + arrayIndex][bestColumn] = true;
}
break;
......@@ -218,7 +206,12 @@ void VaryingPacking::insert(unsigned int registerRow,
registerInfo.registerRow = registerRow + (arrayElement * varyingRows) + varyingRow;
registerInfo.varyingRowIndex = varyingRow;
registerInfo.varyingArrayIndex = arrayElement;
mRegisterList.push_back(registerInfo);
// Do not record register info for builtins.
// TODO(jmadill): Clean this up.
if (!packedVarying.varying->isBuiltIn())
{
mRegisterList.push_back(registerInfo);
}
for (unsigned int columnIndex = 0; columnIndex < varyingColumns; ++columnIndex)
{
......@@ -242,7 +235,7 @@ bool VaryingPacking::packUserVaryings(gl::InfoLog &infoLog,
const auto &varying = *packedVarying.varying;
// Do not assign registers to built-in or unreferenced varyings
if (varying.isBuiltIn() || (!varying.staticUse && !packedVarying.isStructField()))
if (!varying.staticUse && !packedVarying.isStructField())
{
continue;
}
......@@ -315,11 +308,6 @@ bool VaryingPacking::packUserVaryings(gl::InfoLog &infoLog,
return true;
}
bool VaryingPacking::validateBuiltins() const
{
return (static_cast<size_t>(getRegisterCount()) <= mRegisterMap.size());
}
unsigned int VaryingPacking::getRegisterCount() const
{
unsigned int count = 0;
......@@ -332,16 +320,6 @@ unsigned int VaryingPacking::getRegisterCount() const
}
}
if (mBuiltinInfo[SHADER_PIXEL].glFragCoord.enabled)
{
++count;
}
if (mBuiltinInfo[SHADER_PIXEL].glPointCoord.enabled)
{
++count;
}
return count;
}
......
......@@ -4,26 +4,23 @@
// found in the LICENSE file.
//
// VaryingPacking:
// Class which describes a mapping from varyings to registers in D3D
// for linking between shader stages.
// Class which describes a mapping from varyings to registers, according
// to the spec, or using custom packing algorithms. We also keep a register
// allocation list for the D3D renderer.
//
#ifndef LIBANGLE_RENDERER_D3D_HLSL_VARYINGPACKING_H_
#define LIBANGLE_RENDERER_D3D_HLSL_VARYINGPACKING_H_
#ifndef LIBANGLE_VARYINGPACKING_H_
#define LIBANGLE_VARYINGPACKING_H_
#include <GLSLANG/ShaderVars.h>
#include "angle_gl.h"
#include "common/angleutils.h"
#include "libANGLE/renderer/d3d/hlsl/hlsl_utils.h"
namespace gl
{
class InfoLog;
}
namespace rx
{
struct PackedVarying
{
PackedVarying(const sh::ShaderVariable &varyingIn, sh::InterpolationType interpolationIn)
......@@ -103,19 +100,25 @@ struct PackedVaryingRegister final
std::string structFieldName;
};
// Supported packing modes:
enum class PackMode
{
// We treat mat2 arrays as taking two full rows.
WEBGL_STRICT,
// We allow mat2 to take a 2x2 chunk.
ANGLE_RELAXED,
};
class VaryingPacking final : angle::NonCopyable
{
public:
VaryingPacking(GLuint maxVaryingVectors);
VaryingPacking(GLuint maxVaryingVectors, PackMode packMode);
bool packUserVaryings(gl::InfoLog &infoLog,
const std::vector<PackedVarying> &packedVaryings,
const std::vector<std::string> &transformFeedbackVaryings);
// Some built-in varyings require emulation that eats up available registers. This method
// checks that we're within the register limits of the implementation.
bool validateBuiltins() const;
struct Register
{
Register() { data[0] = data[1] = data[2] = data[3] = false; }
......@@ -135,34 +138,7 @@ class VaryingPacking final : angle::NonCopyable
return static_cast<unsigned int>(mRegisterList.size());
}
unsigned int getRegisterCount() const;
struct BuiltinVarying final : angle::NonCopyable
{
BuiltinVarying();
std::string str() const;
void enableSystem(const std::string &systemValueSemantic);
void enable(const std::string &semanticVal, unsigned int indexVal);
bool enabled;
std::string semantic;
unsigned int index;
bool systemValue;
};
struct BuiltinInfo
{
BuiltinVarying dxPosition;
BuiltinVarying glPosition;
BuiltinVarying glFragCoord;
BuiltinVarying glPointCoord;
BuiltinVarying glPointSize;
};
const BuiltinInfo &builtins(ShaderType shaderType) const { return mBuiltinInfo[shaderType]; }
BuiltinInfo &builtins(ShaderType shaderType) { return mBuiltinInfo[shaderType]; }
bool usesPointSize() const { return mBuiltinInfo[SHADER_VERTEX].glPointSize.enabled; }
size_t getRegisterMapSize() const { return mRegisterMap.size(); }
private:
bool packVarying(const PackedVarying &packedVarying);
......@@ -177,9 +153,9 @@ class VaryingPacking final : angle::NonCopyable
std::vector<Register> mRegisterMap;
std::vector<PackedVaryingRegister> mRegisterList;
std::vector<BuiltinInfo> mBuiltinInfo;
PackMode mPackMode;
};
} // namespace rx
} // namespace gl
#endif // LIBANGLE_RENDERER_D3D_HLSL_VARYINGPACKING_H_
#endif // LIBANGLE_VARYINGPACKING_H_
......@@ -7,12 +7,14 @@
// Tests for ANGLE's internal varying packing algorithm.
//
#include "libANGLE/renderer/d3d/hlsl/VaryingPacking.h"
#include "libANGLE/VaryingPacking.h"
#include <gtest/gtest.h>
#include "libANGLE/Program.h"
using namespace gl;
namespace
{
......@@ -22,26 +24,31 @@ class VaryingPackingTest : public ::testing::TestWithParam<GLuint>
VaryingPackingTest() {}
bool testVaryingPacking(const std::vector<sh::Varying> &shVaryings,
rx::VaryingPacking *varyingPacking)
VaryingPacking *varyingPacking)
{
std::vector<rx::PackedVarying> packedVaryings;
std::vector<PackedVarying> packedVaryings;
for (const auto &shVarying : shVaryings)
{
packedVaryings.push_back(rx::PackedVarying(shVarying, shVarying.interpolation));
packedVaryings.push_back(PackedVarying(shVarying, shVarying.interpolation));
}
gl::InfoLog infoLog;
InfoLog infoLog;
std::vector<std::string> transformFeedbackVaryings;
if (!varyingPacking->packUserVaryings(infoLog, packedVaryings, transformFeedbackVaryings))
return false;
return varyingPacking->validateBuiltins();
return varyingPacking->packUserVaryings(infoLog, packedVaryings, transformFeedbackVaryings);
}
// Uses the "relaxed" ANGLE packing mode.
bool packVaryings(GLuint maxVaryings, const std::vector<sh::Varying> &shVaryings)
{
rx::VaryingPacking varyingPacking(maxVaryings);
VaryingPacking varyingPacking(maxVaryings, PackMode::ANGLE_RELAXED);
return testVaryingPacking(shVaryings, &varyingPacking);
}
// Uses the stricter WebGL style packing rules.
bool packVaryingsStrict(GLuint maxVaryings, const std::vector<sh::Varying> &shVaryings)
{
VaryingPacking varyingPacking(maxVaryings, PackMode::WEBGL_STRICT);
return testVaryingPacking(shVaryings, &varyingPacking);
}
......@@ -85,38 +92,6 @@ TEST_P(VaryingPackingTest, OneVaryingLargerThanMax)
ASSERT_FALSE(packVaryings(1, MakeVaryings(GL_FLOAT_MAT4, 1, 0)));
}
// Tests that using FragCoord as a user varying will eat up a register.
TEST_P(VaryingPackingTest, MaxVaryingVec4PlusFragCoord)
{
const std::string &userSemantic = rx::GetVaryingSemantic(4, false);
rx::VaryingPacking varyingPacking(kMaxVaryings);
unsigned int reservedSemanticIndex = varyingPacking.getMaxSemanticIndex();
varyingPacking.builtins(rx::SHADER_PIXEL)
.glFragCoord.enable(userSemantic, reservedSemanticIndex);
const auto &varyings = MakeVaryings(GL_FLOAT_VEC4, kMaxVaryings, 0);
ASSERT_FALSE(testVaryingPacking(varyings, &varyingPacking));
}
// Tests that using PointCoord as a user varying will eat up a register.
TEST_P(VaryingPackingTest, MaxVaryingVec4PlusPointCoord)
{
const std::string &userSemantic = rx::GetVaryingSemantic(4, false);
rx::VaryingPacking varyingPacking(kMaxVaryings);
unsigned int reservedSemanticIndex = varyingPacking.getMaxSemanticIndex();
varyingPacking.builtins(rx::SHADER_PIXEL)
.glPointCoord.enable(userSemantic, reservedSemanticIndex);
const auto &varyings = MakeVaryings(GL_FLOAT_VEC4, kMaxVaryings, 0);
ASSERT_FALSE(testVaryingPacking(varyings, &varyingPacking));
}
// This will overflow the available varying space.
TEST_P(VaryingPackingTest, MaxPlusOneVaryingVec3)
{
......@@ -172,6 +147,13 @@ TEST_P(VaryingPackingTest, MaxVaryingVec3ArrayAndMaxPlusOneFloatArray)
ASSERT_FALSE(packVaryings(kMaxVaryings, varyings));
}
// WebGL should fail to pack max+1 vec2 arrays, unlike our more relaxed packing.
TEST_P(VaryingPackingTest, MaxPlusOneMat2VaryingsFailsWebGL)
{
auto varyings = MakeVaryings(GL_FLOAT_MAT2, kMaxVaryings / 2 + 1, 0);
ASSERT_FALSE(packVaryingsStrict(kMaxVaryings, varyings));
}
// Makes separate tests for different values of kMaxVaryings.
INSTANTIATE_TEST_CASE_P(, VaryingPackingTest, ::testing::Values(1, 4, 8));
......
......@@ -17,6 +17,11 @@
#include <map>
namespace gl
{
class VaryingPacking;
}
namespace sh
{
struct BlockMemberInfo;
......@@ -40,7 +45,9 @@ class ProgramImpl : angle::NonCopyable
virtual gl::Error save(gl::BinaryOutputStream *stream) = 0;
virtual void setBinaryRetrievableHint(bool retrievable) = 0;
virtual LinkResult link(const gl::ContextState &data, gl::InfoLog &infoLog) = 0;
virtual LinkResult link(const gl::ContextState &data,
const gl::VaryingPacking &packing,
gl::InfoLog &infoLog) = 0;
virtual GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) = 0;
virtual void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) = 0;
......
......@@ -27,7 +27,8 @@ class MockProgramImpl : public rx::ProgramImpl
MOCK_METHOD1(save, gl::Error(gl::BinaryOutputStream *));
MOCK_METHOD1(setBinaryRetrievableHint, void(bool));
MOCK_METHOD2(link, LinkResult(const gl::ContextState &, gl::InfoLog &));
MOCK_METHOD3(link,
LinkResult(const gl::ContextState &, const gl::VaryingPacking &, gl::InfoLog &));
MOCK_METHOD2(validate, GLboolean(const gl::Caps &, gl::InfoLog *));
MOCK_METHOD3(setUniform1fv, void(GLint, GLsizei, const GLfloat *));
......
......@@ -16,7 +16,7 @@
#include "libANGLE/renderer/d3d/ProgramD3D.h"
#include "libANGLE/renderer/d3d/RendererD3D.h"
#include "libANGLE/renderer/d3d/ShaderD3D.h"
#include "libANGLE/renderer/d3d/hlsl/VaryingPacking.h"
#include "libANGLE/VaryingPacking.h"
using namespace gl;
......@@ -127,48 +127,6 @@ DynamicHLSL::DynamicHLSL(RendererD3D *const renderer) : mRenderer(renderer)
{
}
void DynamicHLSL::generateVaryingHLSL(const VaryingPacking &varyingPacking,
std::stringstream &hlslStream) const
{
std::string varyingSemantic =
GetVaryingSemantic(mRenderer->getMajorShaderModel(), varyingPacking.usesPointSize());
for (const PackedVaryingRegister &registerInfo : varyingPacking.getRegisterList())
{
const auto &varying = *registerInfo.packedVarying->varying;
ASSERT(!varying.isStruct());
// TODO: Add checks to ensure D3D interpolation modifiers don't result in too many
// registers being used.
// For example, if there are N registers, and we have N vec3 varyings and 1 float
// varying, then D3D will pack them into N registers.
// If the float varying has the 'nointerpolation' modifier on it then we would need
// N + 1 registers, and D3D compilation will fail.
switch (registerInfo.packedVarying->interpolation)
{
case sh::INTERPOLATION_SMOOTH:
hlslStream << " ";
break;
case sh::INTERPOLATION_FLAT:
hlslStream << " nointerpolation ";
break;
case sh::INTERPOLATION_CENTROID:
hlslStream << " centroid ";
break;
default:
UNREACHABLE();
}
GLenum transposedType = gl::TransposeMatrixType(varying.type);
GLenum componentType = gl::VariableComponentType(transposedType);
int columnCount = gl::VariableColumnCount(transposedType);
hlslStream << HLSLComponentTypeString(componentType, columnCount);
unsigned int semanticIndex = registerInfo.semanticIndex;
hlslStream << " v" << semanticIndex << " : " << varyingSemantic << semanticIndex << ";\n";
}
}
std::string DynamicHLSL::generateVertexShaderForInputLayout(
const std::string &sourceShader,
const InputLayout &inputLayout,
......@@ -359,46 +317,81 @@ std::string DynamicHLSL::generatePixelShaderForOutputSignature(
return pixelHLSL;
}
void DynamicHLSL::generateVaryingLinkHLSL(ShaderType shaderType,
const VaryingPacking &varyingPacking,
std::stringstream &linkStream) const
void DynamicHLSL::generateVaryingLinkHLSL(const VaryingPacking &varyingPacking,
const BuiltinInfo &builtins,
bool programUsesPointSize,
std::stringstream &hlslStream) const
{
const auto &builtins = varyingPacking.builtins(shaderType);
ASSERT(builtins.dxPosition.enabled);
linkStream << "{\n"
hlslStream << "{\n"
<< " float4 dx_Position : " << builtins.dxPosition.str() << ";\n";
if (builtins.glPosition.enabled)
{
linkStream << " float4 gl_Position : " << builtins.glPosition.str() << ";\n";
hlslStream << " float4 gl_Position : " << builtins.glPosition.str() << ";\n";
}
if (builtins.glFragCoord.enabled)
{
linkStream << " float4 gl_FragCoord : " << builtins.glFragCoord.str() << ";\n";
hlslStream << " float4 gl_FragCoord : " << builtins.glFragCoord.str() << ";\n";
}
if (builtins.glPointCoord.enabled)
{
linkStream << " float2 gl_PointCoord : " << builtins.glPointCoord.str() << ";\n";
hlslStream << " float2 gl_PointCoord : " << builtins.glPointCoord.str() << ";\n";
}
if (builtins.glPointSize.enabled)
{
linkStream << " float gl_PointSize : " << builtins.glPointSize.str() << ";\n";
hlslStream << " float gl_PointSize : " << builtins.glPointSize.str() << ";\n";
}
// Do this after gl_PointSize, to potentially combine gl_PointCoord and gl_PointSize into the
// same register.
generateVaryingHLSL(varyingPacking, linkStream);
std::string varyingSemantic =
GetVaryingSemantic(mRenderer->getMajorShaderModel(), programUsesPointSize);
for (const PackedVaryingRegister &registerInfo : varyingPacking.getRegisterList())
{
const auto &varying = *registerInfo.packedVarying->varying;
ASSERT(!varying.isStruct());
// TODO: Add checks to ensure D3D interpolation modifiers don't result in too many
// registers being used.
// For example, if there are N registers, and we have N vec3 varyings and 1 float
// varying, then D3D will pack them into N registers.
// If the float varying has the 'nointerpolation' modifier on it then we would need
// N + 1 registers, and D3D compilation will fail.
linkStream << "};\n";
switch (registerInfo.packedVarying->interpolation)
{
case sh::INTERPOLATION_SMOOTH:
hlslStream << " ";
break;
case sh::INTERPOLATION_FLAT:
hlslStream << " nointerpolation ";
break;
case sh::INTERPOLATION_CENTROID:
hlslStream << " centroid ";
break;
default:
UNREACHABLE();
}
GLenum transposedType = gl::TransposeMatrixType(varying.type);
GLenum componentType = gl::VariableComponentType(transposedType);
int columnCount = gl::VariableColumnCount(transposedType);
hlslStream << HLSLComponentTypeString(componentType, columnCount);
unsigned int semanticIndex = registerInfo.semanticIndex;
hlslStream << " v" << semanticIndex << " : " << varyingSemantic << semanticIndex << ";\n";
}
hlslStream << "};\n";
}
bool DynamicHLSL::generateShaderLinkHLSL(const gl::ContextState &data,
void DynamicHLSL::generateShaderLinkHLSL(const gl::ContextState &data,
const gl::ProgramState &programData,
const ProgramD3DMetadata &programMetadata,
const VaryingPacking &varyingPacking,
const BuiltinVaryingsD3D &builtinsD3D,
std::string *pixelHLSL,
std::string *vertexHLSL) const
{
......@@ -435,9 +428,12 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::ContextState &data,
// Add stub string to be replaced when shader is dynamically defined by its layout
vertexStream << "\n" << std::string(VERTEX_ATTRIBUTE_STUB_STRING) << "\n";
const auto &vertexBuiltins = builtinsD3D[SHADER_VERTEX];
// Write the HLSL input/output declarations
vertexStream << "struct VS_OUTPUT\n";
generateVaryingLinkHLSL(SHADER_VERTEX, varyingPacking, vertexStream);
generateVaryingLinkHLSL(varyingPacking, vertexBuiltins, builtinsD3D.usesPointSize(),
vertexStream);
vertexStream << "\n"
<< "VS_OUTPUT main(VS_INPUT input)\n"
<< "{\n"
......@@ -448,8 +444,6 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::ContextState &data,
<< "\n"
<< " VS_OUTPUT output;\n";
const auto &vertexBuiltins = varyingPacking.builtins(SHADER_VERTEX);
if (vertexBuiltins.glPosition.enabled)
{
vertexStream << " output.gl_Position = gl_Position;\n";
......@@ -584,10 +578,13 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::ContextState &data,
<< " return output;\n"
<< "}\n";
const auto &pixelBuiltins = builtinsD3D[SHADER_PIXEL];
std::stringstream pixelStream;
pixelStream << fragmentShaderGL->getTranslatedSource();
pixelStream << "struct PS_INPUT\n";
generateVaryingLinkHLSL(SHADER_PIXEL, varyingPacking, pixelStream);
generateVaryingLinkHLSL(varyingPacking, pixelBuiltins, builtinsD3D.usesPointSize(),
pixelStream);
pixelStream << "\n";
pixelStream << std::string(PIXEL_OUTPUT_STUB_STRING) << "\n";
......@@ -611,8 +608,6 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::ContextState &data,
<< "{\n";
}
const auto &pixelBuiltins = varyingPacking.builtins(SHADER_PIXEL);
if (pixelBuiltins.glFragCoord.enabled)
{
pixelStream << " float rhw = 1.0 / input.gl_FragCoord.w;\n";
......@@ -767,30 +762,31 @@ bool DynamicHLSL::generateShaderLinkHLSL(const gl::ContextState &data,
*vertexHLSL = vertexStream.str();
*pixelHLSL = pixelStream.str();
return true;
}
std::string DynamicHLSL::generateGeometryShaderPreamble(const VaryingPacking &varyingPacking) const
std::string DynamicHLSL::generateGeometryShaderPreamble(const VaryingPacking &varyingPacking,
const BuiltinVaryingsD3D &builtinsD3D) const
{
ASSERT(mRenderer->getMajorShaderModel() >= 4);
std::stringstream preambleStream;
const auto &builtins = varyingPacking.builtins(SHADER_VERTEX);
const auto &vertexBuiltins = builtinsD3D[SHADER_VERTEX];
preambleStream << "struct GS_INPUT\n";
generateVaryingLinkHLSL(SHADER_VERTEX, varyingPacking, preambleStream);
generateVaryingLinkHLSL(varyingPacking, vertexBuiltins, builtinsD3D.usesPointSize(),
preambleStream);
preambleStream << "\n"
<< "struct GS_OUTPUT\n";
generateVaryingLinkHLSL(SHADER_GEOMETRY, varyingPacking, preambleStream);
generateVaryingLinkHLSL(varyingPacking, builtinsD3D[SHADER_GEOMETRY],
builtinsD3D.usesPointSize(), preambleStream);
preambleStream
<< "\n"
<< "void copyVertex(inout GS_OUTPUT output, GS_INPUT input, GS_INPUT flatinput)\n"
<< "{\n"
<< " output.gl_Position = input.gl_Position;\n";
if (builtins.glPointSize.enabled)
if (vertexBuiltins.glPointSize.enabled)
{
preambleStream << " output.gl_PointSize = input.gl_PointSize;\n";
}
......@@ -805,7 +801,7 @@ std::string DynamicHLSL::generateGeometryShaderPreamble(const VaryingPacking &va
preambleStream << "input.v" << varyingRegister.semanticIndex << "; \n";
}
if (builtins.glFragCoord.enabled)
if (vertexBuiltins.glFragCoord.enabled)
{
preambleStream << " output.gl_FragCoord = input.gl_FragCoord;\n";
}
......@@ -1077,4 +1073,97 @@ void DynamicHLSL::getPixelShaderOutputKey(const gl::ContextState &data,
}
}
// BuiltinVarying Implementation.
BuiltinVarying::BuiltinVarying() : enabled(false), index(0), systemValue(false)
{
}
std::string BuiltinVarying::str() const
{
return (systemValue ? semantic : (semantic + Str(index)));
}
void BuiltinVarying::enableSystem(const std::string &systemValueSemantic)
{
enabled = true;
semantic = systemValueSemantic;
systemValue = true;
}
void BuiltinVarying::enable(const std::string &semanticVal, unsigned int indexVal)
{
enabled = true;
semantic = semanticVal;
index = indexVal;
}
// BuiltinVaryingsD3D Implementation.
BuiltinVaryingsD3D::BuiltinVaryingsD3D(const ProgramD3DMetadata &metadata,
const VaryingPacking &packing)
{
updateBuiltins(SHADER_VERTEX, metadata, packing);
updateBuiltins(SHADER_PIXEL, metadata, packing);
if (metadata.getRendererMajorShaderModel() >= 4)
{
updateBuiltins(SHADER_GEOMETRY, metadata, packing);
}
}
void BuiltinVaryingsD3D::updateBuiltins(ShaderType shaderType,
const ProgramD3DMetadata &metadata,
const VaryingPacking &packing)
{
const std::string &userSemantic = GetVaryingSemantic(metadata.getRendererMajorShaderModel(),
metadata.usesSystemValuePointSize());
unsigned int reservedSemanticIndex = packing.getMaxSemanticIndex();
BuiltinInfo *builtins = &mBuiltinInfo[shaderType];
if (metadata.getRendererMajorShaderModel() >= 4)
{
builtins->dxPosition.enableSystem("SV_Position");
}
else if (shaderType == SHADER_PIXEL)
{
builtins->dxPosition.enableSystem("VPOS");
}
else
{
builtins->dxPosition.enableSystem("POSITION");
}
if (metadata.usesTransformFeedbackGLPosition())
{
builtins->glPosition.enable(userSemantic, reservedSemanticIndex++);
}
if (metadata.usesFragCoord())
{
builtins->glFragCoord.enable(userSemantic, reservedSemanticIndex++);
}
if (shaderType == SHADER_VERTEX ? metadata.addsPointCoordToVertexShader()
: metadata.usesPointCoord())
{
// SM3 reserves the TEXCOORD semantic for point sprite texcoords (gl_PointCoord)
// In D3D11 we manually compute gl_PointCoord in the GS.
if (metadata.getRendererMajorShaderModel() >= 4)
{
builtins->glPointCoord.enable(userSemantic, reservedSemanticIndex++);
}
else
{
builtins->glPointCoord.enable("TEXCOORD", 0);
}
}
// Special case: do not include PSIZE semantic in HLSL 3 pixel shaders
if (metadata.usesSystemValuePointSize() &&
(shaderType != SHADER_PIXEL || metadata.getRendererMajorShaderModel() >= 4))
{
builtins->glPointSize.enableSystem("PSIZE");
}
}
} // namespace rx
......@@ -29,15 +29,14 @@ namespace gl
{
class InfoLog;
struct VariableLocation;
class VaryingPacking;
struct VertexAttribute;
}
namespace rx
{
struct PackedVarying;
class ProgramD3DMetadata;
class ShaderD3D;
class VaryingPacking;
struct PixelShaderOutputVariable
{
......@@ -47,6 +46,54 @@ struct PixelShaderOutputVariable
size_t outputIndex;
};
struct BuiltinVarying final : angle::NonCopyable
{
BuiltinVarying();
std::string str() const;
void enableSystem(const std::string &systemValueSemantic);
void enable(const std::string &semanticVal, unsigned int indexVal);
bool enabled;
std::string semantic;
unsigned int index;
bool systemValue;
};
struct BuiltinInfo
{
BuiltinVarying dxPosition;
BuiltinVarying glPosition;
BuiltinVarying glFragCoord;
BuiltinVarying glPointCoord;
BuiltinVarying glPointSize;
};
inline std::string GetVaryingSemantic(int majorShaderModel, bool programUsesPointSize)
{
// SM3 reserves the TEXCOORD semantic for point sprite texcoords (gl_PointCoord)
// In D3D11 we manually compute gl_PointCoord in the GS.
return ((programUsesPointSize && majorShaderModel < 4) ? "COLOR" : "TEXCOORD");
}
class BuiltinVaryingsD3D
{
public:
BuiltinVaryingsD3D(const ProgramD3DMetadata &metadata, const gl::VaryingPacking &packing);
bool usesPointSize() const { return mBuiltinInfo[SHADER_VERTEX].glPointSize.enabled; }
const BuiltinInfo &operator[](ShaderType shaderType) const { return mBuiltinInfo[shaderType]; }
BuiltinInfo &operator[](ShaderType shaderType) { return mBuiltinInfo[shaderType]; }
private:
void updateBuiltins(ShaderType shaderType,
const ProgramD3DMetadata &metadata,
const gl::VaryingPacking &packing);
std::array<BuiltinInfo, SHADER_TYPE_MAX> mBuiltinInfo;
};
class DynamicHLSL : angle::NonCopyable
{
public:
......@@ -61,14 +108,16 @@ class DynamicHLSL : angle::NonCopyable
const std::vector<PixelShaderOutputVariable> &outputVariables,
bool usesFragDepth,
const std::vector<GLenum> &outputLayout) const;
bool generateShaderLinkHLSL(const gl::ContextState &data,
void generateShaderLinkHLSL(const gl::ContextState &data,
const gl::ProgramState &programData,
const ProgramD3DMetadata &programMetadata,
const VaryingPacking &varyingPacking,
const gl::VaryingPacking &varyingPacking,
const BuiltinVaryingsD3D &builtinsD3D,
std::string *pixelHLSL,
std::string *vertexHLSL) const;
std::string generateGeometryShaderPreamble(const VaryingPacking &varyingPacking) const;
std::string generateGeometryShaderPreamble(const gl::VaryingPacking &varyingPacking,
const BuiltinVaryingsD3D &builtinsD3D) const;
std::string generateGeometryShaderHLSL(gl::PrimitiveType primitiveType,
const gl::ContextState &data,
......@@ -84,11 +133,10 @@ class DynamicHLSL : angle::NonCopyable
private:
RendererD3D *const mRenderer;
void generateVaryingLinkHLSL(ShaderType shaderType,
const VaryingPacking &varyingPacking,
std::stringstream &linkStream) const;
void generateVaryingHLSL(const VaryingPacking &varyingPacking,
std::stringstream &hlslStream) const;
void generateVaryingLinkHLSL(const gl::VaryingPacking &varyingPacking,
const BuiltinInfo &builtins,
bool programUsesPointSize,
std::stringstream &hlslStream) const;
// Prepend an underscore
static std::string decorateVariable(const std::string &name);
......@@ -97,6 +145,6 @@ class DynamicHLSL : angle::NonCopyable
const sh::ShaderVariable &shaderAttrib) const;
};
}
} // namespace rx
#endif // LIBANGLE_RENDERER_D3D_DYNAMICHLSL_H_
......@@ -14,6 +14,7 @@
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Program.h"
#include "libANGLE/Uniform.h"
#include "libANGLE/VaryingPacking.h"
#include "libANGLE/VertexArray.h"
#include "libANGLE/features.h"
#include "libANGLE/renderer/d3d/DynamicHLSL.h"
......@@ -22,7 +23,6 @@
#include "libANGLE/renderer/d3d/ShaderD3D.h"
#include "libANGLE/renderer/d3d/ShaderExecutableD3D.h"
#include "libANGLE/renderer/d3d/VertexDataManager.h"
#include "libANGLE/renderer/d3d/hlsl/VaryingPacking.h"
using namespace angle;
......@@ -82,77 +82,6 @@ bool IsRowMajorLayout(const sh::ShaderVariable &var)
return false;
}
// true if varying x has a higher priority in packing than y
bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y)
{
return gl::CompareShaderVar(*x.varying, *y.varying);
}
std::vector<PackedVarying> MergeVaryings(const gl::Shader &vertexShader,
const gl::Shader &fragmentShader,
const std::vector<std::string> &tfVaryings)
{
std::vector<PackedVarying> packedVaryings;
for (const sh::Varying &output : vertexShader.getVaryings())
{
bool packed = false;
// Built-in varyings obey special rules
if (output.isBuiltIn())
{
continue;
}
for (const sh::Varying &input : fragmentShader.getVaryings())
{
if (output.name == input.name)
{
if (output.isStruct())
{
ASSERT(!output.isArray());
for (const auto &field : output.fields)
{
ASSERT(!field.isStruct() && !field.isArray());
packedVaryings.push_back(
PackedVarying(field, input.interpolation, input.name));
}
}
else
{
packedVaryings.push_back(PackedVarying(input, input.interpolation));
}
packed = true;
break;
}
}
// Keep Transform FB varyings in the merged list always.
if (!packed)
{
for (const std::string &tfVarying : tfVaryings)
{
if (tfVarying == output.name)
{
// Transform feedback for varying structs is underspecified.
// See Khronos bug 9856.
// TODO(jmadill): Figure out how to be spec-compliant here.
if (!output.isStruct())
{
packedVaryings.push_back(PackedVarying(output, output.interpolation));
packedVaryings.back().vertexOnly = true;
}
break;
}
}
}
}
std::sort(packedVaryings.begin(), packedVaryings.end(), ComparePackedVarying);
return packedVaryings;
}
template <typename VarT>
void GetUniformBlockInfo(const std::vector<VarT> &fields,
const std::string &prefix,
......@@ -288,6 +217,20 @@ gl::PrimitiveType GetGeometryShaderTypeFromDrawMode(GLenum drawMode)
}
}
bool FindFlatInterpolationVarying(const std::vector<sh::Varying> &varyings)
{
// Note: this assumes nested structs can only be packed with one interpolation.
for (const auto &varying : varyings)
{
if (varying.interpolation == sh::INTERPOLATION_FLAT)
{
return true;
}
}
return false;
}
} // anonymous namespace
// D3DUniform Implementation
......@@ -453,60 +396,6 @@ const ShaderD3D *ProgramD3DMetadata::getFragmentShader() const
return mFragmentShader;
}
void ProgramD3DMetadata::updatePackingBuiltins(ShaderType shaderType, VaryingPacking *packing)
{
const std::string &userSemantic =
GetVaryingSemantic(mRendererMajorShaderModel, usesSystemValuePointSize());
unsigned int reservedSemanticIndex = packing->getMaxSemanticIndex();
VaryingPacking::BuiltinInfo *builtins = &packing->builtins(shaderType);
if (mRendererMajorShaderModel >= 4)
{
builtins->dxPosition.enableSystem("SV_Position");
}
else if (shaderType == SHADER_PIXEL)
{
builtins->dxPosition.enableSystem("VPOS");
}
else
{
builtins->dxPosition.enableSystem("POSITION");
}
if (usesTransformFeedbackGLPosition())
{
builtins->glPosition.enable(userSemantic, reservedSemanticIndex++);
}
if (usesFragCoord())
{
builtins->glFragCoord.enable(userSemantic, reservedSemanticIndex++);
}
if (shaderType == SHADER_VERTEX ? addsPointCoordToVertexShader() : usesPointCoord())
{
// SM3 reserves the TEXCOORD semantic for point sprite texcoords (gl_PointCoord)
// In D3D11 we manually compute gl_PointCoord in the GS.
if (mRendererMajorShaderModel >= 4)
{
builtins->glPointCoord.enable(userSemantic, reservedSemanticIndex++);
}
else
{
builtins->glPointCoord.enable("TEXCOORD", 0);
}
}
// Special case: do not include PSIZE semantic in HLSL 3 pixel shaders
if (usesSystemValuePointSize() &&
(shaderType != SHADER_PIXEL || mRendererMajorShaderModel >= 4))
{
builtins->glPointSize.enableSystem("PSIZE");
}
}
// ProgramD3D Implementation
ProgramD3D::VertexExecutable::VertexExecutable(const gl::InputLayout &inputLayout,
......@@ -1448,7 +1337,9 @@ LinkResult ProgramD3D::compileProgramExecutables(const gl::ContextState &context
(!usesGeometryShader(GL_POINTS) || pointGS));
}
LinkResult ProgramD3D::link(const gl::ContextState &data, gl::InfoLog &infoLog)
LinkResult ProgramD3D::link(const gl::ContextState &data,
const gl::VaryingPacking &packing,
gl::InfoLog &infoLog)
{
reset();
......@@ -1473,70 +1364,40 @@ LinkResult ProgramD3D::link(const gl::ContextState &data, gl::InfoLog &infoLog)
}
}
std::vector<PackedVarying> packedVaryings =
MergeVaryings(*vertexShader, *fragmentShader, mState.getTransformFeedbackVaryingNames());
// Map the varyings to the register file
VaryingPacking varyingPacking(data.getCaps().maxVaryingVectors);
if (!varyingPacking.packUserVaryings(infoLog, packedVaryings,
mState.getTransformFeedbackVaryingNames()))
{
return false;
}
ProgramD3DMetadata metadata(mRenderer, vertexShaderD3D, fragmentShaderD3D);
metadata.updatePackingBuiltins(SHADER_VERTEX, &varyingPacking);
metadata.updatePackingBuiltins(SHADER_PIXEL, &varyingPacking);
if (!varyingPacking.validateBuiltins())
{
infoLog << "No varying registers left to support gl_FragCoord/gl_PointCoord";
return false;
}
// TODO(jmadill): Implement more sophisticated component packing in D3D9.
// We can fail here because we use one semantic per GLSL varying. D3D11 can pack varyings
// intelligently, but D3D9 assumes one semantic per register.
if (mRenderer->getRendererClass() == RENDERER_D3D9 &&
varyingPacking.getMaxSemanticIndex() > data.getCaps().maxVaryingVectors)
packing.getMaxSemanticIndex() > data.getCaps().maxVaryingVectors)
{
infoLog << "Cannot pack these varyings on D3D9.";
return false;
}
if (!mDynamicHLSL->generateShaderLinkHLSL(data, mState, metadata, varyingPacking, &mPixelHLSL,
&mVertexHLSL))
{
return false;
}
ProgramD3DMetadata metadata(mRenderer, vertexShaderD3D, fragmentShaderD3D);
BuiltinVaryingsD3D builtins(metadata, packing);
mDynamicHLSL->generateShaderLinkHLSL(data, mState, metadata, packing, builtins, &mPixelHLSL,
&mVertexHLSL);
mUsesPointSize = vertexShaderD3D->usesPointSize();
mDynamicHLSL->getPixelShaderOutputKey(data, mState, metadata, &mPixelShaderKey);
mUsesFragDepth = metadata.usesFragDepth();
// Cache if we use flat shading
mUsesFlatInterpolation = false;
for (const auto &varying : packedVaryings)
{
if (varying.interpolation == sh::INTERPOLATION_FLAT)
{
mUsesFlatInterpolation = true;
break;
}
}
mUsesFlatInterpolation = (FindFlatInterpolationVarying(fragmentShader->getVaryings()) ||
FindFlatInterpolationVarying(vertexShader->getVaryings()));
if (mRenderer->getMajorShaderModel() >= 4)
{
metadata.updatePackingBuiltins(SHADER_GEOMETRY, &varyingPacking);
mGeometryShaderPreamble = mDynamicHLSL->generateGeometryShaderPreamble(varyingPacking);
mGeometryShaderPreamble = mDynamicHLSL->generateGeometryShaderPreamble(packing, builtins);
}
initAttribLocationsToD3DSemantic();
defineUniformsAndAssignRegisters();
gatherTransformFeedbackVaryings(varyingPacking);
gatherTransformFeedbackVaryings(packing, builtins[SHADER_VERTEX]);
LinkResult result = compileProgramExecutables(data, infoLog);
if (result.isError())
......@@ -2312,10 +2173,9 @@ void ProgramD3D::updateCachedInputLayout(const gl::State &state)
}
}
void ProgramD3D::gatherTransformFeedbackVaryings(const VaryingPacking &varyingPacking)
void ProgramD3D::gatherTransformFeedbackVaryings(const gl::VaryingPacking &varyingPacking,
const BuiltinInfo &builtins)
{
const auto &builtins = varyingPacking.builtins(SHADER_VERTEX);
const std::string &varyingSemantic =
GetVaryingSemantic(mRenderer->getMajorShaderModel(), usesPointSize());
......@@ -2352,7 +2212,7 @@ void ProgramD3D::gatherTransformFeedbackVaryings(const VaryingPacking &varyingPa
}
else
{
for (const PackedVaryingRegister &registerInfo : varyingPacking.getRegisterList())
for (const auto &registerInfo : varyingPacking.getRegisterList())
{
const auto &varying = *registerInfo.packedVarying->varying;
GLenum transposedType = gl::TransposeMatrixType(varying.type);
......
......@@ -119,9 +119,6 @@ class ProgramD3DMetadata final : angle::NonCopyable
GLint getMajorShaderVersion() const;
const ShaderD3D *getFragmentShader() const;
// Applies the metadata structure to the varying packing.
void updatePackingBuiltins(ShaderType shaderType, VaryingPacking *packing);
private:
const int mRendererMajorShaderModel;
const std::string mShaderModelSuffix;
......@@ -170,7 +167,9 @@ class ProgramD3D : public ProgramImpl
ShaderExecutableD3D **outExecutable,
gl::InfoLog *infoLog);
LinkResult link(const gl::ContextState &data, gl::InfoLog &infoLog) override;
LinkResult link(const gl::ContextState &data,
const gl::VaryingPacking &packing,
gl::InfoLog &infoLog) override;
GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override;
bool getUniformBlockSize(const std::string &blockName, size_t *sizeOut) const override;
......@@ -354,7 +353,8 @@ class ProgramD3D : public ProgramImpl
LinkResult compileProgramExecutables(const gl::ContextState &data, gl::InfoLog &infoLog);
void gatherTransformFeedbackVaryings(const VaryingPacking &varyings);
void gatherTransformFeedbackVaryings(const gl::VaryingPacking &varyings,
const BuiltinInfo &builtins);
D3DUniform *getD3DUniformByName(const std::string &name);
D3DUniform *getD3DUniformFromLocation(GLint location);
......
......@@ -17,7 +17,6 @@
#include "libANGLE/Device.h"
#include "libANGLE/formatutils.h"
#include "libANGLE/renderer/d3d/VertexDataManager.h"
#include "libANGLE/renderer/d3d/hlsl/hlsl_utils.h"
#include "libANGLE/renderer/d3d/formatutilsD3D.h"
#include "libANGLE/Version.h"
#include "libANGLE/WorkerThread.h"
......@@ -71,6 +70,14 @@ enum RendererClass
RENDERER_D3D9
};
enum ShaderType
{
SHADER_VERTEX,
SHADER_PIXEL,
SHADER_GEOMETRY,
SHADER_TYPE_MAX
};
// Useful for unit testing
class BufferFactoryD3D : angle::NonCopyable
{
......
//
// 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.
//
// hlsl_utils: Helper functions for HLSL.
#ifndef LIBANGLE_RENDERER_D3D_HLSL_HLSL_UTILS_H_
#define LIBANGLE_RENDERER_D3D_HLSL_HLSL_UTILS_H_
#include <string>
namespace rx
{
enum ShaderType
{
SHADER_VERTEX,
SHADER_PIXEL,
SHADER_GEOMETRY,
SHADER_TYPE_MAX
};
inline std::string GetVaryingSemantic(int majorShaderModel, bool programUsesPointSize)
{
// SM3 reserves the TEXCOORD semantic for point sprite texcoords (gl_PointCoord)
// In D3D11 we manually compute gl_PointCoord in the GS.
return ((programUsesPointSize && majorShaderModel < 4) ? "COLOR" : "TEXCOORD");
}
} // namespace rx
#endif // LIBANGLE_RENDERER_D3D_HLSL_HLSL_UTILS_H_
......@@ -111,7 +111,9 @@ void ProgramGL::setBinaryRetrievableHint(bool retrievable)
}
}
LinkResult ProgramGL::link(const gl::ContextState &data, gl::InfoLog &infoLog)
LinkResult ProgramGL::link(const gl::ContextState &data,
const gl::VaryingPacking &packing,
gl::InfoLog &infoLog)
{
preLink();
......
......@@ -43,7 +43,9 @@ class ProgramGL : public ProgramImpl
gl::Error save(gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override;
LinkResult link(const gl::ContextState &data, gl::InfoLog &infoLog) override;
LinkResult link(const gl::ContextState &data,
const gl::VaryingPacking &packing,
gl::InfoLog &infoLog) override;
GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override;
void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override;
......
......@@ -38,7 +38,9 @@ void ProgramNULL::setBinaryRetrievableHint(bool retrievable)
{
}
LinkResult ProgramNULL::link(const gl::ContextState &data, gl::InfoLog &infoLog)
LinkResult ProgramNULL::link(const gl::ContextState &data,
const gl::VaryingPacking &packing,
gl::InfoLog &infoLog)
{
return true;
}
......
......@@ -27,7 +27,9 @@ class ProgramNULL : public ProgramImpl
gl::Error save(gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override;
LinkResult link(const gl::ContextState &data, gl::InfoLog &infoLog) override;
LinkResult link(const gl::ContextState &data,
const gl::VaryingPacking &packing,
gl::InfoLog &infoLog) override;
GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override;
void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override;
......
......@@ -41,7 +41,9 @@ void ProgramVk::setBinaryRetrievableHint(bool retrievable)
UNIMPLEMENTED();
}
LinkResult ProgramVk::link(const gl::ContextState &data, gl::InfoLog &infoLog)
LinkResult ProgramVk::link(const gl::ContextState &data,
const gl::VaryingPacking &packing,
gl::InfoLog &infoLog)
{
UNIMPLEMENTED();
return gl::Error(GL_INVALID_OPERATION);
......
......@@ -27,7 +27,9 @@ class ProgramVk : public ProgramImpl
gl::Error save(gl::BinaryOutputStream *stream) override;
void setBinaryRetrievableHint(bool retrievable) override;
LinkResult link(const gl::ContextState &data, gl::InfoLog &infoLog) override;
LinkResult link(const gl::ContextState &data,
const gl::VaryingPacking &packing,
gl::InfoLog &infoLog) override;
GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override;
void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override;
......
......@@ -11,6 +11,7 @@
#include <gtest/gtest.h>
#include "libANGLE/ContextState.h"
#include "libANGLE/VaryingPacking.h"
#include "libANGLE/renderer/FramebufferImpl_mock.h"
#include "libANGLE/renderer/ProgramImpl_mock.h"
#include "libANGLE/renderer/TextureImpl_mock.h"
......
......@@ -146,6 +146,8 @@
'libANGLE/TransformFeedback.h',
'libANGLE/Uniform.cpp',
'libANGLE/Uniform.h',
'libANGLE/VaryingPacking.cpp',
'libANGLE/VaryingPacking.h',
'libANGLE/Version.h',
'libANGLE/Version.inl',
'libANGLE/VertexArray.cpp',
......@@ -271,12 +273,6 @@
'libANGLE/renderer/d3d/VertexDataManager.cpp',
'libANGLE/renderer/d3d/VertexDataManager.h',
],
'libangle_d3d_hlsl_sources':
[
'libANGLE/renderer/d3d/hlsl/VaryingPacking.cpp',
'libANGLE/renderer/d3d/hlsl/VaryingPacking.h',
'libANGLE/renderer/d3d/hlsl/hlsl_utils.h',
],
'libangle_d3d9_sources':
[
'libANGLE/renderer/d3d/d3d9/Blit9.cpp',
......@@ -830,7 +826,6 @@
{
'sources':
[
'<@(libangle_d3d_hlsl_sources)',
'<@(libangle_d3d_shared_sources)',
],
}],
......
......@@ -32,6 +32,7 @@
'<(angle_path)/src/libANGLE/ResourceManager_unittest.cpp',
'<(angle_path)/src/libANGLE/Surface_unittest.cpp',
'<(angle_path)/src/libANGLE/TransformFeedback_unittest.cpp',
'<(angle_path)/src/libANGLE/VaryingPacking_unittest.cpp',
'<(angle_path)/src/libANGLE/WorkerThread_unittest.cpp',
'<(angle_path)/src/libANGLE/renderer/BufferImpl_mock.h',
'<(angle_path)/src/libANGLE/renderer/FramebufferImpl_mock.h',
......@@ -103,7 +104,6 @@
# TODO(jmadill): should probably call this windows sources
'angle_unittests_hlsl_sources':
[
'<(angle_path)/src/libANGLE/renderer/d3d/VaryingPacking_unittest.cpp',
'<(angle_path)/src/tests/compiler_tests/UnrollFlatten_test.cpp',
],
},
......
......@@ -1357,7 +1357,12 @@ TEST_P(GLSLTest, MaxVaryingVec2Arrays)
GLint maxVaryings = 0;
glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
VaryingTestBase(0, 0, 0, maxVaryings, 0, 0, 0, 0, false, false, false, true);
// Special case: because arrays of mat2 are packed as small grids of two rows by two columns,
// we should be aware that when we're packing into an odd number of varying registers the
// last row will be empty and can not fit the final vec2 arrary.
GLint maxVec2Arrays = (maxVaryings >> 1) << 1;
VaryingTestBase(0, 0, 0, maxVec2Arrays, 0, 0, 0, 0, false, false, false, true);
}
// Verify shader source with a fixed length that is less than the null-terminated length will compile.
......@@ -2520,6 +2525,73 @@ TEST_P(GLSLTest_ES3, MultipleDeclarationInForLoopEmptyExpression)
ANGLE_GL_PROGRAM(program, mSimpleVSSource, fragmentShader);
}
class WebGLGLSLTest : public GLSLTest
{
protected:
WebGLGLSLTest() { setWebGLCompatibilityEnabled(true); }
};
TEST_P(WebGLGLSLTest, MaxVaryingVec4PlusFragCoord)
{
GLint maxVaryings = 0;
glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
// Generate shader code that uses gl_FragCoord, a special fragment shader variables.
// This test should fail, since we are really using (maxVaryings + 1) varyings.
VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings, 0, true, false, false, false);
}
TEST_P(WebGLGLSLTest, MaxVaryingVec4PlusPointCoord)
{
GLint maxVaryings = 0;
glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
// Generate shader code that uses gl_FragCoord, a special fragment shader variables.
// This test should fail, since we are really using (maxVaryings + 1) varyings.
VaryingTestBase(0, 0, 0, 0, 0, 0, maxVaryings, 0, false, true, false, false);
}
TEST_P(WebGLGLSLTest, MaxPlusOneVaryingVec3)
{
GLint maxVaryings = 0;
glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
VaryingTestBase(0, 0, 0, 0, maxVaryings + 1, 0, 0, 0, false, false, false, false);
}
TEST_P(WebGLGLSLTest, MaxPlusOneVaryingVec3Array)
{
GLint maxVaryings = 0;
glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
VaryingTestBase(0, 0, 0, 0, 0, maxVaryings / 2 + 1, 0, 0, false, false, false, false);
}
TEST_P(WebGLGLSLTest, MaxVaryingVec3AndOneVec2)
{
GLint maxVaryings = 0;
glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
VaryingTestBase(0, 0, 1, 0, maxVaryings, 0, 0, 0, false, false, false, false);
}
TEST_P(WebGLGLSLTest, MaxPlusOneVaryingVec2)
{
GLint maxVaryings = 0;
glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
VaryingTestBase(0, 0, 2 * maxVaryings + 1, 0, 0, 0, 0, 0, false, false, false, false);
}
TEST_P(WebGLGLSLTest, MaxVaryingVec3ArrayAndMaxPlusOneFloatArray)
{
GLint maxVaryings = 0;
glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
VaryingTestBase(0, maxVaryings / 2 + 1, 0, 0, 0, 0, 0, maxVaryings / 2, false, false, false,
false);
}
} // anonymous namespace
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
......@@ -2534,3 +2606,5 @@ ANGLE_INSTANTIATE_TEST(GLSLTest,
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(GLSLTest_ES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(WebGLGLSLTest, ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES());
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment