Front-end support for xfb capture of I/O block members

Validation and generation of transform feedback varyings that specify an
I/O block member are implemented in this change.  The GL backend is able
to pass the added tests.

Bug: angleproject:3606
Change-Id: I66d02bed8ca9161555d0d1e7a32ae9ef4d9e813f
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2599952
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Tim Van Patten <timvp@google.com>
This commit is contained in:
Shahbaz Youssefi 2020-12-22 23:38:42 -05:00 коммит произвёл Commit Bot
Родитель c500ee031d
Коммит 8065aa82d3
6 изменённых файлов: 248 добавлений и 11 удалений

Просмотреть файл

@ -306,16 +306,28 @@ const sh::ShaderVariable *ShaderVariable::findField(const std::string &fullName,
return nullptr;
}
size_t pos = fullName.find_first_of(".");
std::string topName, fieldName;
if (pos == std::string::npos)
{
return nullptr;
// If this is a shader I/O block without an instance name, return the field given only the
// field name.
if (!isShaderIOBlock || !name.empty())
{
return nullptr;
}
fieldName = fullName;
}
std::string topName = fullName.substr(0, pos);
if (topName != name)
else
{
return nullptr;
std::string baseName = isShaderIOBlock ? structName : name;
topName = fullName.substr(0, pos);
if (topName != baseName)
{
return nullptr;
}
fieldName = fullName.substr(pos + 1);
}
std::string fieldName = fullName.substr(pos + 1);
if (fieldName.empty())
{
return nullptr;

Просмотреть файл

@ -628,9 +628,8 @@ const std::vector<sh::ShaderVariable> &Shader::getActiveOutputVariables()
std::string Shader::getTransformFeedbackVaryingMappedName(const std::string &tfVaryingName)
{
// TODO(jiawei.shao@intel.com): support transform feedback on geometry shader.
ASSERT(mState.getShaderType() == ShaderType::Vertex ||
mState.getShaderType() == ShaderType::Geometry);
ASSERT(mState.getShaderType() != ShaderType::Fragment &&
mState.getShaderType() != ShaderType::Compute);
const auto &varyings = getOutputVaryings();
auto bracketPos = tfVaryingName.find("[");
if (bracketPos != std::string::npos)
@ -658,8 +657,21 @@ std::string Shader::getTransformFeedbackVaryingMappedName(const std::string &tfV
{
GLuint fieldIndex = 0;
const auto *field = varying.findField(tfVaryingName, &fieldIndex);
ASSERT(field != nullptr && !field->isStruct() && !field->isArray());
return varying.mappedName + "." + field->mappedName;
if (field == nullptr)
{
continue;
}
ASSERT(field != nullptr && !field->isStruct() &&
(!field->isArray() || varying.isShaderIOBlock));
std::string mappedName;
// If it's an I/O block without an instance name, don't include the block name.
if (!varying.isShaderIOBlock || !varying.name.empty())
{
mappedName =
varying.isShaderIOBlock ? varying.mappedStructName : varying.mappedName;
mappedName += '.';
}
return mappedName + field->mappedName;
}
}
}

Просмотреть файл

@ -667,7 +667,7 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
const sh::ShaderVariable *field = input->findField(tfVarying, &fieldIndex);
if (field != nullptr)
{
ASSERT(!field->isStruct() && !field->isArray());
ASSERT(!field->isStruct() && (!field->isArray() || input->isShaderIOBlock));
packUserVaryingFieldTF(ref, *field, fieldIndex);
uniqueFullNames[ref.frontShaderStage].insert(tfVarying);

Просмотреть файл

@ -2800,6 +2800,213 @@ TEST_P(TransformFeedbackTestES32, PrimitivesWrittenAndGenerated)
}
}
// Verify that capture of I/O block fields works, both when the instance name is specified and when
// not. This test uses interleaved components.
TEST_P(TransformFeedbackTestES31, IOBlocksInterleaved)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks"));
// http://anglebug.com/5488
ANGLE_SKIP_TEST_IF(IsQualcomm() && IsOpenGLES());
// Not supported in Vulkan yet. http://anglebug.com/3606
ANGLE_SKIP_TEST_IF(IsVulkan());
constexpr char kVS[] = R"(#version 310 es
#extension GL_EXT_shader_io_blocks : require
out VSBlock1
{
vec4 a;
vec4 b[2];
} blockOut1;
out VSBlock2
{
vec4 c;
mat3 d;
vec4 e;
};
out vec4 looseVarying;
void main()
{
blockOut1.a = vec4(0.15, 0.18, 0.21, 0.24);
blockOut1.b[0] = vec4(0.27, 0.30, 0.33, 0.36);
blockOut1.b[1] = vec4(0.39, 0.42, 0.45, 0.48);
c = vec4(0.51, 0.54, 0.57, 0.6);
d = mat3(vec3(0.63, 0.66, 0.69), vec3(0.72, 0.75, 0.78), vec3(0.81, 0.84, 0.87));
e = vec4(0.9, 0.93, 0.96, 0.99);
looseVarying = vec4(0.25, 0.5, 0.75, 1.0);
})";
constexpr char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_io_blocks : require
precision mediump float;
layout(location = 0) out mediump vec4 color;
in VSBlock2
{
vec4 c;
mat3 d;
vec4 e;
};
void main()
{
color = vec4(c.x, d[0].y, e.z, 1.0);
})";
std::vector<std::string> tfVaryings = {"VSBlock1.b", "d", "looseVarying"};
constexpr size_t kCapturedVaryingsCount = 3;
constexpr std::array<size_t, kCapturedVaryingsCount> kCaptureSizes = {8, 9, 4};
const std::vector<float> kExpected[kCapturedVaryingsCount] = {
{0.27, 0.30, 0.33, 0.36, 0.39, 0.42, 0.45, 0.48},
{0.63, 0.66, 0.69, 0.72, 0.75, 0.78, 0.81, 0.84, 0.87},
{0.25, 0.5, 0.75, 1.0},
};
ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(program, kVS, kFS, tfVaryings, GL_INTERLEAVED_ATTRIBS);
EXPECT_GL_NO_ERROR();
GLTransformFeedback xfb;
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, xfb);
GLBuffer xfbBuffer;
size_t totalSize = 0;
for (size_t index = 0; index < kCapturedVaryingsCount; ++index)
{
totalSize += kCaptureSizes[index];
}
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfbBuffer);
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, totalSize * sizeof(float), nullptr, GL_STATIC_DRAW);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfbBuffer);
glUseProgram(program);
glBeginTransformFeedback(GL_POINTS);
glDrawArrays(GL_POINTS, 0, 1);
glEndTransformFeedback();
const float *bufferData = static_cast<float *>(glMapBufferRange(
GL_TRANSFORM_FEEDBACK_BUFFER, 0, totalSize * sizeof(float), GL_MAP_READ_BIT));
size_t currentOffset = 0;
for (size_t index = 0; index < kCapturedVaryingsCount; ++index)
{
for (size_t component = 0; component < kCaptureSizes[index]; ++component)
{
EXPECT_NEAR(bufferData[currentOffset + component], kExpected[index][component], 0.001f)
<< index << " " << component;
}
currentOffset += kCaptureSizes[index];
}
glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
}
// Verify that capture of I/O block fields works. This test uses separate components.
TEST_P(TransformFeedbackTestES31, IOBlocksSeparate)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks"));
// http://anglebug.com/5487
ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsOpenGL());
// http://anglebug.com/5488
ANGLE_SKIP_TEST_IF(IsQualcomm() && IsOpenGLES());
// Not supported in Vulkan yet. http://anglebug.com/3606
ANGLE_SKIP_TEST_IF(IsVulkan());
constexpr char kVS[] = R"(#version 310 es
#extension GL_EXT_shader_io_blocks : require
out VSBlock
{
float a;
vec2 b;
};
out float c;
void main()
{
a = 0.25;
b = vec2(0.5, 0.75);
c = 1.0;
})";
constexpr char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_io_blocks : require
precision mediump float;
layout(location = 0) out mediump vec4 color;
in VSBlock
{
float a;
vec2 b;
};
void main()
{
color = vec4(a, b, 1.0);
})";
std::vector<std::string> tfVaryings = {"a", "b", "c"};
constexpr size_t kCapturedVaryingsCount = 3;
constexpr std::array<size_t, kCapturedVaryingsCount> kCaptureSizes = {1, 2, 1};
const std::vector<float> kExpected[kCapturedVaryingsCount] = {
{0.25},
{0.5, 0.75},
{1.0},
};
ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(program, kVS, kFS, tfVaryings, GL_SEPARATE_ATTRIBS);
EXPECT_GL_NO_ERROR();
GLTransformFeedback xfb;
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, xfb);
std::array<GLBuffer, kCapturedVaryingsCount> xfbBuffers;
for (size_t index = 0; index < kCapturedVaryingsCount; ++index)
{
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfbBuffers[index]);
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, kCaptureSizes[index] * sizeof(float), nullptr,
GL_STATIC_DRAW);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, index, xfbBuffers[index]);
}
glUseProgram(program);
glBeginTransformFeedback(GL_POINTS);
glDrawArrays(GL_POINTS, 0, 1);
glEndTransformFeedback();
for (size_t index = 0; index < kCapturedVaryingsCount; ++index)
{
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfbBuffers[index]);
const float *bufferData = static_cast<float *>(
glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, kCaptureSizes[index] * sizeof(float),
GL_MAP_READ_BIT));
for (size_t component = 0; component < kCaptureSizes[index]; ++component)
{
EXPECT_NEAR(bufferData[component], kExpected[index][component], 0.001f)
<< index << " " << component;
}
glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
}
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES3(TransformFeedbackTest);

Просмотреть файл

@ -350,6 +350,11 @@ bool IsNVIDIA()
return HasSystemVendorID(kVendorID_NVIDIA);
}
bool IsQualcomm()
{
return IsNexus5X() || IsNexus9() || IsPixelXL() || IsPixel2() || IsPixel2XL();
}
bool IsConfigAllowlisted(const SystemInfo &systemInfo, const PlatformParameters &param)
{
VendorID vendorID =

Просмотреть файл

@ -44,6 +44,7 @@ bool IsIntel();
bool IsAMD();
bool IsARM();
bool IsNVIDIA();
bool IsQualcomm();
// GPU devices.
bool IsSwiftshaderDevice();