Commit 8065aa82 by Shahbaz Youssefi Committed by Commit Bot

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: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent c500ee03
...@@ -306,16 +306,28 @@ const sh::ShaderVariable *ShaderVariable::findField(const std::string &fullName, ...@@ -306,16 +306,28 @@ const sh::ShaderVariable *ShaderVariable::findField(const std::string &fullName,
return nullptr; return nullptr;
} }
size_t pos = fullName.find_first_of("."); size_t pos = fullName.find_first_of(".");
std::string topName, fieldName;
if (pos == std::string::npos) 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); else
if (topName != name)
{ {
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()) if (fieldName.empty())
{ {
return nullptr; return nullptr;
......
...@@ -628,9 +628,8 @@ const std::vector<sh::ShaderVariable> &Shader::getActiveOutputVariables() ...@@ -628,9 +628,8 @@ const std::vector<sh::ShaderVariable> &Shader::getActiveOutputVariables()
std::string Shader::getTransformFeedbackVaryingMappedName(const std::string &tfVaryingName) std::string Shader::getTransformFeedbackVaryingMappedName(const std::string &tfVaryingName)
{ {
// TODO(jiawei.shao@intel.com): support transform feedback on geometry shader. ASSERT(mState.getShaderType() != ShaderType::Fragment &&
ASSERT(mState.getShaderType() == ShaderType::Vertex || mState.getShaderType() != ShaderType::Compute);
mState.getShaderType() == ShaderType::Geometry);
const auto &varyings = getOutputVaryings(); const auto &varyings = getOutputVaryings();
auto bracketPos = tfVaryingName.find("["); auto bracketPos = tfVaryingName.find("[");
if (bracketPos != std::string::npos) if (bracketPos != std::string::npos)
...@@ -658,8 +657,21 @@ std::string Shader::getTransformFeedbackVaryingMappedName(const std::string &tfV ...@@ -658,8 +657,21 @@ std::string Shader::getTransformFeedbackVaryingMappedName(const std::string &tfV
{ {
GLuint fieldIndex = 0; GLuint fieldIndex = 0;
const auto *field = varying.findField(tfVaryingName, &fieldIndex); const auto *field = varying.findField(tfVaryingName, &fieldIndex);
ASSERT(field != nullptr && !field->isStruct() && !field->isArray()); if (field == nullptr)
return varying.mappedName + "." + field->mappedName; {
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, ...@@ -667,7 +667,7 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
const sh::ShaderVariable *field = input->findField(tfVarying, &fieldIndex); const sh::ShaderVariable *field = input->findField(tfVarying, &fieldIndex);
if (field != nullptr) if (field != nullptr)
{ {
ASSERT(!field->isStruct() && !field->isArray()); ASSERT(!field->isStruct() && (!field->isArray() || input->isShaderIOBlock));
packUserVaryingFieldTF(ref, *field, fieldIndex); packUserVaryingFieldTF(ref, *field, fieldIndex);
uniqueFullNames[ref.frontShaderStage].insert(tfVarying); uniqueFullNames[ref.frontShaderStage].insert(tfVarying);
......
...@@ -2800,6 +2800,213 @@ TEST_P(TransformFeedbackTestES32, PrimitivesWrittenAndGenerated) ...@@ -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 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. // tests should be run against.
ANGLE_INSTANTIATE_TEST_ES3(TransformFeedbackTest); ANGLE_INSTANTIATE_TEST_ES3(TransformFeedbackTest);
......
...@@ -350,6 +350,11 @@ bool IsNVIDIA() ...@@ -350,6 +350,11 @@ bool IsNVIDIA()
return HasSystemVendorID(kVendorID_NVIDIA); return HasSystemVendorID(kVendorID_NVIDIA);
} }
bool IsQualcomm()
{
return IsNexus5X() || IsNexus9() || IsPixelXL() || IsPixel2() || IsPixel2XL();
}
bool IsConfigAllowlisted(const SystemInfo &systemInfo, const PlatformParameters &param) bool IsConfigAllowlisted(const SystemInfo &systemInfo, const PlatformParameters &param)
{ {
VendorID vendorID = VendorID vendorID =
......
...@@ -44,6 +44,7 @@ bool IsIntel(); ...@@ -44,6 +44,7 @@ bool IsIntel();
bool IsAMD(); bool IsAMD();
bool IsARM(); bool IsARM();
bool IsNVIDIA(); bool IsNVIDIA();
bool IsQualcomm();
// GPU devices. // GPU devices.
bool IsSwiftshaderDevice(); bool IsSwiftshaderDevice();
......
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