Commit fe3a9ffb by Tim Van Patten Committed by Commit Bot

Support linking PPOs without attached Shaders

The application "Command & Conquer: Rivals" uses the following pattern: // Create and link VS, save binary data glCreateProgram() glAttachShader() glLinkProgram() glGetProgramBinary() // Create and link FS, save binary data glCreateProgram() glAttachShader() glLinkProgram() glGetProgramBinary() ... // Create VS, load binary data glCreateProgram() glProgramBinary() // Create FS, load binary data glCreateProgram() glProgramBinary() ... glUseProgramStages(GL_VERTEX_SHADER_BIT) glUseProgramStages(GL_FRAGMENT_SHADER_BIT) glBindProgramPipeline() Later, when issuing the draw command that uses the PPO, the PPO must be linked with the Programs that were loaded via binary data. Those Programs don't have any Shaders attached, just linked. This CL adds support for linking Programs without attached Shaders. Bug: b/182409935 Test: ProgramPipelineTest31.ProgramBinary Change-Id: Ic5a4776e1374322665f45fbbcbf955838d093d02 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2770685 Commit-Queue: Tim Van Patten <timvp@google.com> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 86a86a7d
......@@ -1630,7 +1630,7 @@ angle::Result Program::linkImpl(const Context *context)
InitUniformBlockLinker(mState, &resources.uniformBlockLinker);
InitShaderStorageBlockLinker(mState, &resources.shaderStorageBlockLinker);
mergedVaryings = GetMergedVaryingsFromShaders(*this);
mergedVaryings = GetMergedVaryingsFromShaders(*this, getExecutable());
if (!mState.mExecutable->linkMergedVaryings(context, *this, mergedVaryings,
mState.mTransformFeedbackVaryingNames,
isSeparable(), &resources.varyingPacking))
......
......@@ -899,17 +899,22 @@ bool ProgramExecutable::linkMergedVaryings(
}
// Build active shader stage map.
ShaderBitSet attachedShadersMask;
ShaderBitSet activeShadersMask;
for (ShaderType shaderType : kAllGraphicsShaderTypes)
{
if (programOrPipeline.getAttachedShader(shaderType))
// - Check for attached shaders to handle the case of a Program linking the currently
// attached shaders.
// - Check for linked shaders to handle the case of a PPO linking separable programs before
// drawing.
if (programOrPipeline.getAttachedShader(shaderType) ||
getLinkedShaderStages().test(shaderType))
{
attachedShadersMask[shaderType] = true;
activeShadersMask[shaderType] = true;
}
}
if (!varyingPacking->collectAndPackUserVaryings(mInfoLog, context->getCaps(), packMode,
attachedShadersMask, mergedVaryings,
activeShadersMask, mergedVaryings,
transformFeedbackVaryingNames, isSeparable))
{
return false;
......
......@@ -534,7 +534,7 @@ angle::Result ProgramPipeline::link(const Context *context)
return angle::Result::Stop;
}
mergedVaryings = GetMergedVaryingsFromShaders(*this);
mergedVaryings = GetMergedVaryingsFromShaders(*this, getExecutable());
// If separable program objects are in use, the set of attributes captured is taken
// from the program object active on the last vertex processing stage.
Program *tfProgram = mState.mPrograms[ShaderType::Geometry];
......
......@@ -13,6 +13,7 @@
#include "common/utilities.h"
#include "libANGLE/Program.h"
#include "libANGLE/ProgramExecutable.h"
#include "libANGLE/Shader.h"
namespace gl
......@@ -988,17 +989,18 @@ const VaryingPacking &ProgramVaryingPacking::getOutputPacking(ShaderType frontSh
bool ProgramVaryingPacking::collectAndPackUserVaryings(InfoLog &infoLog,
const Caps &caps,
PackMode packMode,
const ShaderBitSet &attachedShadersMask,
const ShaderBitSet &activeShadersMask,
const ProgramMergedVaryings &mergedVaryings,
const std::vector<std::string> &tfVaryings,
bool isSeparableProgram)
{
mBackToFrontStageMap.fill(ShaderType::InvalidEnum);
ShaderBitSet attachedShaders = attachedShadersMask;
ShaderBitSet activeShaders = activeShadersMask;
ShaderType frontShaderStage = attachedShaders.first();
attachedShaders[frontShaderStage] = false;
ASSERT(activeShaders.any());
ShaderType frontShaderStage = activeShaders.first();
activeShaders[frontShaderStage] = false;
// Special case for start-after-vertex.
if (frontShaderStage != ShaderType::Vertex)
......@@ -1019,7 +1021,7 @@ bool ProgramVaryingPacking::collectAndPackUserVaryings(InfoLog &infoLog,
}
// Process input/output shader pairs.
for (ShaderType backShaderStage : attachedShaders)
for (ShaderType backShaderStage : activeShaders)
{
GLint maxVaryingVectors;
if (frontShaderStage == ShaderType::Vertex && backShaderStage == ShaderType::Fragment)
......@@ -1066,56 +1068,63 @@ bool ProgramVaryingPacking::collectAndPackUserVaryings(InfoLog &infoLog,
return true;
}
ProgramMergedVaryings GetMergedVaryingsFromShaders(const HasAttachedShaders &programOrPipeline)
ProgramMergedVaryings GetMergedVaryingsFromShaders(const HasAttachedShaders &programOrPipeline,
const ProgramExecutable &programExecutable)
{
Shader *frontShader = nullptr;
ShaderType frontShaderType = ShaderType::InvalidEnum;
ProgramMergedVaryings merged;
for (ShaderType shaderType : kAllGraphicsShaderTypes)
for (ShaderType backShaderType : kAllGraphicsShaderTypes)
{
Shader *backShader = programOrPipeline.getAttachedShader(shaderType);
if (!backShader)
Shader *backShader = programOrPipeline.getAttachedShader(backShaderType);
if (!backShader && !programExecutable.hasLinkedShaderStage(backShaderType))
{
continue;
}
ASSERT(backShader->getType() != ShaderType::Compute);
const std::vector<sh::ShaderVariable> &backShaderOutputVaryings =
backShader ? backShader->getOutputVaryings()
: programExecutable.getLinkedOutputVaryings(backShaderType);
const std::vector<sh::ShaderVariable> &backShaderInputVaryings =
backShader ? backShader->getInputVaryings()
: programExecutable.getLinkedInputVaryings(backShaderType);
// Add outputs. These are always unmatched since we walk shader stages sequentially.
for (const sh::ShaderVariable &frontVarying : backShader->getOutputVaryings())
for (const sh::ShaderVariable &frontVarying : backShaderOutputVaryings)
{
ProgramVaryingRef ref;
ref.frontShader = &frontVarying;
ref.frontShaderStage = backShader->getType();
ref.frontShaderStage = backShaderType;
merged.push_back(ref);
}
if (!frontShader)
if (frontShaderType == ShaderType::InvalidEnum)
{
// If this is our first shader stage, and not a VS, we might have unmatched inputs.
for (const sh::ShaderVariable &backVarying : backShader->getInputVaryings())
for (const sh::ShaderVariable &backVarying : backShaderInputVaryings)
{
ProgramVaryingRef ref;
ref.backShader = &backVarying;
ref.backShaderStage = backShader->getType();
ref.backShaderStage = backShaderType;
merged.push_back(ref);
}
}
else
{
// Match inputs with the prior shader stage outputs.
for (const sh::ShaderVariable &backVarying : backShader->getInputVaryings())
for (const sh::ShaderVariable &backVarying : backShaderInputVaryings)
{
bool found = false;
for (ProgramVaryingRef &ref : merged)
{
if (ref.frontShader && ref.frontShaderStage == frontShader->getType() &&
if (ref.frontShader && ref.frontShaderStage == frontShaderType &&
InterfaceVariablesMatch(*ref.frontShader, backVarying))
{
ASSERT(ref.backShader == nullptr);
ref.backShader = &backVarying;
ref.backShaderStage = backShader->getType();
ref.backShaderStage = backShaderType;
found = true;
break;
}
......@@ -1126,14 +1135,14 @@ ProgramMergedVaryings GetMergedVaryingsFromShaders(const HasAttachedShaders &pro
{
ProgramVaryingRef ref;
ref.backShader = &backVarying;
ref.backShaderStage = backShader->getType();
ref.backShaderStage = backShaderType;
merged.push_back(ref);
}
}
}
// Save the current back shader to use as the next front shader.
frontShader = backShader;
frontShaderType = backShaderType;
}
return merged;
......
......@@ -24,6 +24,7 @@ namespace gl
{
class HasAttachedShaders;
class InfoLog;
class ProgramExecutable;
struct Caps;
struct ProgramVaryingRef;
......@@ -308,7 +309,7 @@ class ProgramVaryingPacking final : angle::NonCopyable
ANGLE_NO_DISCARD bool collectAndPackUserVaryings(InfoLog &infoLog,
const Caps &caps,
PackMode packMode,
const ShaderBitSet &attachedShadersMask,
const ShaderBitSet &activeShadersMask,
const ProgramMergedVaryings &mergedVaryings,
const std::vector<std::string> &tfVaryings,
bool isSeparableProgram);
......@@ -322,7 +323,8 @@ class ProgramVaryingPacking final : angle::NonCopyable
};
// Takes an abstract handle to a program or pipeline.
ProgramMergedVaryings GetMergedVaryingsFromShaders(const HasAttachedShaders &programOrPipeline);
ProgramMergedVaryings GetMergedVaryingsFromShaders(const HasAttachedShaders &programOrPipeline,
const ProgramExecutable &programExecutable);
} // namespace gl
#endif // LIBANGLE_VARYINGPACKING_H_
......@@ -751,6 +751,98 @@ TEST_P(ProgramPipelineTest31, ModifyAndRelinkShader)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
// Test that a PPO can be used when the attached shader programs are created with glProgramBinary().
// This validates the necessary programs' information is serialized/deserialized so they can be
// linked by the PPO during glDrawArrays.
TEST_P(ProgramPipelineTest31, ProgramBinary)
{
ANGLE_SKIP_TEST_IF(!IsVulkan());
const GLchar *vertString = R"(#version 310 es
precision highp float;
in vec4 a_position;
out vec2 texCoord;
void main()
{
gl_Position = a_position;
texCoord = vec2(a_position.x, a_position.y) * 0.5 + vec2(0.5);
})";
const GLchar *fragString = R"(#version 310 es
precision highp float;
in vec2 texCoord;
uniform sampler2D tex;
out vec4 my_FragColor;
void main()
{
my_FragColor = texture(tex, texCoord);
})";
std::array<GLColor, 4> colors = {
{GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}};
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
mVertProg = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &vertString);
ASSERT_NE(mVertProg, 0u);
mFragProg = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragString);
ASSERT_NE(mFragProg, 0u);
// Save the VS program binary out
std::vector<uint8_t> vsBinary(0);
GLint vsProgramLength = 0;
GLint vsWrittenLength = 0;
GLenum vsBinaryFormat = 0;
glGetProgramiv(mVertProg, GL_PROGRAM_BINARY_LENGTH, &vsProgramLength);
ASSERT_GL_NO_ERROR();
vsBinary.resize(vsProgramLength);
glGetProgramBinary(mVertProg, vsProgramLength, &vsWrittenLength, &vsBinaryFormat,
vsBinary.data());
ASSERT_GL_NO_ERROR();
// Save the FS program binary out
std::vector<uint8_t> fsBinary(0);
GLint fsProgramLength = 0;
GLint fsWrittenLength = 0;
GLenum fsBinaryFormat = 0;
glGetProgramiv(mFragProg, GL_PROGRAM_BINARY_LENGTH, &fsProgramLength);
ASSERT_GL_NO_ERROR();
fsBinary.resize(fsProgramLength);
glGetProgramBinary(mFragProg, fsProgramLength, &fsWrittenLength, &fsBinaryFormat,
fsBinary.data());
ASSERT_GL_NO_ERROR();
mVertProg = glCreateProgram();
glProgramBinary(mVertProg, vsBinaryFormat, vsBinary.data(), vsWrittenLength);
mFragProg = glCreateProgram();
glProgramBinary(mFragProg, fsBinaryFormat, fsBinary.data(), fsWrittenLength);
// Generate a program pipeline and attach the programs to their respective stages
glGenProgramPipelines(1, &mPipeline);
EXPECT_GL_NO_ERROR();
glUseProgramStages(mPipeline, GL_VERTEX_SHADER_BIT, mVertProg);
EXPECT_GL_NO_ERROR();
glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, mFragProg);
EXPECT_GL_NO_ERROR();
glBindProgramPipeline(mPipeline);
EXPECT_GL_NO_ERROR();
drawQuadWithPPO("a_position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
int w = getWindowWidth() - 2;
int h = getWindowHeight() - 2;
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::yellow);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ProgramPipelineTest);
ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(ProgramPipelineTest);
......
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