f0eaf25e92
Add a callback to the producer side, onBufferReleased, which will be called every time the consumer releases a buffer back to the BufferQueue. This will enable a buffer stream splitter to work autonomously without having to block on dequeueBuffer. The binder object used for the callback replaces the generic IBinder token that was passed into IGraphicBufferProducer::connect to detect the death of the producer. If a producer does not wish to listen for buffer release events, it can pass in an instance of the DummyProducerListener class defined in IProducerListener.h, if it even cares about death events (BufferQueue doesn't enforce the token being non-NULL, though perhaps we should). Change-Id: I23935760673524abeafea2b58dccc3583b368710
570 lines
20 KiB
C++
570 lines
20 KiB
C++
/*
|
|
* Copyright (C) 2013 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#define LOG_TAG "IGraphicBufferProducer_test"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <utils/String8.h>
|
|
#include <utils/threads.h>
|
|
|
|
#include <ui/GraphicBuffer.h>
|
|
|
|
#include <gui/BufferQueue.h>
|
|
#include <gui/IProducerListener.h>
|
|
|
|
#include <vector>
|
|
|
|
#define ASSERT_OK(x) ASSERT_EQ(OK, (x))
|
|
#define EXPECT_OK(x) EXPECT_EQ(OK, (x))
|
|
|
|
#define TEST_TOKEN ((IProducerListener*)(NULL))
|
|
#define TEST_API NATIVE_WINDOW_API_CPU
|
|
#define TEST_API_OTHER NATIVE_WINDOW_API_EGL // valid API that's not TEST_API
|
|
#define TEST_CONTROLLED_BY_APP false
|
|
#define TEST_PRODUCER_USAGE_BITS (0)
|
|
|
|
// TODO: Make these public constants in a header
|
|
enum {
|
|
// Default dimensions before setDefaultBufferSize is called
|
|
DEFAULT_WIDTH = 1,
|
|
DEFAULT_HEIGHT = 1,
|
|
|
|
// Default format before setDefaultBufferFormat is called
|
|
DEFAULT_FORMAT = HAL_PIXEL_FORMAT_RGBA_8888,
|
|
|
|
// Default transform hint before setTransformHint is called
|
|
DEFAULT_TRANSFORM_HINT = 0,
|
|
};
|
|
|
|
namespace android {
|
|
|
|
namespace {
|
|
// Parameters for a generic "valid" input for queueBuffer.
|
|
const int64_t QUEUE_BUFFER_INPUT_TIMESTAMP = 1384888611;
|
|
const bool QUEUE_BUFFER_INPUT_IS_AUTO_TIMESTAMP = false;
|
|
const Rect QUEUE_BUFFER_INPUT_RECT = Rect(DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
|
const int QUEUE_BUFFER_INPUT_SCALING_MODE = 0;
|
|
const int QUEUE_BUFFER_INPUT_TRANSFORM = 0;
|
|
const bool QUEUE_BUFFER_INPUT_ASYNC = false;
|
|
const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
|
|
}; // namespace anonymous
|
|
|
|
struct DummyConsumer : public BnConsumerListener {
|
|
virtual void onFrameAvailable() {}
|
|
virtual void onBuffersReleased() {}
|
|
virtual void onSidebandStreamChanged() {}
|
|
};
|
|
|
|
class IGraphicBufferProducerTest : public ::testing::Test {
|
|
protected:
|
|
|
|
IGraphicBufferProducerTest() {}
|
|
|
|
virtual void SetUp() {
|
|
const ::testing::TestInfo* const testInfo =
|
|
::testing::UnitTest::GetInstance()->current_test_info();
|
|
ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
|
|
testInfo->name());
|
|
|
|
mBQ = new BufferQueue();
|
|
mDC = new DummyConsumer;
|
|
|
|
mProducer = mBQ;
|
|
mConsumer = mBQ;
|
|
|
|
// Test check: Can't connect producer if no consumer yet
|
|
ASSERT_EQ(NO_INIT, TryConnectProducer());
|
|
|
|
// Must connect consumer before producer connects will succeed.
|
|
ASSERT_OK(mConsumer->consumerConnect(mDC, /*controlledByApp*/false));
|
|
}
|
|
|
|
virtual void TearDown() {
|
|
const ::testing::TestInfo* const testInfo =
|
|
::testing::UnitTest::GetInstance()->current_test_info();
|
|
ALOGV("End test: %s.%s", testInfo->test_case_name(),
|
|
testInfo->name());
|
|
}
|
|
|
|
status_t TryConnectProducer() {
|
|
IGraphicBufferProducer::QueueBufferOutput output;
|
|
return mProducer->connect(TEST_TOKEN,
|
|
TEST_API,
|
|
TEST_CONTROLLED_BY_APP,
|
|
&output);
|
|
// TODO: use params to vary token, api, producercontrolledbyapp, etc
|
|
}
|
|
|
|
// Connect to a producer in a 'correct' fashion.
|
|
// Precondition: Consumer is connected.
|
|
void ConnectProducer() {
|
|
ASSERT_OK(TryConnectProducer());
|
|
}
|
|
|
|
// Create a generic "valid" input for queueBuffer
|
|
// -- uses the default buffer format, width, etc.
|
|
static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() {
|
|
return QueueBufferInputBuilder().build();
|
|
}
|
|
|
|
// Builder pattern to slightly vary *almost* correct input
|
|
// -- avoids copying and pasting
|
|
struct QueueBufferInputBuilder {
|
|
QueueBufferInputBuilder() {
|
|
timestamp = QUEUE_BUFFER_INPUT_TIMESTAMP;
|
|
isAutoTimestamp = QUEUE_BUFFER_INPUT_IS_AUTO_TIMESTAMP;
|
|
crop = QUEUE_BUFFER_INPUT_RECT;
|
|
scalingMode = QUEUE_BUFFER_INPUT_SCALING_MODE;
|
|
transform = QUEUE_BUFFER_INPUT_TRANSFORM;
|
|
async = QUEUE_BUFFER_INPUT_ASYNC;
|
|
fence = QUEUE_BUFFER_INPUT_FENCE;
|
|
}
|
|
|
|
IGraphicBufferProducer::QueueBufferInput build() {
|
|
return IGraphicBufferProducer::QueueBufferInput(
|
|
timestamp,
|
|
isAutoTimestamp,
|
|
crop,
|
|
scalingMode,
|
|
transform,
|
|
async,
|
|
fence);
|
|
}
|
|
|
|
QueueBufferInputBuilder& setTimestamp(int64_t timestamp) {
|
|
this->timestamp = timestamp;
|
|
return *this;
|
|
}
|
|
|
|
QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) {
|
|
this->isAutoTimestamp = isAutoTimestamp;
|
|
return *this;
|
|
}
|
|
|
|
QueueBufferInputBuilder& setCrop(Rect crop) {
|
|
this->crop = crop;
|
|
return *this;
|
|
}
|
|
|
|
QueueBufferInputBuilder& setScalingMode(int scalingMode) {
|
|
this->scalingMode = scalingMode;
|
|
return *this;
|
|
}
|
|
|
|
QueueBufferInputBuilder& setTransform(uint32_t transform) {
|
|
this->transform = transform;
|
|
return *this;
|
|
}
|
|
|
|
QueueBufferInputBuilder& setAsync(bool async) {
|
|
this->async = async;
|
|
return *this;
|
|
}
|
|
|
|
QueueBufferInputBuilder& setFence(sp<Fence> fence) {
|
|
this->fence = fence;
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int64_t timestamp;
|
|
bool isAutoTimestamp;
|
|
Rect crop;
|
|
int scalingMode;
|
|
uint32_t transform;
|
|
int async;
|
|
sp<Fence> fence;
|
|
}; // struct QueueBufferInputBuilder
|
|
|
|
// To easily store dequeueBuffer results into containers
|
|
struct DequeueBufferResult {
|
|
int slot;
|
|
sp<Fence> fence;
|
|
};
|
|
|
|
status_t dequeueBuffer(bool async, uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) {
|
|
return mProducer->dequeueBuffer(&result->slot, &result->fence, async, w, h, format, usage);
|
|
}
|
|
|
|
private: // hide from test body
|
|
sp<BufferQueue> mBQ;
|
|
sp<DummyConsumer> mDC;
|
|
|
|
protected: // accessible from test body
|
|
sp<IGraphicBufferProducer> mProducer;
|
|
sp<IGraphicBufferConsumer> mConsumer;
|
|
};
|
|
|
|
TEST_F(IGraphicBufferProducerTest, ConnectFirst_ReturnsError) {
|
|
IGraphicBufferProducer::QueueBufferOutput output;
|
|
|
|
// NULL output returns BAD_VALUE
|
|
EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN,
|
|
TEST_API,
|
|
TEST_CONTROLLED_BY_APP,
|
|
/*output*/NULL));
|
|
|
|
// Invalid API returns bad value
|
|
EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN,
|
|
/*api*/0xDEADBEEF,
|
|
TEST_CONTROLLED_BY_APP,
|
|
&output));
|
|
|
|
// TODO: get a token from a dead process somehow
|
|
}
|
|
|
|
TEST_F(IGraphicBufferProducerTest, 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(TEST_TOKEN,
|
|
TEST_API,
|
|
TEST_CONTROLLED_BY_APP,
|
|
&output));
|
|
|
|
ASSERT_OK(mConsumer->consumerDisconnect());
|
|
// Can't connect when IGBP is abandoned
|
|
EXPECT_EQ(NO_INIT, mProducer->connect(TEST_TOKEN,
|
|
TEST_API,
|
|
TEST_CONTROLLED_BY_APP,
|
|
&output));
|
|
}
|
|
|
|
TEST_F(IGraphicBufferProducerTest, Disconnect_Succeeds) {
|
|
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
|
|
|
|
ASSERT_OK(mProducer->disconnect(TEST_API));
|
|
}
|
|
|
|
|
|
TEST_F(IGraphicBufferProducerTest, Disconnect_ReturnsError) {
|
|
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
|
|
|
|
// Must disconnect with same API number
|
|
ASSERT_EQ(BAD_VALUE, mProducer->disconnect(TEST_API_OTHER));
|
|
// API must not be out of range
|
|
ASSERT_EQ(BAD_VALUE, mProducer->disconnect(/*api*/0xDEADBEEF));
|
|
|
|
// TODO: somehow kill mProducer so that this returns DEAD_OBJECT
|
|
}
|
|
|
|
TEST_F(IGraphicBufferProducerTest, Query_Succeeds) {
|
|
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
|
|
|
|
// TODO: Make these constants in header
|
|
const int DEFAULT_CONSUMER_USAGE_BITS = 0;
|
|
|
|
int value = -1;
|
|
EXPECT_OK(mProducer->query(NATIVE_WINDOW_WIDTH, &value));
|
|
EXPECT_EQ(DEFAULT_WIDTH, value);
|
|
|
|
EXPECT_OK(mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
|
|
EXPECT_EQ(DEFAULT_HEIGHT, value);
|
|
|
|
EXPECT_OK(mProducer->query(NATIVE_WINDOW_FORMAT, &value));
|
|
EXPECT_EQ(DEFAULT_FORMAT, value);
|
|
|
|
EXPECT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
|
|
EXPECT_LE(0, value);
|
|
EXPECT_GE(BufferQueue::NUM_BUFFER_SLOTS, value);
|
|
|
|
EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value));
|
|
EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue
|
|
|
|
EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
|
|
EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, value);
|
|
|
|
}
|
|
|
|
TEST_F(IGraphicBufferProducerTest, 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_CONSUMER_USAGE_BITS + 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));
|
|
// TODO: Consider documented the above enums as unsupported or make a new enum for IGBP
|
|
|
|
// Value was NULL
|
|
EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/NULL));
|
|
|
|
ASSERT_OK(mConsumer->consumerDisconnect());
|
|
|
|
// BQ was abandoned
|
|
EXPECT_EQ(NO_INIT, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
|
|
|
|
// TODO: other things in window.h that are supported by Surface::query
|
|
// but not by BufferQueue::query
|
|
}
|
|
|
|
// TODO: queue under more complicated situations not involving just a single buffer
|
|
TEST_F(IGraphicBufferProducerTest, Queue_Succeeds) {
|
|
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
|
|
|
|
int dequeuedSlot = -1;
|
|
sp<Fence> dequeuedFence;
|
|
|
|
// XX: OK to assume first call returns this flag or not? Not really documented.
|
|
ASSERT_EQ(OK | IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
|
|
mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
|
|
QUEUE_BUFFER_INPUT_ASYNC,
|
|
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
|
|
TEST_PRODUCER_USAGE_BITS));
|
|
|
|
EXPECT_LE(0, dequeuedSlot);
|
|
EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot);
|
|
|
|
// Request the buffer (pre-requisite for queueing)
|
|
sp<GraphicBuffer> dequeuedBuffer;
|
|
ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer));
|
|
|
|
// A generic "valid" input
|
|
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
|
|
IGraphicBufferProducer::QueueBufferOutput output;
|
|
|
|
// Queue the buffer back into the BQ
|
|
ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output));
|
|
|
|
{
|
|
uint32_t width;
|
|
uint32_t height;
|
|
uint32_t transformHint;
|
|
uint32_t numPendingBuffers;
|
|
|
|
output.deflate(&width, &height, &transformHint, &numPendingBuffers);
|
|
|
|
EXPECT_EQ(DEFAULT_WIDTH, width);
|
|
EXPECT_EQ(DEFAULT_HEIGHT, height);
|
|
EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint);
|
|
EXPECT_EQ(1, numPendingBuffers); // since queueBuffer was called exactly once
|
|
}
|
|
|
|
// Buffer was not in the dequeued state
|
|
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
|
|
}
|
|
|
|
TEST_F(IGraphicBufferProducerTest, Queue_ReturnsError) {
|
|
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
|
|
|
|
// Invalid slot number
|
|
{
|
|
// 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(BufferQueue::NUM_BUFFER_SLOTS,
|
|
input, &output));
|
|
}
|
|
|
|
// Slot was not in the dequeued state (all slots start out in Free state)
|
|
{
|
|
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
|
|
IGraphicBufferProducer::QueueBufferOutput output;
|
|
|
|
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0, input, &output));
|
|
}
|
|
|
|
// Put the slot into the "dequeued" state for the rest of the test
|
|
int dequeuedSlot = -1;
|
|
sp<Fence> dequeuedFence;
|
|
|
|
ASSERT_EQ(OK | IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
|
|
mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
|
|
QUEUE_BUFFER_INPUT_ASYNC,
|
|
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
|
|
TEST_PRODUCER_USAGE_BITS));
|
|
|
|
// Slot was enqueued without requesting a buffer
|
|
{
|
|
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
|
|
IGraphicBufferProducer::QueueBufferOutput output;
|
|
|
|
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
|
|
}
|
|
|
|
// Request the buffer so that the rest of the tests don't fail on earlier checks.
|
|
sp<GraphicBuffer> dequeuedBuffer;
|
|
ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer));
|
|
|
|
// Fence was NULL
|
|
{
|
|
sp<Fence> nullFence = NULL;
|
|
|
|
IGraphicBufferProducer::QueueBufferInput input =
|
|
QueueBufferInputBuilder().setFence(nullFence).build();
|
|
IGraphicBufferProducer::QueueBufferOutput output;
|
|
|
|
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
|
|
}
|
|
|
|
// Scaling mode was unknown
|
|
{
|
|
IGraphicBufferProducer::QueueBufferInput input =
|
|
QueueBufferInputBuilder().setScalingMode(-1).build();
|
|
IGraphicBufferProducer::QueueBufferOutput output;
|
|
|
|
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
|
|
|
|
input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build();
|
|
|
|
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
|
|
}
|
|
|
|
// Crop rect is out of bounds of the buffer dimensions
|
|
{
|
|
IGraphicBufferProducer::QueueBufferInput input =
|
|
QueueBufferInputBuilder().setCrop(Rect(DEFAULT_WIDTH + 1, DEFAULT_HEIGHT + 1))
|
|
.build();
|
|
IGraphicBufferProducer::QueueBufferOutput output;
|
|
|
|
EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output));
|
|
}
|
|
|
|
// Abandon the buffer queue so that the last test fails
|
|
ASSERT_OK(mConsumer->consumerDisconnect());
|
|
|
|
// The buffer queue has been abandoned.
|
|
{
|
|
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
|
|
IGraphicBufferProducer::QueueBufferOutput output;
|
|
|
|
EXPECT_EQ(NO_INIT, mProducer->queueBuffer(dequeuedSlot, input, &output));
|
|
}
|
|
}
|
|
|
|
TEST_F(IGraphicBufferProducerTest, CancelBuffer_DoesntCrash) {
|
|
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
|
|
|
|
int dequeuedSlot = -1;
|
|
sp<Fence> dequeuedFence;
|
|
|
|
ASSERT_EQ(OK | IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
|
|
mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
|
|
QUEUE_BUFFER_INPUT_ASYNC,
|
|
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
|
|
TEST_PRODUCER_USAGE_BITS));
|
|
|
|
// No return code, but at least test that it doesn't blow up...
|
|
// TODO: add a return code
|
|
mProducer->cancelBuffer(dequeuedSlot, dequeuedFence);
|
|
}
|
|
|
|
TEST_F(IGraphicBufferProducerTest, SetBufferCount_Succeeds) {
|
|
|
|
// The producer does not wish to set a buffer count
|
|
EXPECT_OK(mProducer->setBufferCount(0)) << "bufferCount: " << 0;
|
|
// TODO: how to test "0" buffer count?
|
|
|
|
int minBuffers;
|
|
ASSERT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minBuffers));
|
|
|
|
// The MIN_UNDEQUEUED_BUFFERS limit is exclusive, so need to increment by at least 1
|
|
minBuffers++;
|
|
|
|
ASSERT_OK(mProducer->setBufferCount(minBuffers)) << "bufferCount: " << minBuffers;
|
|
|
|
std::vector<DequeueBufferResult> dequeueList;
|
|
|
|
// Should now be able to dequeue up to minBuffers times
|
|
for (int i = 0; i < minBuffers; ++i) {
|
|
DequeueBufferResult result;
|
|
|
|
EXPECT_LE(OK,
|
|
dequeueBuffer(QUEUE_BUFFER_INPUT_ASYNC,
|
|
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
|
|
TEST_PRODUCER_USAGE_BITS, &result))
|
|
<< "iteration: " << i << ", slot: " << result.slot;
|
|
|
|
dequeueList.push_back(result);
|
|
}
|
|
|
|
// Cancel every buffer, so we can set buffer count again
|
|
for (int i = 0; i < minBuffers; ++i) {
|
|
DequeueBufferResult& result = dequeueList[i];
|
|
mProducer->cancelBuffer(result.slot, result.fence);
|
|
}
|
|
|
|
ASSERT_OK(mProducer->setBufferCount(BufferQueue::NUM_BUFFER_SLOTS));
|
|
|
|
// Should now be able to dequeue up to NUM_BUFFER_SLOTS times
|
|
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; ++i) {
|
|
int dequeuedSlot = -1;
|
|
sp<Fence> dequeuedFence;
|
|
|
|
EXPECT_LE(OK,
|
|
mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
|
|
QUEUE_BUFFER_INPUT_ASYNC,
|
|
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
|
|
TEST_PRODUCER_USAGE_BITS))
|
|
<< "iteration: " << i << ", slot: " << dequeuedSlot;
|
|
}
|
|
}
|
|
|
|
TEST_F(IGraphicBufferProducerTest, SetBufferCount_Fails) {
|
|
int minBuffers;
|
|
ASSERT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minBuffers));
|
|
|
|
// The MIN_UNDEQUEUED_BUFFERS limit is exclusive, so need to increment by at least 1
|
|
minBuffers++;
|
|
|
|
// Buffer count was out of range
|
|
EXPECT_EQ(BAD_VALUE, mProducer->setBufferCount(-1)) << "bufferCount: " << -1;
|
|
EXPECT_EQ(BAD_VALUE, mProducer->setBufferCount(minBuffers - 1)) << "bufferCount: " << minBuffers - 1;
|
|
EXPECT_EQ(BAD_VALUE, mProducer->setBufferCount(BufferQueue::NUM_BUFFER_SLOTS + 1))
|
|
<< "bufferCount: " << BufferQueue::NUM_BUFFER_SLOTS + 1;
|
|
|
|
// Pre-requisite to fail out a valid setBufferCount call
|
|
{
|
|
int dequeuedSlot = -1;
|
|
sp<Fence> dequeuedFence;
|
|
|
|
ASSERT_LE(OK,
|
|
mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence,
|
|
QUEUE_BUFFER_INPUT_ASYNC,
|
|
DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
|
|
TEST_PRODUCER_USAGE_BITS))
|
|
<< "slot: " << dequeuedSlot;
|
|
}
|
|
|
|
// Client has one or more buffers dequeued
|
|
EXPECT_EQ(BAD_VALUE, mProducer->setBufferCount(minBuffers)) << "bufferCount: " << minBuffers;
|
|
|
|
// Abandon buffer queue
|
|
ASSERT_OK(mConsumer->consumerDisconnect());
|
|
|
|
// Fail because the buffer queue was abandoned
|
|
EXPECT_EQ(NO_INIT, mProducer->setBufferCount(minBuffers)) << "bufferCount: " << minBuffers;
|
|
|
|
}
|
|
|
|
} // namespace android
|