NXTTest: add -w to run the end2end tests through the wire

This will be useful to provide additional coverage of the wire in
addition to the very focused WireTests unittests. All tests pass except
the MapWriteAsync ones that aren't implemented in the wire yet. They can
be skipped with --gtest_filter=-*MapWrite*
This commit is contained in:
Corentin Wallez 2018-06-07 13:10:44 +02:00 committed by Corentin Wallez
parent 862f884a65
commit 419e9841a8
4 changed files with 151 additions and 58 deletions

View File

@ -90,5 +90,5 @@ add_executable(nxt_end2end_tests
${TESTS_DIR}/NXTTest.cpp
${TESTS_DIR}/NXTTest.h
)
target_link_libraries(nxt_end2end_tests nxt_common gtest utils)
target_link_libraries(nxt_end2end_tests nxt_common nxt_wire gtest utils)
NXTInternalTarget("tests" nxt_end2end_tests)

View File

@ -14,7 +14,23 @@
#include <gtest/gtest.h>
extern bool gTestUsesWire;
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
for (int i = 1; i < argc; ++i) {
if (strcmp("-w", argv[i]) == 0 || strcmp("--use-wire", argv[i]) == 0) {
gTestUsesWire = true;
continue;
}
if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
printf("\n\nUsage: %s [GTEST_FLAGS...] [-w] \n", argv[0]);
printf(" -w, --use-wire: Run the tests through the wire (defaults to no wire)\n");
return 0;
}
}
return RUN_ALL_TESTS();
}

View File

@ -20,9 +20,12 @@
#include "utils/BackendBinding.h"
#include "utils/NXTHelpers.h"
#include "utils/SystemUtils.h"
#include "wire/TerribleCommandBuffer.h"
#include "wire/Wire.h"
#include <iostream>
#include "GLFW/glfw3.h"
namespace {
utils::BackendType ParamToBackendType(BackendType type) {
@ -122,6 +125,8 @@ bool NXTTest::IsVulkan() const {
return GetParam() == VulkanBackend;
}
bool gTestUsesWire = false;
void NXTTest::SetUp() {
mBinding = utils::CreateBinding(ParamToBackendType(GetParam()));
NXT_ASSERT(mBinding != nullptr);
@ -135,20 +140,50 @@ void NXTTest::SetUp() {
nxtProcTable backendProcs;
mBinding->GetProcAndDevice(&backendProcs, &backendDevice);
nxtSetProcs(&backendProcs);
device = nxt::Device::Acquire(backendDevice);
// Choose whether to use the backend procs and devices directly, or set up the wire.
nxtDevice cDevice = nullptr;
nxtProcTable procs;
if (gTestUsesWire) {
mC2sBuf = new nxt::wire::TerribleCommandBuffer();
mS2cBuf = new nxt::wire::TerribleCommandBuffer();
mWireServer = nxt::wire::NewServerCommandHandler(backendDevice, backendProcs, mS2cBuf);
mC2sBuf->SetHandler(mWireServer);
nxtDevice clientDevice;
nxtProcTable clientProcs;
mWireClient = nxt::wire::NewClientDevice(&clientProcs, &clientDevice, mC2sBuf);
mS2cBuf->SetHandler(mWireClient);
procs = clientProcs;
cDevice = clientDevice;
} else {
procs = backendProcs;
cDevice = backendDevice;
}
// Set up the device and queue because all tests need them, and NXTTest needs them too for the
// deferred expectations.
nxtSetProcs(&procs);
device = nxt::Device::Acquire(cDevice);
queue = device.CreateQueueBuilder().GetResult();
// The swapchain isn't used by tests but is useful when debugging with graphics debuggers that
// capture at frame boundaries.
swapchain = device.CreateSwapChainBuilder()
.SetImplementation(mBinding->GetSwapChainImplementation())
.GetResult();
swapchain.Configure(static_cast<nxt::TextureFormat>(mBinding->GetPreferredSwapChainTextureFormat()),
nxt::TextureUsageBit::OutputAttachment, 400, 400);
// The end2end tests should never cause validation errors. These should be tested in unittests.
device.SetErrorCallback(DeviceErrorCauseTestFailure, 0);
}
void NXTTest::TearDown() {
FlushWire();
MapSlotsSynchronously();
ResolveExpectations();
@ -160,6 +195,13 @@ void NXTTest::TearDown() {
delete expectation.expectation;
expectation.expectation = nullptr;
}
if (gTestUsesWire) {
delete mC2sBuf;
delete mS2cBuf;
delete mWireClient;
delete mWireServer;
}
}
std::ostringstream& NXTTest::AddBufferExpectation(const char* file, int line, const nxt::Buffer& buffer, uint32_t offset, uint32_t size, detail::Expectation* expectation) {
@ -226,6 +268,8 @@ std::ostringstream& NXTTest::AddTextureExpectation(const char* file, int line, c
void NXTTest::WaitABit() {
device.Tick();
FlushWire();
utils::USleep(100);
}
@ -236,6 +280,13 @@ void NXTTest::SwapBuffersForCapture() {
swapchain.Present(backBuffer);
}
void NXTTest::FlushWire() {
if (gTestUsesWire) {
mC2sBuf->Flush();
mS2cBuf->Flush();
}
}
NXTTest::ReadbackReservation NXTTest::ReserveReadback(uint32_t readbackSize) {
// For now create a new MapRead buffer for each readback
// TODO(cwallez@chromium.org): eventually make bigger buffers and allocate linearly?

View File

@ -66,74 +66,100 @@ namespace detail {
class Expectation;
}
namespace nxt { namespace wire {
class CommandHandler;
class TerribleCommandBuffer;
}} // namespace nxt::wire
class NXTTest : public ::testing::TestWithParam<BackendType> {
public:
~NXTTest();
public:
~NXTTest();
void SetUp() override;
void TearDown() override;
void SetUp() override;
void TearDown() override;
bool IsD3D12() const;
bool IsMetal() const;
bool IsOpenGL() const;
bool IsVulkan() const;
bool IsD3D12() const;
bool IsMetal() const;
bool IsOpenGL() const;
bool IsVulkan() const;
protected:
nxt::Device device;
nxt::Queue queue;
nxt::SwapChain swapchain;
protected:
nxt::Device device;
nxt::Queue queue;
nxt::SwapChain swapchain;
// Helper methods to implement the EXPECT_ macros
std::ostringstream& AddBufferExpectation(const char* file, int line, const nxt::Buffer& buffer, uint32_t offset, uint32_t size, detail::Expectation* expectation);
std::ostringstream& AddTextureExpectation(const char* file, int line, const nxt::Texture& texture, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t level, uint32_t pixelSize, detail::Expectation* expectation);
// Helper methods to implement the EXPECT_ macros
std::ostringstream& AddBufferExpectation(const char* file,
int line,
const nxt::Buffer& buffer,
uint32_t offset,
uint32_t size,
detail::Expectation* expectation);
std::ostringstream& AddTextureExpectation(const char* file,
int line,
const nxt::Texture& texture,
uint32_t x,
uint32_t y,
uint32_t width,
uint32_t height,
uint32_t level,
uint32_t pixelSize,
detail::Expectation* expectation);
void WaitABit();
void WaitABit();
void SwapBuffersForCapture();
void SwapBuffersForCapture();
private:
// MapRead buffers used to get data for the expectations
struct ReadbackSlot {
nxt::Buffer buffer;
uint32_t bufferSize;
const void* mappedData = nullptr;
};
std::vector<ReadbackSlot> mReadbackSlots;
private:
// Things used to set up testing through the Wire.
nxt::wire::CommandHandler* mWireServer = nullptr;
nxt::wire::CommandHandler* mWireClient = nullptr;
nxt::wire::TerribleCommandBuffer* mC2sBuf = nullptr;
nxt::wire::TerribleCommandBuffer* mS2cBuf = nullptr;
void FlushWire();
// Maps all the buffers and fill ReadbackSlot::mappedData
void MapSlotsSynchronously();
static void SlotMapReadCallback(nxtBufferMapAsyncStatus status,
const void* data,
nxtCallbackUserdata userdata);
size_t mNumPendingMapOperations = 0;
// MapRead buffers used to get data for the expectations
struct ReadbackSlot {
nxt::Buffer buffer;
uint32_t bufferSize;
const void* mappedData = nullptr;
};
std::vector<ReadbackSlot> mReadbackSlots;
// Reserve space where the data for an expectation can be copied
struct ReadbackReservation {
nxt::Buffer buffer;
size_t slot;
uint32_t offset;
};
ReadbackReservation ReserveReadback(uint32_t readbackSize);
// Maps all the buffers and fill ReadbackSlot::mappedData
void MapSlotsSynchronously();
static void SlotMapReadCallback(nxtBufferMapAsyncStatus status,
const void* data,
nxtCallbackUserdata userdata);
size_t mNumPendingMapOperations = 0;
struct DeferredExpectation {
const char* file;
int line;
size_t readbackSlot;
uint32_t readbackOffset;
uint32_t size;
uint32_t rowBytes;
uint32_t rowPitch;
detail::Expectation* expectation;
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316
// Use unique_ptr because of missing move/copy constructors on std::basic_ostringstream
std::unique_ptr<std::ostringstream> message;
};
std::vector<DeferredExpectation> mDeferredExpectations;
// Reserve space where the data for an expectation can be copied
struct ReadbackReservation {
nxt::Buffer buffer;
size_t slot;
uint32_t offset;
};
ReadbackReservation ReserveReadback(uint32_t readbackSize);
// Assuming the data is mapped, checks all expectations
void ResolveExpectations();
struct DeferredExpectation {
const char* file;
int line;
size_t readbackSlot;
uint32_t readbackOffset;
uint32_t size;
uint32_t rowBytes;
uint32_t rowPitch;
detail::Expectation* expectation;
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316
// Use unique_ptr because of missing move/copy constructors on std::basic_ostringstream
std::unique_ptr<std::ostringstream> message;
};
std::vector<DeferredExpectation> mDeferredExpectations;
utils::BackendBinding* mBinding = nullptr;
// Assuming the data is mapped, checks all expectations
void ResolveExpectations();
utils::BackendBinding* mBinding = nullptr;
};
// Instantiate the test once for each backend provided after the first argument. Use it like this: