blob: c7692d05b0798bb3a4fb69aaf3be4c2b67806763 [file] [log] [blame]
#include <private/dvr/buffer_hub_queue_producer.h>
#include <base/logging.h>
#include <gui/IProducerListener.h>
#include <gui/Surface.h>
#include <gtest/gtest.h>
namespace android {
namespace dvr {
namespace {
// Default dimensions before setDefaultBufferSize is called by the consumer.
constexpr uint32_t kDefaultWidth = 1;
constexpr uint32_t kDefaultHeight = 1;
// Default format before setDefaultBufferFormat is called by the consumer.
constexpr PixelFormat kDefaultFormat = HAL_PIXEL_FORMAT_RGBA_8888;
constexpr int kDefaultConsumerUsageBits = 0;
// Default transform hint before setTransformHint is called by the consumer.
constexpr uint32_t kDefaultTransformHint = 0;
constexpr int kTestApi = NATIVE_WINDOW_API_CPU;
constexpr int kTestApiOther = NATIVE_WINDOW_API_EGL;
constexpr int kTestApiInvalid = 0xDEADBEEF;
constexpr int kTestProducerUsageBits = 0;
constexpr bool kTestControlledByApp = true;
// Builder pattern to slightly vary *almost* correct input
// -- avoids copying and pasting
struct QueueBufferInputBuilder {
IGraphicBufferProducer::QueueBufferInput build() {
return IGraphicBufferProducer::QueueBufferInput(
mTimestamp, mIsAutoTimestamp, mDataSpace, mCrop, mScalingMode,
mTransform, mFence);
}
QueueBufferInputBuilder& setTimestamp(int64_t timestamp) {
this->mTimestamp = timestamp;
return *this;
}
QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) {
this->mIsAutoTimestamp = isAutoTimestamp;
return *this;
}
QueueBufferInputBuilder& setDataSpace(android_dataspace dataSpace) {
this->mDataSpace = dataSpace;
return *this;
}
QueueBufferInputBuilder& setCrop(Rect crop) {
this->mCrop = crop;
return *this;
}
QueueBufferInputBuilder& setScalingMode(int scalingMode) {
this->mScalingMode = scalingMode;
return *this;
}
QueueBufferInputBuilder& setTransform(uint32_t transform) {
this->mTransform = transform;
return *this;
}
QueueBufferInputBuilder& setFence(sp<Fence> fence) {
this->mFence = fence;
return *this;
}
private:
int64_t mTimestamp{1384888611};
bool mIsAutoTimestamp{false};
android_dataspace mDataSpace{HAL_DATASPACE_UNKNOWN};
Rect mCrop{Rect(kDefaultWidth, kDefaultHeight)};
int mScalingMode{0};
uint32_t mTransform{0};
sp<Fence> mFence{Fence::NO_FENCE};
};
// This is a test that covers our implementation of bufferhubqueue-based
// IGraphicBufferProducer.
class BufferHubQueueProducerTest : public ::testing::Test {
protected:
virtual void SetUp() {
const ::testing::TestInfo* const testInfo =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD_IF(TRACE, "Begin test: %s.%s", testInfo->test_case_name(),
testInfo->name());
mProducer = BufferHubQueueProducer::Create();
ASSERT_TRUE(mProducer != nullptr);
mSurface = new Surface(mProducer, true);
ASSERT_TRUE(mSurface != nullptr);
}
// Connect to a producer in a 'correct' fashion.
void ConnectProducer() {
IGraphicBufferProducer::QueueBufferOutput output;
// Can connect the first time.
ASSERT_EQ(NO_ERROR, mProducer->connect(kDummyListener, kTestApi,
kTestControlledByApp, &output));
}
// Dequeue a buffer in a 'correct' fashion.
// Precondition: Producer is connected.
void DequeueBuffer(int* outSlot) {
sp<Fence> fence;
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(outSlot, &fence));
}
void DequeueBuffer(int* outSlot, sp<Fence>* outFence) {
ASSERT_NE(nullptr, outSlot);
ASSERT_NE(nullptr, outFence);
int ret = mProducer->dequeueBuffer(outSlot, outFence, kDefaultWidth,
kDefaultHeight, kDefaultFormat,
kTestProducerUsageBits, nullptr);
// BUFFER_NEEDS_REALLOCATION can be either on or off.
ASSERT_EQ(0, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & ret);
// Slot number should be in boundary.
ASSERT_LE(0, *outSlot);
ASSERT_GT(BufferQueueDefs::NUM_BUFFER_SLOTS, *outSlot);
}
// Create a generic "valid" input for queueBuffer
// -- uses the default buffer format, width, etc.
static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() {
return QueueBufferInputBuilder().build();
}
const sp<IProducerListener> kDummyListener{new DummyProducerListener};
sp<BufferHubQueueProducer> mProducer;
sp<Surface> mSurface;
};
TEST_F(BufferHubQueueProducerTest, ConnectFirst_ReturnsError) {
IGraphicBufferProducer::QueueBufferOutput output;
// NULL output returns BAD_VALUE
EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
kTestControlledByApp, nullptr));
// Invalid API returns bad value
EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApiInvalid,
kTestControlledByApp, &output));
}
TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
// Can't connect when there is already a producer connected.
IGraphicBufferProducer::QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi,
kTestControlledByApp, &output));
}
TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
}
TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
// Must disconnect with same API number
EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiOther));
// API must not be out of range
EXPECT_EQ(BAD_VALUE, mProducer->disconnect(kTestApiInvalid));
}
TEST_F(BufferHubQueueProducerTest, Query_Succeeds) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
int32_t value = -1;
EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value));
EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value));
EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
EXPECT_EQ(kDefaultFormat, value);
EXPECT_EQ(NO_ERROR,
mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
EXPECT_LE(0, value);
EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value);
EXPECT_EQ(NO_ERROR,
mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value));
EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue
EXPECT_EQ(NO_ERROR,
mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
EXPECT_EQ(kDefaultConsumerUsageBits, value);
}
TEST_F(BufferHubQueueProducerTest, Query_ReturnsError) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
// One past the end of the last 'query' enum value. Update this if we add more
// enums.
const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_BUFFER_AGE + 1;
int value;
// What was out of range
EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ -1, &value));
EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/ 0xDEADBEEF, &value));
EXPECT_EQ(BAD_VALUE,
mProducer->query(NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, &value));
// Some enums from window.h are 'invalid'
EXPECT_EQ(BAD_VALUE,
mProducer->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &value));
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_CONCRETE_TYPE, &value));
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_WIDTH, &value));
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_HEIGHT, &value));
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value));
// Value was NULL
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/ NULL));
}
TEST_F(BufferHubQueueProducerTest, Queue_Succeeds) {
int slot = -1;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
// Request the buffer (pre-requisite for queueing)
sp<GraphicBuffer> buffer;
ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
// A generic "valid" input
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
// Queue the buffer back into the BQ
ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
EXPECT_EQ(kDefaultWidth, output.width);
EXPECT_EQ(kDefaultHeight, output.height);
EXPECT_EQ(kDefaultTransformHint, output.transformHint);
// BufferHubQueue delivers buffers to consumer immediately.
EXPECT_EQ(0u, output.numPendingBuffers);
// Note that BufferHubQueue doesn't support nextFrameNumber as it seems to
// be a SurfaceFlinger specific optimization.
EXPECT_EQ(0u, output.nextFrameNumber);
// Buffer was not in the dequeued state
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
}
// Test invalid slot number
TEST_F(BufferHubQueueProducerTest, QueueInvalidSlot_ReturnsError) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
// A generic "valid" input
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ -1, input, &output));
EXPECT_EQ(BAD_VALUE,
mProducer->queueBuffer(/*slot*/ 0xDEADBEEF, input, &output));
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueueDefs::NUM_BUFFER_SLOTS,
input, &output));
}
// Slot was not in the dequeued state (all slots start out in Free state)
TEST_F(BufferHubQueueProducerTest, QueueNotDequeued_ReturnsError) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/ 0, input, &output));
}
// Slot was enqueued without requesting a buffer
TEST_F(BufferHubQueueProducerTest, QueueNotRequested_ReturnsError) {
int slot = -1;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
}
// Test when fence was NULL
TEST_F(BufferHubQueueProducerTest, QueueNoFence_ReturnsError) {
int slot = -1;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
sp<GraphicBuffer> buffer;
ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
sp<Fence> nullFence = NULL;
IGraphicBufferProducer::QueueBufferInput input =
QueueBufferInputBuilder().setFence(nullFence).build();
IGraphicBufferProducer::QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
}
// Test scaling mode was invalid
TEST_F(BufferHubQueueProducerTest, QueueTestInvalidScalingMode_ReturnsError) {
int slot = -1;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
sp<GraphicBuffer> buffer;
ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input =
QueueBufferInputBuilder().setScalingMode(-1).build();
IGraphicBufferProducer::QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build();
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
}
// Test crop rect is out of bounds of the buffer dimensions
TEST_F(BufferHubQueueProducerTest, QueueCropOutOfBounds_ReturnsError) {
int slot = -1;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
sp<GraphicBuffer> buffer;
ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input =
QueueBufferInputBuilder()
.setCrop(Rect(kDefaultWidth + 1, kDefaultHeight + 1))
.build();
IGraphicBufferProducer::QueueBufferOutput output;
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(slot, input, &output));
}
TEST_F(BufferHubQueueProducerTest, CancelBuffer_Succeeds) {
int slot = -1;
sp<Fence> fence;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
// Should be able to cancel buffer after a dequeue.
EXPECT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
}
TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
return;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
int minUndequeuedBuffers;
ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
&minUndequeuedBuffers));
const int minBuffers = 1;
const int maxBuffers =
BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
<< "async mode: " << false;
ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(minBuffers))
<< "bufferCount: " << minBuffers;
// Should now be able to dequeue up to minBuffers times
// Should now be able to dequeue up to maxBuffers times
int slot = -1;
for (int i = 0; i < minBuffers; ++i) {
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
}
ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers));
// queue the first buffer to enable max dequeued buffer count checking
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
sp<GraphicBuffer> buffer;
ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
sp<Fence> fence;
for (int i = 0; i < maxBuffers; ++i) {
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
}
// Cancel a buffer, so we can decrease the buffer count
ASSERT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
// Should now be able to decrease the max dequeued count by 1
ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
}
TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
int minUndequeuedBuffers;
ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
&minUndequeuedBuffers));
const int minBuffers = 1;
const int maxBuffers =
BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
<< "async mode: " << false;
// Buffer count was out of range
EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0))
<< "bufferCount: " << 0;
EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(maxBuffers + 1))
<< "bufferCount: " << maxBuffers + 1;
// Set max dequeue count to 2
ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(2));
// Dequeue 2 buffers
int slot = -1;
sp<Fence> fence;
for (int i = 0; i < 2; i++) {
ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
(mProducer->dequeueBuffer(
&slot, &fence, kDefaultWidth, kDefaultHeight,
kDefaultFormat, kTestProducerUsageBits, nullptr)))
<< "slot: " << slot;
}
// Client has too many buffers dequeued
EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(1))
<< "bufferCount: " << minBuffers;
}
TEST_F(BufferHubQueueProducerTest,
DisconnectedProducerReturnsError_dequeueBuffer) {
int slot = -1;
sp<Fence> fence;
ASSERT_EQ(NO_INIT, mProducer->dequeueBuffer(&slot, &fence, kDefaultWidth,
kDefaultHeight, kDefaultFormat,
kTestProducerUsageBits, nullptr));
}
TEST_F(BufferHubQueueProducerTest,
DisconnectedProducerReturnsError_requestBuffer) {
int slot = -1;
sp<GraphicBuffer> buffer;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
// Shouldn't be able to request buffer after disconnect.
ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer));
}
TEST_F(BufferHubQueueProducerTest,
DisconnectedProducerReturnsError_queueBuffer) {
int slot = -1;
sp<GraphicBuffer> buffer;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
// A generic "valid" input
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
// Shouldn't be able to queue buffer after disconnect.
ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output));
}
TEST_F(BufferHubQueueProducerTest,
DisconnectedProducerReturnsError_cancelBuffer) {
int slot = -1;
sp<GraphicBuffer> buffer;
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
// Shouldn't be able to cancel buffer after disconnect.
ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE));
}
} // namespace
} // namespace dvr
} // namespace android