Skip to content

Allocate exact-sized interleaved (strided) buffers#10017

Open
mhansen wants to merge 3 commits into
google:mainfrom
mhansen:exact-size
Open

Allocate exact-sized interleaved (strided) buffers#10017
mhansen wants to merge 3 commits into
google:mainfrom
mhansen:exact-size

Conversation

@mhansen
Copy link
Copy Markdown
Contributor

@mhansen mhansen commented May 19, 2026

Fixes #10009

@mhansen mhansen changed the title Allocate exact-sized strided buffers Allocate exact-sized interleaved (strided) buffers May 19, 2026
@show50726 show50726 added the internal Issue/PR does not affect clients label May 19, 2026
@mhansen mhansen force-pushed the exact-size branch 6 times, most recently from a35232c to eac2a19 Compare May 19, 2026 03:24
@pixelflinger
Copy link
Copy Markdown
Collaborator

@mhansen the unit tests seem to fail under ASAN.

@mhansen
Copy link
Copy Markdown
Contributor Author

mhansen commented May 19, 2026

Whoops, sorry for the noise. I'll look at the tests tomorrow.

const uint8_t slot = mAttributes[attributeIndex].buffer;
const size_t end = offset + mVertexCount * stride;
const size_t elementSize = Driver::getElementTypeSize(mAttributes[attributeIndex].type);
const size_t end = offset + (mVertexCount - 1) * stride + elementSize;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has an underflow issue. e.g., if mVertexCount == 0, mVertexCount - 1 will underflow for the type uint32_t.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On #10009 I asked about this:

I see there's a check that mVertexCount > 0 here:

FILAMENT_CHECK_PRECONDITION(mImpl->mVertexCount > 0) << "vertexCount cannot be 0";
. Can we rely on that? Or is it stripped out in some builds?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah we can rely on it then. Could you add comments about why it's safe?

const uint8_t slot = mAttributes[attributeIndex].buffer;
const size_t end = offset + mVertexCount * stride;
const size_t elementSize = Driver::getElementTypeSize(mAttributes[attributeIndex].type);
const size_t end = offset + (mVertexCount - 1) * stride + elementSize;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The correct calculation is mVertexCount * stride.

Indeed the existing code was wrong because it added the "offset" to the size calculation, the offset should be irrelevant. If we compare this to a std::vector<Foo>, stride plays the role of sizeof(Foo) and mVertexCount plays the role of .size(). offset plays the role of offsetof(Foo, field), it shouldn't be part of the size calculation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

D'oh. You're right, I completely bamboozled myself here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The simplification is offset + elementSize = stride, so offset + (mVertexCount - 1) * stride + elementSize collapses to stride + (mVertexCount - 1) * stride collapses to mVertexCount * stride. Sorry, I did this in an accretive not simplififying way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, no, I remember why I did this - I was worried about if attributes are struct-of-array. While in this case (array of struct), mVertexCount * stride is correct, filament also seems to support vertices that are offset, e.g. here we have position at bytes 0-23 and color at bytes 23-35:

app.vb = VertexBuffer::Builder().vertexCount(3).bufferCount(1)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 8)
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4, 24, 4)
.normalized(VertexAttribute::COLOR).build(*engine);

If we used mVertexCount * stride, this would ignore colour's starting offset of 24. And the max-sized attribute would be computed as 24, so we'd allocate a 24-byte buffer.

I've added a test demonstrating this. And also another test for what happens with 32-byte-padded rows (ensuring that we allocate for the padding too)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch! I completely agree.

Comment thread filament/test/test_VertexBuffer.cpp
Comment thread filament/test/test_VertexBuffer.cpp Outdated
mhansen added 2 commits May 20, 2026 11:42
This demonstrates the issue: oversized buffer. In the next commit, we
will fix this.

Towards google#10009
const uint8_t slot = mAttributes[attributeIndex].buffer;
const size_t end = offset + mVertexCount * stride;
const size_t elementSize = Driver::getElementTypeSize(mAttributes[attributeIndex].type);
const size_t end = offset + (mVertexCount - 1) * stride + elementSize;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch! I completely agree.

const size_t end = offset + mVertexCount * stride;
const size_t elementSize = Driver::getElementTypeSize(mAttributes[attributeIndex].type);
const size_t end = offset + (mVertexCount - 1) * stride + elementSize;
const size_t rounded = ((end + stride - 1) / stride) * stride;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes perfect, this works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal Issue/PR does not affect clients

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenGL backend: slightly over-allocates interleaved buffer size by adding full stride to last element offset

4 participants