mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
[Fix] (GL): resolve attribute array elements in glGetAttribLocation
This commit is contained in:
@@ -26,6 +26,48 @@ void main() {}
|
||||
namespace MobileGL::MG_State::GLState {
|
||||
static std::atomic<Uint64> s_nextProgramLifetimeId = 1;
|
||||
|
||||
Int ProgramObject::GetAttributeArrayElementLocation(const String& name) {
|
||||
if (name.empty()) return -1;
|
||||
if (name.back() != ']') return GetAttributeArrayElementLocation(name + "[0]");
|
||||
// A strict decimal subscript: no white space, no sign, no leading zeros.
|
||||
const SizeT bracket = name.rfind('[');
|
||||
if (bracket == String::npos || bracket == 0) return -1;
|
||||
const SizeT first = bracket + 1;
|
||||
const SizeT last = name.size() - 1;
|
||||
if (first >= last || (name[first] == '0' && last - first > 1)) return -1;
|
||||
Int element = 0;
|
||||
for (SizeT i = first; i < last; ++i) {
|
||||
if (name[i] < '0' || name[i] > '9') return -1;
|
||||
element = element * 10 + (name[i] - '0');
|
||||
if (element > 0xFFFF) return -1;
|
||||
}
|
||||
const String base = name.substr(0, bracket);
|
||||
|
||||
const auto& artifacts = Artifacts();
|
||||
const auto& attribs = artifacts.attribs;
|
||||
for (const auto& input : artifacts.pipeInputReflection) {
|
||||
// Reflection may spell an array with or without its "[0]".
|
||||
if (input.name != base && input.name != base + "[0]") continue;
|
||||
if (!input.type.isArray || element >= input.arraySize) return -1;
|
||||
// The link expanded the array over consecutive locations, every one carrying the
|
||||
// reflected name (ProgramLinkTask's attribute table), so its span is the run of them.
|
||||
// An exact match for the reflected name was already tried by GetAttributeLocation, so
|
||||
// this is reached for element 0 only through the plain-name spelling.
|
||||
const auto it = std::find(attribs.begin(), attribs.end(), input.name);
|
||||
if (it == attribs.end()) return -1;
|
||||
const Int baseLocation = static_cast<Int>(std::distance(attribs.begin(), it));
|
||||
Int span = 0;
|
||||
while (baseLocation + span < static_cast<Int>(attribs.size()) &&
|
||||
attribs[static_cast<SizeT>(baseLocation + span)] == input.name) {
|
||||
++span;
|
||||
}
|
||||
const Int locationsPerElement = std::max<Int>(1, span / std::max<Int>(input.arraySize, 1));
|
||||
const Int location = baseLocation + element * locationsPerElement;
|
||||
return location < baseLocation + span ? location : -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Uint64 ProgramObject::AllocateLifetimeId() {
|
||||
return s_nextProgramLifetimeId.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
@@ -663,8 +663,15 @@ namespace MobileGL::MG_State::GLState {
|
||||
|
||||
Int GetAttributeLocation(const String& name) {
|
||||
const auto it = std::find(Artifacts().attribs.begin(), Artifacts().attribs.end(), name);
|
||||
return (it == Artifacts().attribs.end()) ? -1 : (Int)std::distance(Artifacts().attribs.begin(), it);
|
||||
return (it == Artifacts().attribs.end()) ? GetAttributeArrayElementLocation(name)
|
||||
: (Int)std::distance(Artifacts().attribs.begin(), it);
|
||||
}
|
||||
// An attribute ARRAY by its plain name or as "name[N]" (GL 4.6 core 7.3.1.1 / 11.1.1).
|
||||
// Reflection records the array as "name[0]", so the plain name answers element 0, and
|
||||
// "name[N]" answers the array's base location plus N times the locations one element
|
||||
// occupies (a matrix takes one per column). -1 past the end, for a subscript on a
|
||||
// non-array, and for a subscript the resource-name grammar rejects ("a[01]", "a[ 1]").
|
||||
Int GetAttributeArrayElementLocation(const String& name);
|
||||
Uint32 GetActiveAttributeLocationMask() const {
|
||||
Uint32 mask = 0;
|
||||
const SizeT count = std::min<SizeT>(Artifacts().attribs.size(), 32);
|
||||
|
||||
@@ -951,6 +951,45 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
// glGetAttribLocation("name[N]") on an attribute array answers element N's location
|
||||
// (KHR-GL4x.cull_distance.functional_* feed `in float culldistance_data[N]` element by element).
|
||||
TEST_F(ProgramInterfaceTest, AttribLocationResolvesArrayElements) {
|
||||
const char* vs = R"(#version 430
|
||||
in vec4 position;
|
||||
in float data[4];
|
||||
in mat2 m[2];
|
||||
out float v;
|
||||
void main(void) {
|
||||
v = data[0] + data[1] + data[2] + data[3] + m[0][0][0] + m[1][1][1];
|
||||
gl_Position = position;
|
||||
}
|
||||
)";
|
||||
const GLuint p = MakeProgram(vs, kSimpleFs);
|
||||
LinkProgram(p);
|
||||
ExpectLinked(p);
|
||||
ClearErrors();
|
||||
|
||||
const GLint data = GetAttribLocation(p, "data");
|
||||
ASSERT_GE(data, 0);
|
||||
EXPECT_EQ(GetAttribLocation(p, "data[0]"), data);
|
||||
EXPECT_EQ(GetAttribLocation(p, "data[1]"), data + 1);
|
||||
EXPECT_EQ(GetAttribLocation(p, "data[3]"), data + 3);
|
||||
EXPECT_EQ(GetAttribLocation(p, "data[4]"), -1);
|
||||
EXPECT_EQ(GetAttribLocation(p, "data[01]"), -1);
|
||||
EXPECT_EQ(GetAttribLocation(p, "data[ 1]"), -1);
|
||||
|
||||
// A matrix element occupies one location per column.
|
||||
const GLint m = GetAttribLocation(p, "m");
|
||||
ASSERT_GE(m, 0);
|
||||
EXPECT_EQ(GetAttribLocation(p, "m[1]"), m + 2);
|
||||
EXPECT_EQ(GetAttribLocation(p, "m[2]"), -1);
|
||||
|
||||
// A subscript on a non-array names nothing.
|
||||
ASSERT_GE(GetAttribLocation(p, "position"), 0);
|
||||
EXPECT_EQ(GetAttribLocation(p, "position[0]"), -1);
|
||||
EXPECT_EQ(TakeError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
// The same rule for the tessellation stages, whose limits come from the backend like the
|
||||
// others (they used to be hard-coded zeros). Sized off the advertised limit for the same
|
||||
// reason as above: one image over it must fail and name the limit, and exactly at it must
|
||||
|
||||
Reference in New Issue
Block a user