blob: 53c469652f8f9fb2c68a1b00cb09f4badd1a51d3 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <chrono>
#include <queue>
#include <vulkan/vulkan.hpp>
#include "gtest/gtest.h"
#include "lib/escher/impl/vk/pipeline_cache.h"
#include "lib/escher/impl/vk/pipeline_factory.h"
#include "lib/fxl/logging.h"
namespace escher {
namespace impl {
namespace {
class TestPipelineFactory : public PipelineFactory {
public:
TestPipelineFactory() {}
// Enqueue a request, but don't immediately create a new Pipeline.
std::future<PipelinePtr> NewPipeline(PipelineSpec spec) override;
// Service one of the queued requests.
void ServiceOneRequest();
private:
struct Request {
PipelineSpec spec;
std::promise<PipelinePtr> promise;
};
std::queue<Request> requests_;
std::mutex mutex_;
};
std::future<PipelinePtr> TestPipelineFactory::NewPipeline(PipelineSpec spec) {
std::lock_guard<std::mutex> lock(mutex_);
Request request = {spec, std::promise<PipelinePtr>()};
auto future = request.promise.get_future();
requests_.push(std::move(request));
return future;
}
void TestPipelineFactory::ServiceOneRequest() {
std::lock_guard<std::mutex> lock(mutex_);
if (requests_.empty())
return;
Request request = std::move(requests_.front());
requests_.pop();
VkPipelineLayout fake_layout;
VkPipeline fake_pipeline;
reinterpret_cast<uint32_t*>(&fake_layout)[0] = 1U;
reinterpret_cast<uint32_t*>(&fake_pipeline)[0] = 1U;
auto layout = fxl::MakeRefCounted<PipelineLayout>(nullptr, fake_layout);
auto pipeline = fxl::MakeRefCounted<Pipeline>(nullptr, fake_pipeline, layout,
request.spec);
request.promise.set_value(pipeline);
}
// This test is disabled because it hangs on arm64.
// TODO(ES-34): un-disable when possible.
TEST(PipelineCache, DISABLED_SuperComprehensive) {
PipelineCache cache;
auto factory = fxl::MakeRefCounted<TestPipelineFactory>();
PipelineSpec spec1(1, {});
PipelineSpec spec2(1, {1, 2, 3});
PipelineSpec spec3(2, {1, 2, 3});
auto req1_1 = cache.GetPipeline(spec1, factory);
auto req1_2 = cache.GetPipeline(spec1, factory);
// Servicing one factory request resolves both cache requests for spec1, but
// none of the cache/factory requests for spec2/spec3.
std::chrono::milliseconds msecs(10);
EXPECT_EQ(std::future_status::timeout, req1_1.wait_for(msecs));
EXPECT_EQ(std::future_status::timeout, req1_2.wait_for(msecs));
factory->ServiceOneRequest();
auto req2_1 = cache.GetPipeline(spec2, factory);
auto req2_2 = cache.GetPipeline(spec2, factory);
auto req3_1 = cache.GetPipeline(spec3, factory);
auto req3_2 = cache.GetPipeline(spec3, factory);
EXPECT_EQ(std::future_status::ready, req1_1.wait_for(msecs));
EXPECT_EQ(std::future_status::ready, req1_2.wait_for(msecs));
EXPECT_EQ(std::future_status::timeout, req2_1.wait_for(msecs));
EXPECT_EQ(std::future_status::timeout, req2_2.wait_for(msecs));
// Servicing two more factory requests resolves the outstanding cache
// requests.
factory->ServiceOneRequest();
factory->ServiceOneRequest();
EXPECT_EQ(std::future_status::ready, req2_1.wait_for(msecs));
EXPECT_EQ(std::future_status::ready, req2_2.wait_for(msecs));
EXPECT_EQ(std::future_status::ready, req3_1.wait_for(msecs));
EXPECT_EQ(std::future_status::ready, req3_2.wait_for(msecs));
// Get the PipelinePtrs, and sanity-check them.
auto p1_1 = req1_1.get();
auto p1_2 = req1_2.get();
auto p2_1 = req2_1.get();
auto p2_2 = req2_2.get();
auto p3_2 = req3_2.get();
auto p3_1 = req3_1.get();
EXPECT_EQ(p1_1, p1_2);
EXPECT_EQ(p2_1, p2_2);
EXPECT_EQ(p3_1, p3_2);
EXPECT_NE(p1_1, p2_1);
EXPECT_NE(p2_1, p3_1);
EXPECT_EQ(spec1, p1_1->spec());
EXPECT_EQ(spec2, p2_1->spec());
EXPECT_EQ(spec3, p3_1->spec());
EXPECT_NE(p1_1->spec(), p2_1->spec());
EXPECT_NE(p2_1->spec(), p3_1->spec());
}
} // namespace
} // namespace impl
} // namespace escher