Add explicit stride options to I420BufferPool.

Also fix tests that relied on memory allocation behaviors. These only
worked by chance in the past because the allocated sizes of planes
changed enough to put them in a different location in memory. But there's
no easy/valid way to ensure memory *wasn't* re-used, and the test doesn't
really care anyways (if I420BufferPool could re-use the buffer object but
change the resolution/stride, it'd still be fine).

Bug: None
Change-Id: I28135d58d23f194a0142e5a037fa9d315af6b1c8
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/130821
Commit-Queue: Noah Richards <noahric@chromium.org>
Reviewed-by: Magnus Flodman <mflodman@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27551}
This commit is contained in:
Noah Richards 2019-04-01 10:14:47 -07:00 committed by Commit Bot
parent ae68ea0008
commit 408a3c63d3
3 changed files with 71 additions and 11 deletions

View File

@ -31,14 +31,27 @@ void I420BufferPool::Release() {
rtc::scoped_refptr<I420Buffer> I420BufferPool::CreateBuffer(int width, rtc::scoped_refptr<I420Buffer> I420BufferPool::CreateBuffer(int width,
int height) { int height) {
// Default stride_y is width, default uv stride is width / 2 (rounding up).
return CreateBuffer(width, height, width, (width + 1) / 2, (width + 1) / 2);
}
rtc::scoped_refptr<I420Buffer> I420BufferPool::CreateBuffer(int width,
int height,
int stride_y,
int stride_u,
int stride_v) {
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
// Release buffers with wrong resolution. // Release buffers with wrong resolution.
for (auto it = buffers_.begin(); it != buffers_.end();) { for (auto it = buffers_.begin(); it != buffers_.end();) {
if ((*it)->width() != width || (*it)->height() != height) const auto& buffer = *it;
if (buffer->width() != width || buffer->height() != height ||
buffer->StrideY() != stride_y || buffer->StrideU() != stride_u ||
buffer->StrideV() != stride_v) {
it = buffers_.erase(it); it = buffers_.erase(it);
else } else {
++it; ++it;
} }
}
// Look for a free buffer. // Look for a free buffer.
for (const rtc::scoped_refptr<PooledI420Buffer>& buffer : buffers_) { for (const rtc::scoped_refptr<PooledI420Buffer>& buffer : buffers_) {
// If the buffer is in use, the ref count will be >= 2, one from the list we // If the buffer is in use, the ref count will be >= 2, one from the list we
@ -53,7 +66,7 @@ rtc::scoped_refptr<I420Buffer> I420BufferPool::CreateBuffer(int width,
return nullptr; return nullptr;
// Allocate new buffer. // Allocate new buffer.
rtc::scoped_refptr<PooledI420Buffer> buffer = rtc::scoped_refptr<PooledI420Buffer> buffer =
new PooledI420Buffer(width, height); new PooledI420Buffer(width, height, stride_y, stride_u, stride_v);
if (zero_initialize_) if (zero_initialize_)
buffer->InitializeData(); buffer->InitializeData();
buffers_.push_back(buffer); buffers_.push_back(buffer);

View File

@ -21,7 +21,7 @@ namespace webrtc {
TEST(TestI420BufferPool, SimpleFrameReuse) { TEST(TestI420BufferPool, SimpleFrameReuse) {
I420BufferPool pool; I420BufferPool pool;
rtc::scoped_refptr<I420BufferInterface> buffer = pool.CreateBuffer(16, 16); auto buffer = pool.CreateBuffer(16, 16);
EXPECT_EQ(16, buffer->width()); EXPECT_EQ(16, buffer->width());
EXPECT_EQ(16, buffer->height()); EXPECT_EQ(16, buffer->height());
// Extract non-refcounted pointers for testing. // Extract non-refcounted pointers for testing.
@ -35,24 +35,63 @@ TEST(TestI420BufferPool, SimpleFrameReuse) {
EXPECT_EQ(y_ptr, buffer->DataY()); EXPECT_EQ(y_ptr, buffer->DataY());
EXPECT_EQ(u_ptr, buffer->DataU()); EXPECT_EQ(u_ptr, buffer->DataU());
EXPECT_EQ(v_ptr, buffer->DataV()); EXPECT_EQ(v_ptr, buffer->DataV());
EXPECT_EQ(16, buffer->width());
EXPECT_EQ(16, buffer->height());
} }
TEST(TestI420BufferPool, FailToReuse) { TEST(TestI420BufferPool, FrameReuseWithDefaultThenExplicitStride) {
I420BufferPool pool; I420BufferPool pool;
rtc::scoped_refptr<I420BufferInterface> buffer = pool.CreateBuffer(16, 16); auto buffer = pool.CreateBuffer(15, 16);
EXPECT_EQ(15, buffer->width());
EXPECT_EQ(16, buffer->height());
// The default Y stride is width and UV stride is halfwidth (rounded up).
ASSERT_EQ(15, buffer->StrideY());
ASSERT_EQ(8, buffer->StrideU());
ASSERT_EQ(8, buffer->StrideV());
// Extract non-refcounted pointers for testing. // Extract non-refcounted pointers for testing.
const uint8_t* y_ptr = buffer->DataY();
const uint8_t* u_ptr = buffer->DataU(); const uint8_t* u_ptr = buffer->DataU();
const uint8_t* v_ptr = buffer->DataV(); const uint8_t* v_ptr = buffer->DataV();
// Release buffer so that it is returned to the pool. // Release buffer so that it is returned to the pool.
buffer = nullptr; buffer = nullptr;
// Check that the memory is resued with explicit strides if they match the
// assumed default above.
buffer = pool.CreateBuffer(15, 16, 15, 8, 8);
EXPECT_EQ(y_ptr, buffer->DataY());
EXPECT_EQ(u_ptr, buffer->DataU());
EXPECT_EQ(v_ptr, buffer->DataV());
EXPECT_EQ(15, buffer->width());
EXPECT_EQ(16, buffer->height());
EXPECT_EQ(15, buffer->StrideY());
EXPECT_EQ(8, buffer->StrideU());
EXPECT_EQ(8, buffer->StrideV());
}
TEST(TestI420BufferPool, FailToReuseWrongSize) {
// Set max frames to 1, just to make sure the first buffer is being released.
I420BufferPool pool(/*zero_initialize=*/false, 1);
auto buffer = pool.CreateBuffer(16, 16);
EXPECT_EQ(16, buffer->width());
EXPECT_EQ(16, buffer->height());
// Release buffer so that it is returned to the pool.
buffer = nullptr;
// Check that the pool doesn't try to reuse buffers of incorrect size. // Check that the pool doesn't try to reuse buffers of incorrect size.
buffer = pool.CreateBuffer(32, 16); buffer = pool.CreateBuffer(32, 16);
ASSERT_TRUE(buffer);
EXPECT_EQ(32, buffer->width()); EXPECT_EQ(32, buffer->width());
EXPECT_EQ(16, buffer->height()); EXPECT_EQ(16, buffer->height());
EXPECT_NE(u_ptr, buffer->DataU()); }
EXPECT_NE(v_ptr, buffer->DataV());
TEST(TestI420BufferPool, FailToReuseWrongStride) {
// Set max frames to 1, just to make sure the first buffer is being released.
I420BufferPool pool(/*zero_initialize=*/false, 1);
auto buffer = pool.CreateBuffer(32, 32, 32, 16, 16);
// Make sure the stride was read correctly, for the rest of the test.
ASSERT_EQ(16, buffer->StrideU());
ASSERT_EQ(16, buffer->StrideV());
buffer = pool.CreateBuffer(32, 32, 32, 20, 20);
ASSERT_TRUE(buffer);
EXPECT_EQ(32, buffer->StrideY());
EXPECT_EQ(20, buffer->StrideU());
EXPECT_EQ(20, buffer->StrideV());
} }
TEST(TestI420BufferPool, FrameValidAfterPoolDestruction) { TEST(TestI420BufferPool, FrameValidAfterPoolDestruction) {
@ -69,7 +108,7 @@ TEST(TestI420BufferPool, FrameValidAfterPoolDestruction) {
TEST(TestI420BufferPool, MaxNumberOfBuffers) { TEST(TestI420BufferPool, MaxNumberOfBuffers) {
I420BufferPool pool(false, 1); I420BufferPool pool(false, 1);
rtc::scoped_refptr<I420BufferInterface> buffer1 = pool.CreateBuffer(16, 16); auto buffer1 = pool.CreateBuffer(16, 16);
EXPECT_NE(nullptr, buffer1.get()); EXPECT_NE(nullptr, buffer1.get());
EXPECT_EQ(nullptr, pool.CreateBuffer(16, 16).get()); EXPECT_EQ(nullptr, pool.CreateBuffer(16, 16).get());
} }

View File

@ -39,6 +39,14 @@ class I420BufferPool {
// and there are less than |max_number_of_buffers| pending, a buffer is // and there are less than |max_number_of_buffers| pending, a buffer is
// created. Returns null otherwise. // created. Returns null otherwise.
rtc::scoped_refptr<I420Buffer> CreateBuffer(int width, int height); rtc::scoped_refptr<I420Buffer> CreateBuffer(int width, int height);
// Returns a buffer from the pool with the explicitly specified stride.
rtc::scoped_refptr<I420Buffer> CreateBuffer(int width,
int height,
int stride_y,
int stride_u,
int stride_v);
// Clears buffers_ and detaches the thread checker so that it can be reused // Clears buffers_ and detaches the thread checker so that it can be reused
// later from another thread. // later from another thread.
void Release(); void Release();