You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
872 lines
27 KiB
872 lines
27 KiB
// Copyright 2019 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.
|
|
|
|
#include <gtest/gtest.h> // for Message
|
|
#include <stdint.h> // for uint32_t
|
|
#include <stdio.h> // for printf
|
|
#include <string.h> // for size_t
|
|
#include <sys/types.h> // for ssize_t
|
|
#include <algorithm> // for uniform_...
|
|
#include <functional> // for __base
|
|
#include <random> // for default_...
|
|
#include <vector> // for vector
|
|
|
|
#include "base/ring_buffer.h" // for ring_buf...
|
|
#include "base/FunctorThread.h" // for FunctorT...
|
|
#include "host-common/AndroidAgentFactory.h" // for getConso...
|
|
#include "host-common/AddressSpaceService.h" // for AddressS...
|
|
#include "host-common/address_space_device.hpp" // for goldfish...
|
|
#include "host-common/address_space_graphics.h" // for AddressS...
|
|
#include "host-common/address_space_graphics_types.h" // for asg_context
|
|
#include "testing/HostAddressSpace.h" // for HostAddr...
|
|
#include "host-common/globals.h" // for android_hw
|
|
|
|
namespace android {
|
|
namespace base {
|
|
class Stream;
|
|
} // namespace base
|
|
} // namespace android
|
|
|
|
using android::base::FunctorThread;
|
|
|
|
|
|
|
|
namespace android {
|
|
namespace emulation {
|
|
namespace asg {
|
|
|
|
#define ASG_TEST_READ_PATTERN 0xAA
|
|
#define ASG_TEST_WRITE_PATTERN 0xBB
|
|
|
|
class AddressSpaceGraphicsTest : public ::testing::Test {
|
|
public:
|
|
class Client {
|
|
public:
|
|
Client(HostAddressSpaceDevice* device) :
|
|
mDevice(device),
|
|
mHandle(mDevice->open()) {
|
|
|
|
ping((uint64_t)AddressSpaceDeviceType::Graphics);
|
|
|
|
auto getRingResult = ping((uint64_t)ASG_GET_RING);
|
|
mRingOffset = getRingResult.metadata;
|
|
mRingSize = getRingResult.size;
|
|
|
|
EXPECT_EQ(0, mDevice->claimShared(mHandle, mRingOffset, mRingSize));
|
|
|
|
mRingStorage =
|
|
(char*)mDevice->getHostAddr(
|
|
mDevice->offsetToPhysAddr(mRingOffset));
|
|
|
|
auto getBufferResult = ping((uint64_t)ASG_GET_BUFFER);
|
|
mBufferOffset = getBufferResult.metadata;
|
|
mBufferSize = getBufferResult.size;
|
|
|
|
EXPECT_EQ(0, mDevice->claimShared(mHandle, mBufferOffset, mBufferSize));
|
|
mBuffer =
|
|
(char*)mDevice->getHostAddr(
|
|
mDevice->offsetToPhysAddr(mBufferOffset));
|
|
|
|
mContext = asg_context_create(mRingStorage, mBuffer, mBufferSize);
|
|
|
|
EXPECT_EQ(mBuffer, mContext.buffer);
|
|
|
|
auto setVersionResult = ping((uint64_t)ASG_SET_VERSION, mVersion);
|
|
uint32_t hostVersion = setVersionResult.size;
|
|
EXPECT_LE(hostVersion, mVersion);
|
|
EXPECT_EQ(android_hw->hw_gltransport_asg_writeStepSize,
|
|
mContext.ring_config->flush_interval);
|
|
EXPECT_EQ(android_hw->hw_gltransport_asg_writeBufferSize,
|
|
mBufferSize);
|
|
|
|
mContext.ring_config->transfer_mode = 1;
|
|
mContext.ring_config->host_consumed_pos = 0;
|
|
mContext.ring_config->guest_write_pos = 0;
|
|
mBufferMask = mBufferSize - 1;
|
|
|
|
mWriteStart = mBuffer;
|
|
}
|
|
|
|
~Client() {
|
|
mDevice->unclaimShared(mHandle, mBufferOffset);
|
|
mDevice->unclaimShared(mHandle, mRingOffset);
|
|
mDevice->close(mHandle);
|
|
}
|
|
|
|
bool isInError() const {
|
|
return 1 == mContext.ring_config->in_error;
|
|
}
|
|
|
|
void abort() {
|
|
mContext.ring_config->in_error = 1;
|
|
}
|
|
|
|
char* allocBuffer(size_t size) {
|
|
if (size > mContext.ring_config->flush_interval) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (mWriteStart + mCurrentWriteBytes + size >
|
|
mWriteStart + mWriteStep) {
|
|
flush();
|
|
mCurrentWriteBytes = 0;
|
|
}
|
|
|
|
char* res = mWriteStart + mCurrentWriteBytes;
|
|
mCurrentWriteBytes += size;
|
|
|
|
return res;
|
|
}
|
|
|
|
int writeFully(const char* buf, size_t size) {
|
|
flush();
|
|
ensureType1Finished();
|
|
mContext.ring_config->transfer_size = size;
|
|
mContext.ring_config->transfer_mode = 3;
|
|
|
|
size_t sent = 0;
|
|
size_t quarterRingSize = mBufferSize / 4;
|
|
size_t chunkSize = size < quarterRingSize ? size : quarterRingSize;
|
|
|
|
while (sent < size) {
|
|
size_t remaining = size - sent;
|
|
size_t sendThisTime = remaining < chunkSize ? remaining : chunkSize;
|
|
|
|
long sentChunks =
|
|
ring_buffer_view_write(
|
|
mContext.to_host_large_xfer.ring,
|
|
&mContext.to_host_large_xfer.view,
|
|
buf + sent, sendThisTime, 1);
|
|
|
|
if (*(mContext.host_state) != ASG_HOST_STATE_CAN_CONSUME) {
|
|
ping(ASG_NOTIFY_AVAILABLE);
|
|
}
|
|
|
|
if (sentChunks == 0) {
|
|
ring_buffer_yield();
|
|
}
|
|
|
|
sent += sentChunks * sendThisTime;
|
|
|
|
if (isInError()) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ensureType3Finished();
|
|
mContext.ring_config->transfer_mode = 1;
|
|
return 0;
|
|
}
|
|
|
|
ssize_t speculativeRead(char* readBuffer, size_t minSizeToRead) {
|
|
flush();
|
|
ensureConsumerFinishing();
|
|
|
|
size_t actuallyRead = 0;
|
|
while (!actuallyRead) {
|
|
uint32_t readAvail =
|
|
ring_buffer_available_read(
|
|
mContext.from_host_large_xfer.ring,
|
|
&mContext.from_host_large_xfer.view);
|
|
|
|
if (!readAvail) {
|
|
ring_buffer_yield();
|
|
continue;
|
|
}
|
|
|
|
uint32_t toRead = readAvail > minSizeToRead ?
|
|
minSizeToRead : readAvail;
|
|
|
|
long stepsRead = ring_buffer_view_read(
|
|
mContext.from_host_large_xfer.ring,
|
|
&mContext.from_host_large_xfer.view,
|
|
readBuffer, toRead, 1);
|
|
|
|
actuallyRead += stepsRead * toRead;
|
|
|
|
if (isInError()) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return actuallyRead;
|
|
}
|
|
|
|
void flush() {
|
|
if (!mCurrentWriteBytes) return;
|
|
type1WriteWithNotify(mWriteStart - mBuffer, mCurrentWriteBytes);
|
|
advanceWrite();
|
|
}
|
|
|
|
uint32_t get_relative_buffer_pos(uint32_t pos) {
|
|
return pos & mBufferMask;
|
|
}
|
|
|
|
uint32_t get_available_for_write() {
|
|
uint32_t host_consumed_view;
|
|
__atomic_load(&mContext.ring_config->host_consumed_pos,
|
|
&host_consumed_view,
|
|
__ATOMIC_SEQ_CST);
|
|
uint32_t availableForWrite =
|
|
get_relative_buffer_pos(
|
|
host_consumed_view -
|
|
mContext.ring_config->guest_write_pos - 1);
|
|
return availableForWrite;
|
|
}
|
|
|
|
void advanceWrite() {
|
|
uint32_t avail = get_available_for_write();
|
|
|
|
while (avail < mContext.ring_config->flush_interval) {
|
|
ensureConsumerFinishing();
|
|
avail = get_available_for_write();
|
|
}
|
|
|
|
__atomic_add_fetch(
|
|
&mContext.ring_config->guest_write_pos,
|
|
mContext.ring_config->flush_interval,
|
|
__ATOMIC_SEQ_CST);
|
|
|
|
char* newBuffer =
|
|
mBuffer +
|
|
get_relative_buffer_pos(
|
|
mContext.ring_config->guest_write_pos);
|
|
|
|
mWriteStart = newBuffer;
|
|
mCurrentWriteBytes = 0;
|
|
}
|
|
|
|
int type1WriteWithNotify(uint32_t bufferOffset, size_t size) {
|
|
size_t sent = 0;
|
|
size_t sizeForRing = 8;
|
|
|
|
struct asg_type1_xfer xfer {
|
|
bufferOffset,
|
|
(uint32_t)size,
|
|
};
|
|
|
|
uint8_t* writeBufferBytes = (uint8_t*)(&xfer);
|
|
|
|
while (sent < sizeForRing) {
|
|
|
|
long sentChunks = ring_buffer_write(
|
|
mContext.to_host, writeBufferBytes + sent, sizeForRing - sent, 1);
|
|
|
|
if (*(mContext.host_state) != ASG_HOST_STATE_CAN_CONSUME) {
|
|
ping(ASG_NOTIFY_AVAILABLE);
|
|
}
|
|
|
|
if (sentChunks == 0) {
|
|
ring_buffer_yield();
|
|
}
|
|
|
|
sent += sentChunks * (sizeForRing - sent);
|
|
|
|
if (isInError()) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ensureConsumerFinishing() {
|
|
uint32_t currAvailRead =
|
|
ring_buffer_available_read(mContext.to_host, 0);
|
|
|
|
while (currAvailRead) {
|
|
ring_buffer_yield();
|
|
uint32_t nextAvailRead = ring_buffer_available_read(mContext.to_host, 0);
|
|
|
|
if (nextAvailRead != currAvailRead) {
|
|
break;
|
|
}
|
|
|
|
if (*(mContext.host_state) != ASG_HOST_STATE_CAN_CONSUME) {
|
|
ping(ASG_NOTIFY_AVAILABLE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ensureType1Finished() {
|
|
ensureConsumerFinishing();
|
|
|
|
uint32_t currAvailRead =
|
|
ring_buffer_available_read(mContext.to_host, 0);
|
|
|
|
while (currAvailRead) {
|
|
ring_buffer_yield();
|
|
currAvailRead = ring_buffer_available_read(mContext.to_host, 0);
|
|
if (isInError()) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ensureType3Finished() {
|
|
uint32_t availReadLarge =
|
|
ring_buffer_available_read(
|
|
mContext.to_host_large_xfer.ring,
|
|
&mContext.to_host_large_xfer.view);
|
|
while (availReadLarge) {
|
|
ring_buffer_yield();
|
|
availReadLarge =
|
|
ring_buffer_available_read(
|
|
mContext.to_host_large_xfer.ring,
|
|
&mContext.to_host_large_xfer.view);
|
|
if (*(mContext.host_state) != ASG_HOST_STATE_CAN_CONSUME) {
|
|
ping(ASG_NOTIFY_AVAILABLE);
|
|
}
|
|
if (isInError()) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
char* getBufferPtr() { return mBuffer; }
|
|
|
|
private:
|
|
|
|
AddressSpaceDevicePingInfo ping(uint64_t metadata, uint64_t size = 0) {
|
|
AddressSpaceDevicePingInfo info;
|
|
info.metadata = metadata;
|
|
mDevice->ping(mHandle, &info);
|
|
return info;
|
|
}
|
|
|
|
HostAddressSpaceDevice* mDevice;
|
|
uint32_t mHandle;
|
|
uint64_t mRingOffset;
|
|
uint64_t mRingSize;
|
|
uint64_t mBufferOffset;
|
|
uint64_t mBufferSize;
|
|
char* mRingStorage;
|
|
char* mBuffer;
|
|
struct asg_context mContext;
|
|
uint32_t mVersion = 1;
|
|
|
|
char* mWriteStart = 0;
|
|
uint32_t mWriteStep = 0;
|
|
uint32_t mCurrentWriteBytes = 0;
|
|
uint32_t mBufferMask = 0;
|
|
};
|
|
|
|
class Consumer {
|
|
public:
|
|
Consumer(struct asg_context context,
|
|
ConsumerCallbacks callbacks) :
|
|
mContext(context),
|
|
mCallbacks(callbacks),
|
|
mThread([this] { threadFunc(); }) {
|
|
mThread.start();
|
|
}
|
|
|
|
~Consumer() {
|
|
mThread.wait();
|
|
}
|
|
|
|
void setRoundTrip(bool enabled,
|
|
uint32_t toHostBytes = 0,
|
|
uint32_t fromHostBytes = 0) {
|
|
mRoundTripEnabled = enabled;
|
|
if (mRoundTripEnabled) {
|
|
mToHostBytes = toHostBytes;
|
|
mFromHostBytes = fromHostBytes;
|
|
}
|
|
}
|
|
|
|
void handleRoundTrip() {
|
|
if (!mRoundTripEnabled) return;
|
|
|
|
if (mReadPos == mToHostBytes) {
|
|
std::vector<char> reply(mFromHostBytes, ASG_TEST_READ_PATTERN);
|
|
uint32_t origBytes = mFromHostBytes;
|
|
auto res = ring_buffer_write_fully_with_abort(
|
|
mContext.from_host_large_xfer.ring,
|
|
&mContext.from_host_large_xfer.view,
|
|
reply.data(),
|
|
mFromHostBytes,
|
|
1, &mContext.ring_config->in_error);
|
|
if (res < mFromHostBytes) {
|
|
printf("%s: aborted write (%u vs %u %u). in error? %u\n", __func__,
|
|
res, mFromHostBytes, origBytes,
|
|
mContext.ring_config->in_error);
|
|
EXPECT_EQ(1, mContext.ring_config->in_error);
|
|
}
|
|
mReadPos = 0;
|
|
}
|
|
}
|
|
|
|
void ensureWritebackDone() {
|
|
while (mReadPos) {
|
|
ring_buffer_yield();
|
|
}
|
|
}
|
|
|
|
int step() {
|
|
|
|
uint32_t nonLargeAvail =
|
|
ring_buffer_available_read(
|
|
mContext.to_host, 0);
|
|
|
|
uint32_t largeAvail =
|
|
ring_buffer_available_read(
|
|
mContext.to_host_large_xfer.ring,
|
|
&mContext.to_host_large_xfer.view);
|
|
|
|
ensureReadBuffer(nonLargeAvail);
|
|
|
|
int res = 0;
|
|
if (nonLargeAvail) {
|
|
uint32_t transferMode = mContext.ring_config->transfer_mode;
|
|
|
|
switch (transferMode) {
|
|
case 1:
|
|
type1Read(nonLargeAvail);
|
|
break;
|
|
case 2:
|
|
type2Read(nonLargeAvail);
|
|
break;
|
|
case 3:
|
|
break;
|
|
default:
|
|
EXPECT_TRUE(false) << "Failed, invalid transfer mode";
|
|
}
|
|
|
|
|
|
res = 0;
|
|
} else if (largeAvail) {
|
|
res = type3Read(largeAvail);
|
|
} else {
|
|
res = mCallbacks.onUnavailableRead();
|
|
}
|
|
|
|
handleRoundTrip();
|
|
|
|
return res;
|
|
}
|
|
|
|
void ensureReadBuffer(uint32_t new_xfer) {
|
|
size_t readBufferAvail = mReadBuffer.size() - mReadPos;
|
|
if (readBufferAvail < new_xfer) {
|
|
mReadBuffer.resize(mReadBuffer.size() + 2 * new_xfer);
|
|
}
|
|
}
|
|
|
|
void type1Read(uint32_t avail) {
|
|
uint32_t xferTotal = avail / 8;
|
|
for (uint32_t i = 0; i < xferTotal; ++i) {
|
|
struct asg_type1_xfer currentXfer;
|
|
uint8_t* currentXferPtr = (uint8_t*)(¤tXfer);
|
|
|
|
EXPECT_EQ(0, ring_buffer_copy_contents(
|
|
mContext.to_host, 0,
|
|
sizeof(currentXfer), currentXferPtr));
|
|
|
|
char* ptr = mContext.buffer + currentXfer.offset;
|
|
size_t size = currentXfer.size;
|
|
|
|
ensureReadBuffer(size);
|
|
|
|
memcpy(mReadBuffer.data() + mReadPos,
|
|
ptr, size);
|
|
|
|
for (uint32_t j = 0; j < size; ++j) {
|
|
EXPECT_EQ((char)ASG_TEST_WRITE_PATTERN,
|
|
(mReadBuffer.data() + mReadPos)[j]);
|
|
}
|
|
|
|
mReadPos += size;
|
|
mContext.ring_config->host_consumed_pos =
|
|
ptr - mContext.buffer;
|
|
|
|
EXPECT_EQ(1, ring_buffer_advance_read(
|
|
mContext.to_host, sizeof(asg_type1_xfer), 1));
|
|
}
|
|
}
|
|
|
|
void type2Read(uint32_t avail) {
|
|
uint32_t xferTotal = avail / 16;
|
|
for (uint32_t i = 0; i < xferTotal; ++i) {
|
|
struct asg_type2_xfer currentXfer;
|
|
uint8_t* xferPtr = (uint8_t*)(¤tXfer);
|
|
|
|
EXPECT_EQ(0, ring_buffer_copy_contents(
|
|
mContext.to_host, 0, sizeof(currentXfer),
|
|
xferPtr));
|
|
|
|
char* ptr = mCallbacks.getPtr(currentXfer.physAddr);
|
|
ensureReadBuffer(currentXfer.size);
|
|
|
|
memcpy(mReadBuffer.data() + mReadPos, ptr,
|
|
currentXfer.size);
|
|
mReadPos += currentXfer.size;
|
|
|
|
EXPECT_EQ(1, ring_buffer_advance_read(
|
|
mContext.to_host, sizeof(currentXfer), 1));
|
|
}
|
|
}
|
|
|
|
int type3Read(uint32_t avail) {
|
|
(void)avail;
|
|
ensureReadBuffer(avail);
|
|
ring_buffer_read_fully_with_abort(
|
|
mContext.to_host_large_xfer.ring,
|
|
&mContext.to_host_large_xfer.view,
|
|
mReadBuffer.data() + mReadPos,
|
|
avail,
|
|
1, &mContext.ring_config->in_error);
|
|
mReadPos += avail;
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
|
|
void threadFunc() {
|
|
while(-1 != step());
|
|
}
|
|
|
|
struct asg_context mContext;
|
|
ConsumerCallbacks mCallbacks;
|
|
FunctorThread mThread;
|
|
std::vector<char> mReadBuffer;
|
|
std::vector<char> mWriteBuffer;
|
|
size_t mReadPos = 0;
|
|
uint32_t mToHostBytes = 0;
|
|
uint32_t mFromHostBytes = 0;
|
|
bool mRoundTripEnabled = false;
|
|
};
|
|
|
|
protected:
|
|
static void SetUpTestCase() {
|
|
goldfish_address_space_set_vm_operations(getConsoleAgents()->vm);
|
|
}
|
|
|
|
static void TearDownTestCase() { }
|
|
|
|
void SetUp() override {
|
|
android_hw->hw_gltransport_asg_writeBufferSize = 524288;
|
|
android_hw->hw_gltransport_asg_writeStepSize = 1024;
|
|
|
|
mDevice = HostAddressSpaceDevice::get();
|
|
ConsumerInterface interface = {
|
|
// create
|
|
[this](struct asg_context context,
|
|
base::Stream* loadStream,
|
|
ConsumerCallbacks callbacks) {
|
|
Consumer* c = new Consumer(context, callbacks);
|
|
mCurrentConsumer = c;
|
|
return (void*)c;
|
|
},
|
|
// destroy
|
|
[this](void* context) {
|
|
Consumer* c = reinterpret_cast<Consumer*>(context);
|
|
delete c;
|
|
mCurrentConsumer = nullptr;
|
|
},
|
|
// presave
|
|
[](void* consumer) { },
|
|
// global presave
|
|
[]() { },
|
|
// save
|
|
[](void* consumer, base::Stream* stream) { },
|
|
// global postsave
|
|
[]() { },
|
|
// postsave
|
|
[](void* consumer) { },
|
|
// postload
|
|
[](void* consumer) { },
|
|
// global preload
|
|
[]() { },
|
|
};
|
|
AddressSpaceGraphicsContext::setConsumer(interface);
|
|
}
|
|
|
|
void TearDown() override {
|
|
AddressSpaceGraphicsContext::clear();
|
|
mDevice->clear();
|
|
android_hw->hw_gltransport_asg_writeBufferSize = 524288;
|
|
android_hw->hw_gltransport_asg_writeStepSize = 1024;
|
|
EXPECT_EQ(nullptr, mCurrentConsumer);
|
|
}
|
|
|
|
void setRoundTrip(bool enabled, size_t writeBytes, size_t readBytes) {
|
|
EXPECT_NE(nullptr, mCurrentConsumer);
|
|
mCurrentConsumer->setRoundTrip(enabled, writeBytes, readBytes);
|
|
}
|
|
|
|
struct RoundTrip {
|
|
size_t writeBytes;
|
|
size_t readBytes;
|
|
};
|
|
|
|
void runRoundTrips(Client& client, const std::vector<RoundTrip>& trips) {
|
|
EXPECT_NE(nullptr, mCurrentConsumer);
|
|
|
|
for (const auto& trip : trips) {
|
|
mCurrentConsumer->setRoundTrip(true, trip.writeBytes, trip.readBytes);
|
|
|
|
std::vector<char> send(trip.writeBytes, ASG_TEST_WRITE_PATTERN);
|
|
std::vector<char> expectedRead(trip.readBytes, ASG_TEST_READ_PATTERN);
|
|
std::vector<char> toRead(trip.readBytes, 0);
|
|
|
|
size_t stepSize = android_hw->hw_gltransport_asg_writeStepSize;
|
|
size_t stepSizeRead = android_hw->hw_gltransport_asg_writeBufferSize;
|
|
|
|
size_t sent = 0;
|
|
while (sent < trip.writeBytes) {
|
|
size_t remaining = trip.writeBytes - sent;
|
|
size_t next = remaining < stepSize ? remaining : stepSize;
|
|
auto buf = client.allocBuffer(next);
|
|
memcpy(buf, send.data() + sent, next);
|
|
sent += next;
|
|
}
|
|
|
|
client.flush();
|
|
|
|
size_t recv = 0;
|
|
|
|
while (recv < trip.readBytes) {
|
|
ssize_t readThisTime = client.speculativeRead(
|
|
toRead.data() + recv, stepSizeRead);
|
|
EXPECT_GE(readThisTime, 0);
|
|
recv += readThisTime;
|
|
}
|
|
|
|
EXPECT_EQ(expectedRead, toRead);
|
|
|
|
// make sure the consumer is hung up here or this will
|
|
// race with setRoundTrip
|
|
mCurrentConsumer->ensureWritebackDone();
|
|
}
|
|
|
|
mCurrentConsumer->setRoundTrip(false);
|
|
}
|
|
|
|
HostAddressSpaceDevice* mDevice = nullptr;
|
|
Consumer* mCurrentConsumer = nullptr;
|
|
};
|
|
|
|
// Tests that we can create a client for ASG,
|
|
// which then in turn creates a consumer thread on the "host."
|
|
// Then test the thread teardown.
|
|
TEST_F(AddressSpaceGraphicsTest, Basic) {
|
|
Client client(mDevice);
|
|
}
|
|
|
|
// Tests writing via an IOStream-like interface
|
|
// (allocBuffer, then flush)
|
|
TEST_F(AddressSpaceGraphicsTest, BasicWrite) {
|
|
EXPECT_EQ(1024, android_hw->hw_gltransport_asg_writeStepSize);
|
|
Client client(mDevice);
|
|
|
|
// Tests that going over the step size results in nullptr
|
|
// when using allocBuffer
|
|
auto buf = client.allocBuffer(1025);
|
|
EXPECT_EQ(nullptr, buf);
|
|
|
|
buf = client.allocBuffer(4);
|
|
EXPECT_NE(nullptr, buf);
|
|
memset(buf, ASG_TEST_WRITE_PATTERN, 4);
|
|
client.flush();
|
|
}
|
|
|
|
// Tests that further allocs result in flushing
|
|
TEST_F(AddressSpaceGraphicsTest, FlushFromAlloc) {
|
|
EXPECT_EQ(1024, android_hw->hw_gltransport_asg_writeStepSize);
|
|
Client client(mDevice);
|
|
|
|
auto buf = client.allocBuffer(1024);
|
|
memset(buf, ASG_TEST_WRITE_PATTERN, 1024);
|
|
|
|
for (uint32_t i = 0; i < 10; ++i) {
|
|
buf = client.allocBuffer(1024);
|
|
memset(buf, ASG_TEST_WRITE_PATTERN, 1024);
|
|
}
|
|
}
|
|
|
|
// Tests type 3 (large) transfer by itself
|
|
TEST_F(AddressSpaceGraphicsTest, LargeXfer) {
|
|
Client client(mDevice);
|
|
|
|
std::vector<char> largeBuf(1048576, ASG_TEST_WRITE_PATTERN);
|
|
client.writeFully(largeBuf.data(), largeBuf.size());
|
|
}
|
|
|
|
// Round trip test
|
|
TEST_F(AddressSpaceGraphicsTest, RoundTrip) {
|
|
Client client(mDevice);
|
|
setRoundTrip(true, 1, 1);
|
|
char element = (char)(ASG_TEST_WRITE_PATTERN);
|
|
char reply;
|
|
|
|
auto buf = client.allocBuffer(1);
|
|
*buf = element;
|
|
client.flush();
|
|
client.speculativeRead(&reply, 1);
|
|
}
|
|
|
|
// Round trip test (more than one)
|
|
TEST_F(AddressSpaceGraphicsTest, RoundTrips) {
|
|
Client client(mDevice);
|
|
|
|
std::vector<RoundTrip> trips = {
|
|
{ 1, 1, },
|
|
{ 2, 2, },
|
|
{ 4, 4, },
|
|
{ 1026, 34, },
|
|
{ 4, 1048576, },
|
|
};
|
|
|
|
runRoundTrips(client, trips);
|
|
}
|
|
|
|
// Round trip test (random)
|
|
TEST_F(AddressSpaceGraphicsTest, RoundTripsRandom) {
|
|
Client client(mDevice);
|
|
|
|
std::default_random_engine generator;
|
|
generator.seed(0);
|
|
std::uniform_int_distribution<int>
|
|
sizeDist(1, 4097);
|
|
std::vector<RoundTrip> trips;
|
|
for (uint32_t i = 0; i < 1000; ++i) {
|
|
trips.push_back({
|
|
(size_t)sizeDist(generator),
|
|
(size_t)sizeDist(generator),
|
|
});
|
|
};
|
|
|
|
runRoundTrips(client, trips);
|
|
}
|
|
|
|
// Abort test. Say that we are reading back 4096
|
|
// bytes, but only actually read back 1 then abort.
|
|
TEST_F(AddressSpaceGraphicsTest, Abort) {
|
|
Client client(mDevice);
|
|
setRoundTrip(true, 1, 1048576);
|
|
|
|
char send = ASG_TEST_WRITE_PATTERN;
|
|
auto buf = client.allocBuffer(1);
|
|
*buf = send;
|
|
client.flush();
|
|
client.abort();
|
|
}
|
|
|
|
// Test having to create more than one block, and
|
|
// ensure traffic works each time.
|
|
TEST_F(AddressSpaceGraphicsTest, BlockCreateDestroy) {
|
|
|
|
std::vector<Client*> clients;
|
|
|
|
std::default_random_engine generator;
|
|
generator.seed(0);
|
|
std::uniform_int_distribution<int>
|
|
sizeDist(1, 47);
|
|
std::vector<RoundTrip> trips;
|
|
for (uint32_t i = 0; i < 100; ++i) {
|
|
trips.push_back({
|
|
(size_t)sizeDist(generator),
|
|
(size_t)sizeDist(generator),
|
|
});
|
|
};
|
|
|
|
int numBlocksMax = 3;
|
|
int numBlocksDetected = 0;
|
|
char* bufLow = (char*)(uintptr_t)(-1);
|
|
char* bufHigh = 0;
|
|
|
|
while (true) {
|
|
Client* c = new Client(mDevice);
|
|
runRoundTrips(*c, trips);
|
|
|
|
clients.push_back(c);
|
|
|
|
char* bufPtr = c->getBufferPtr();
|
|
bufLow = bufPtr < bufLow ? bufPtr : bufLow;
|
|
bufHigh = bufPtr > bufHigh ? bufPtr : bufHigh;
|
|
|
|
size_t gap = bufHigh - bufLow;
|
|
|
|
numBlocksDetected =
|
|
gap / ADDRESS_SPACE_GRAPHICS_BLOCK_SIZE;
|
|
|
|
if (numBlocksDetected > numBlocksMax) break;
|
|
}
|
|
|
|
for (auto c: clients) {
|
|
delete c;
|
|
}
|
|
}
|
|
|
|
// Test having to create more than one block, and
|
|
// ensure traffic works each time, but also randomly
|
|
// delete previous allocs to cause fragmentation.
|
|
TEST_F(AddressSpaceGraphicsTest, BlockCreateDestroyRandom) {
|
|
std::vector<Client*> clients;
|
|
|
|
std::default_random_engine generator;
|
|
generator.seed(0);
|
|
|
|
std::uniform_int_distribution<int>
|
|
sizeDist(1, 89);
|
|
std::bernoulli_distribution
|
|
deleteDist(0.2);
|
|
|
|
std::vector<RoundTrip> trips;
|
|
for (uint32_t i = 0; i < 100; ++i) {
|
|
trips.push_back({
|
|
(size_t)sizeDist(generator),
|
|
(size_t)sizeDist(generator),
|
|
});
|
|
};
|
|
|
|
int numBlocksMax = 3;
|
|
int numBlocksDetected = 0;
|
|
char* bufLow = (char*)(uintptr_t)(-1);
|
|
char* bufHigh = 0;
|
|
|
|
while (true) {
|
|
Client* c = new Client(mDevice);
|
|
runRoundTrips(*c, trips);
|
|
|
|
clients.push_back(c);
|
|
|
|
char* bufPtr = c->getBufferPtr();
|
|
bufLow = bufPtr < bufLow ? bufPtr : bufLow;
|
|
bufHigh = bufPtr > bufHigh ? bufPtr : bufHigh;
|
|
|
|
size_t gap = bufHigh - bufLow;
|
|
|
|
numBlocksDetected =
|
|
gap / ADDRESS_SPACE_GRAPHICS_BLOCK_SIZE;
|
|
|
|
if (numBlocksDetected > numBlocksMax) break;
|
|
|
|
if (deleteDist(generator)) {
|
|
delete c;
|
|
clients[clients.size() - 1] = 0;
|
|
}
|
|
}
|
|
|
|
for (auto c: clients) {
|
|
delete c;
|
|
}
|
|
}
|
|
|
|
} // namespace asg
|
|
} // namespace emulation
|
|
} // namespace android
|