mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-14 19:31:25 +00:00
By default, the tests will run on all available adapters, so this adds an --exclusive-device-type-preference flag which takes a list of comma-delimited device type preferences (discrete,integrated,cpu). Tests will run only on the first available device type. This is useful because in Chromium's test infrastructure, the same test arguments are passed to one machine on which we want to use the discrete GPU, as well as one machine where we want to use the integrated GPU. Bug: dawn:396 Change-Id: Id936fff3356eef3c6d12dfd1407b0e1f0f020dc1 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/21202 Commit-Queue: Austin Eng <enga@chromium.org> Reviewed-by: Stephen White <senorblanco@chromium.org>
1141 lines
42 KiB
C++
1141 lines
42 KiB
C++
// Copyright 2017 The Dawn Authors
|
|
//
|
|
// 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 "tests/DawnTest.h"
|
|
|
|
#include "common/Assert.h"
|
|
#include "common/GPUInfo.h"
|
|
#include "common/Log.h"
|
|
#include "common/Math.h"
|
|
#include "common/Platform.h"
|
|
#include "common/SystemUtils.h"
|
|
#include "dawn/dawn_proc.h"
|
|
#include "dawn_native/DawnNative.h"
|
|
#include "dawn_wire/WireClient.h"
|
|
#include "dawn_wire/WireServer.h"
|
|
#include "utils/SystemUtils.h"
|
|
#include "utils/TerribleCommandBuffer.h"
|
|
#include "utils/WGPUHelpers.h"
|
|
|
|
#include <algorithm>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <regex>
|
|
#include <sstream>
|
|
#include <unordered_map>
|
|
|
|
#ifdef DAWN_ENABLE_BACKEND_OPENGL
|
|
# include "GLFW/glfw3.h"
|
|
# include "dawn_native/OpenGLBackend.h"
|
|
#endif // DAWN_ENABLE_BACKEND_OPENGL
|
|
|
|
namespace {
|
|
|
|
std::string ParamName(wgpu::BackendType type) {
|
|
switch (type) {
|
|
case wgpu::BackendType::D3D12:
|
|
return "D3D12";
|
|
case wgpu::BackendType::Metal:
|
|
return "Metal";
|
|
case wgpu::BackendType::Null:
|
|
return "Null";
|
|
case wgpu::BackendType::OpenGL:
|
|
return "OpenGL";
|
|
case wgpu::BackendType::Vulkan:
|
|
return "Vulkan";
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
const char* AdapterTypeName(wgpu::AdapterType type) {
|
|
switch (type) {
|
|
case wgpu::AdapterType::DiscreteGPU:
|
|
return "Discrete GPU";
|
|
case wgpu::AdapterType::IntegratedGPU:
|
|
return "Integrated GPU";
|
|
case wgpu::AdapterType::CPU:
|
|
return "CPU";
|
|
case wgpu::AdapterType::Unknown:
|
|
return "Unknown";
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
struct MapReadUserdata {
|
|
DawnTestBase* test;
|
|
size_t slot;
|
|
};
|
|
|
|
DawnTestEnvironment* gTestEnv = nullptr;
|
|
|
|
} // namespace
|
|
|
|
const RGBA8 RGBA8::kZero = RGBA8(0, 0, 0, 0);
|
|
const RGBA8 RGBA8::kBlack = RGBA8(0, 0, 0, 255);
|
|
const RGBA8 RGBA8::kRed = RGBA8(255, 0, 0, 255);
|
|
const RGBA8 RGBA8::kGreen = RGBA8(0, 255, 0, 255);
|
|
const RGBA8 RGBA8::kBlue = RGBA8(0, 0, 255, 255);
|
|
const RGBA8 RGBA8::kYellow = RGBA8(255, 255, 0, 255);
|
|
const RGBA8 RGBA8::kWhite = RGBA8(255, 255, 255, 255);
|
|
|
|
BackendTestConfig::BackendTestConfig(wgpu::BackendType backendType,
|
|
std::initializer_list<const char*> forceEnabledWorkarounds,
|
|
std::initializer_list<const char*> forceDisabledWorkarounds)
|
|
: backendType(backendType),
|
|
forceEnabledWorkarounds(forceEnabledWorkarounds),
|
|
forceDisabledWorkarounds(forceDisabledWorkarounds) {
|
|
}
|
|
|
|
BackendTestConfig D3D12Backend(std::initializer_list<const char*> forceEnabledWorkarounds,
|
|
std::initializer_list<const char*> forceDisabledWorkarounds) {
|
|
return BackendTestConfig(wgpu::BackendType::D3D12, forceEnabledWorkarounds,
|
|
forceDisabledWorkarounds);
|
|
}
|
|
|
|
BackendTestConfig MetalBackend(std::initializer_list<const char*> forceEnabledWorkarounds,
|
|
std::initializer_list<const char*> forceDisabledWorkarounds) {
|
|
return BackendTestConfig(wgpu::BackendType::Metal, forceEnabledWorkarounds,
|
|
forceDisabledWorkarounds);
|
|
}
|
|
|
|
BackendTestConfig NullBackend(std::initializer_list<const char*> forceEnabledWorkarounds,
|
|
std::initializer_list<const char*> forceDisabledWorkarounds) {
|
|
return BackendTestConfig(wgpu::BackendType::Null, forceEnabledWorkarounds,
|
|
forceDisabledWorkarounds);
|
|
}
|
|
|
|
BackendTestConfig OpenGLBackend(std::initializer_list<const char*> forceEnabledWorkarounds,
|
|
std::initializer_list<const char*> forceDisabledWorkarounds) {
|
|
return BackendTestConfig(wgpu::BackendType::OpenGL, forceEnabledWorkarounds,
|
|
forceDisabledWorkarounds);
|
|
}
|
|
|
|
BackendTestConfig VulkanBackend(std::initializer_list<const char*> forceEnabledWorkarounds,
|
|
std::initializer_list<const char*> forceDisabledWorkarounds) {
|
|
return BackendTestConfig(wgpu::BackendType::Vulkan, forceEnabledWorkarounds,
|
|
forceDisabledWorkarounds);
|
|
}
|
|
|
|
TestAdapterProperties::TestAdapterProperties(const wgpu::AdapterProperties& properties,
|
|
bool selected)
|
|
: wgpu::AdapterProperties(properties), adapterName(properties.name), selected(selected) {
|
|
}
|
|
|
|
AdapterTestParam::AdapterTestParam(const BackendTestConfig& config,
|
|
const TestAdapterProperties& adapterProperties)
|
|
: adapterProperties(adapterProperties),
|
|
forceEnabledWorkarounds(config.forceEnabledWorkarounds),
|
|
forceDisabledWorkarounds(config.forceDisabledWorkarounds) {
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const AdapterTestParam& param) {
|
|
// Sanitize the adapter name for GoogleTest
|
|
std::string sanitizedName =
|
|
std::regex_replace(param.adapterProperties.adapterName, std::regex("[^a-zA-Z0-9]+"), "_");
|
|
|
|
// Strip trailing underscores, if any.
|
|
if (sanitizedName.back() == '_') {
|
|
sanitizedName.back() = '\0';
|
|
}
|
|
|
|
os << ParamName(param.adapterProperties.backendType) << "_" << sanitizedName.c_str();
|
|
for (const char* forceEnabledWorkaround : param.forceEnabledWorkarounds) {
|
|
os << "__e_" << forceEnabledWorkaround;
|
|
}
|
|
for (const char* forceDisabledWorkaround : param.forceDisabledWorkarounds) {
|
|
os << "__d_" << forceDisabledWorkaround;
|
|
}
|
|
return os;
|
|
}
|
|
|
|
// Implementation of DawnTestEnvironment
|
|
|
|
void InitDawnEnd2EndTestEnvironment(int argc, char** argv) {
|
|
gTestEnv = new DawnTestEnvironment(argc, argv);
|
|
testing::AddGlobalTestEnvironment(gTestEnv);
|
|
}
|
|
|
|
// static
|
|
void DawnTestEnvironment::SetEnvironment(DawnTestEnvironment* env) {
|
|
gTestEnv = env;
|
|
}
|
|
|
|
DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) {
|
|
ParseArgs(argc, argv);
|
|
|
|
// Create a temporary instance to select available and preferred adapters. This is done before
|
|
// test instantiation so GetAvailableAdapterTestParamsForBackends can generate test
|
|
// parameterizations all selected adapters. We drop the instance at the end of this function
|
|
// because the Vulkan validation layers use static global mutexes which behave badly when
|
|
// Chromium's test launcher forks the test process. The instance will be recreated on test
|
|
// environment setup.
|
|
std::unique_ptr<dawn_native::Instance> instance = CreateInstanceAndDiscoverAdapters();
|
|
ASSERT(instance);
|
|
|
|
SelectPreferredAdapterProperties(instance.get());
|
|
PrintTestConfigurationAndAdapterInfo();
|
|
}
|
|
|
|
void DawnTestEnvironment::ParseArgs(int argc, char** argv) {
|
|
size_t argLen = 0; // Set when parsing --arg=X arguments
|
|
for (int i = 1; i < argc; ++i) {
|
|
if (strcmp("-w", argv[i]) == 0 || strcmp("--use-wire", argv[i]) == 0) {
|
|
mUseWire = true;
|
|
continue;
|
|
}
|
|
|
|
if (strcmp("-d", argv[i]) == 0 || strcmp("--enable-backend-validation", argv[i]) == 0) {
|
|
mEnableBackendValidation = true;
|
|
continue;
|
|
}
|
|
|
|
if (strcmp("-c", argv[i]) == 0 || strcmp("--begin-capture-on-startup", argv[i]) == 0) {
|
|
mBeginCaptureOnStartup = true;
|
|
continue;
|
|
}
|
|
|
|
if (strcmp("--skip-validation", argv[i]) == 0) {
|
|
mSkipDawnValidation = true;
|
|
continue;
|
|
}
|
|
|
|
if (strcmp("--use-spvc", argv[i]) == 0) {
|
|
if (mSpvcFlagSeen) {
|
|
dawn::WarningLog() << "Multiple flags passed in that force whether or not to use "
|
|
"the spvc. This may lead to unexpected behaviour.";
|
|
}
|
|
ASSERT(!mSpvcFlagSeen);
|
|
|
|
mUseSpvc = true;
|
|
mSpvcFlagSeen = true;
|
|
continue;
|
|
}
|
|
|
|
if (strcmp("--no-use-spvc", argv[i]) == 0) {
|
|
if (mSpvcFlagSeen) {
|
|
dawn::WarningLog() << "Multiple flags passed in that force whether or not to use "
|
|
"the spvc. This may lead to unexpected behaviour.";
|
|
}
|
|
ASSERT(!mSpvcFlagSeen);
|
|
|
|
mUseSpvc = false;
|
|
mSpvcFlagSeen = true;
|
|
continue;
|
|
}
|
|
|
|
if (strcmp("--use-spvc-parser", argv[i]) == 0) {
|
|
if (mSpvcParserFlagSeen) {
|
|
dawn::WarningLog() << "Multiple flags passed in that force whether or not to use "
|
|
"the spvc parser. This may cause unexpected behaviour.";
|
|
}
|
|
ASSERT(!mSpvcParserFlagSeen);
|
|
|
|
if (!mUseSpvc) {
|
|
if (mSpvcFlagSeen) {
|
|
dawn::ErrorLog()
|
|
<< "Overriding force disabling of spvc since it is required for using the "
|
|
"spvc parser. This indicates a likely misconfiguration.";
|
|
} else {
|
|
dawn::InfoLog()
|
|
<< "Enabling spvc since it is required for using the spvc parser.";
|
|
}
|
|
ASSERT(!mSpvcFlagSeen);
|
|
}
|
|
|
|
mUseSpvc = true; // It's impossible to use the spvc parser without using spvc, so
|
|
// turning on mUseSpvc implicitly.
|
|
mUseSpvcParser = true;
|
|
mSpvcParserFlagSeen = true;
|
|
continue;
|
|
}
|
|
|
|
if (strcmp("--no-use-spvc-parser", argv[i]) == 0) {
|
|
if (mSpvcParserFlagSeen) {
|
|
dawn::WarningLog() << "Multiple flags passed in that force whether or not to use "
|
|
"the spvc parser. This may cause unexpected behaviour.";
|
|
}
|
|
ASSERT(!mSpvcParserFlagSeen);
|
|
|
|
// Intentionally not changing mUseSpvc, since the dependency is one-way. This will
|
|
// not correctly handle the case where spvc is off by default, then there is a spvc
|
|
// parser on flag followed by a off flag, but that is already being indicated as a
|
|
// misuse.
|
|
mUseSpvcParser = false;
|
|
mSpvcParserFlagSeen = true;
|
|
continue;
|
|
}
|
|
|
|
constexpr const char kVendorIdFilterArg[] = "--adapter-vendor-id=";
|
|
argLen = sizeof(kVendorIdFilterArg) - 1;
|
|
if (strncmp(argv[i], kVendorIdFilterArg, argLen) == 0) {
|
|
const char* vendorIdFilter = argv[i] + argLen;
|
|
if (vendorIdFilter[0] != '\0') {
|
|
mVendorIdFilter = strtoul(vendorIdFilter, nullptr, 16);
|
|
// Set filter flag if vendor id is non-zero.
|
|
mHasVendorIdFilter = mVendorIdFilter != 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
constexpr const char kExclusiveDeviceTypePreferenceArg[] =
|
|
"--exclusive-device-type-preference=";
|
|
argLen = sizeof(kExclusiveDeviceTypePreferenceArg) - 1;
|
|
if (strncmp(argv[i], kExclusiveDeviceTypePreferenceArg, argLen) == 0) {
|
|
const char* preference = argv[i] + argLen;
|
|
if (preference[0] != '\0') {
|
|
std::istringstream ss(preference);
|
|
std::string type;
|
|
while (std::getline(ss, type, ',')) {
|
|
if (strcmp(type.c_str(), "discrete") == 0) {
|
|
mDevicePreferences.push_back(dawn_native::DeviceType::DiscreteGPU);
|
|
} else if (strcmp(type.c_str(), "integrated") == 0) {
|
|
mDevicePreferences.push_back(dawn_native::DeviceType::IntegratedGPU);
|
|
} else if (strcmp(type.c_str(), "cpu") == 0) {
|
|
mDevicePreferences.push_back(dawn_native::DeviceType::CPU);
|
|
} else {
|
|
dawn::ErrorLog() << "Invalid device type preference: " << type;
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
constexpr const char kWireTraceDirArg[] = "--wire-trace-dir=";
|
|
argLen = sizeof(kWireTraceDirArg) - 1;
|
|
if (strncmp(argv[i], kWireTraceDirArg, argLen) == 0) {
|
|
const char* wireTraceDir = argv[i] + argLen;
|
|
if (wireTraceDir[0] != '\0') {
|
|
const char* sep = GetPathSeparator();
|
|
mWireTraceDir = wireTraceDir;
|
|
if (mWireTraceDir.back() != *sep) {
|
|
mWireTraceDir += sep;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
|
|
dawn::InfoLog()
|
|
<< "\n\nUsage: " << argv[0]
|
|
<< " [GTEST_FLAGS...] [-w] [-d] [-c] [--adapter-vendor-id=x]"
|
|
" [--exclusive-device-type-preference=integrated,cpu,discrete]\n"
|
|
" -w, --use-wire: Run the tests through the wire (defaults to no wire)\n"
|
|
" -d, --enable-backend-validation: Enable backend validation (defaults"
|
|
" to disabled)\n"
|
|
" -c, --begin-capture-on-startup: Begin debug capture on startup "
|
|
"(defaults to no capture)\n"
|
|
" --skip-validation: Skip Dawn validation\n"
|
|
" --use-spvc: Use spvc for accessing spirv-cross\n"
|
|
" --no-use-spvc: Do not use spvc for accessing spirv-cross\n"
|
|
" --use-spvc-parser: Use spvc's spir-v parsing insteads of spirv-cross's, "
|
|
"implies --use-spvc\n"
|
|
" --no-use-spvc-parser: Do no use spvc's spir-v parsing insteads of "
|
|
"spirv-cross's\n"
|
|
" --adapter-vendor-id: Select adapter by vendor id to run end2end tests"
|
|
"on multi-GPU systems \n"
|
|
" --exclusive-device-type-preference: Comma-delimited list of preferred device "
|
|
"types. For each backend, tests will run only on adapters that match the first "
|
|
"available device type\n";
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<dawn_native::Instance> DawnTestEnvironment::CreateInstanceAndDiscoverAdapters()
|
|
const {
|
|
auto instance = std::make_unique<dawn_native::Instance>();
|
|
instance->EnableBackendValidation(mEnableBackendValidation);
|
|
instance->EnableBeginCaptureOnStartup(mBeginCaptureOnStartup);
|
|
|
|
instance->DiscoverDefaultAdapters();
|
|
|
|
#ifdef DAWN_ENABLE_BACKEND_OPENGL
|
|
if (!glfwInit()) {
|
|
return instance;
|
|
}
|
|
glfwDefaultWindowHints();
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
|
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
|
|
std::string windowName = "Dawn OpenGL test window";
|
|
GLFWwindow* window = glfwCreateWindow(400, 400, windowName.c_str(), nullptr, nullptr);
|
|
|
|
glfwMakeContextCurrent(window);
|
|
dawn_native::opengl::AdapterDiscoveryOptions adapterOptions;
|
|
adapterOptions.getProc = reinterpret_cast<void* (*)(const char*)>(glfwGetProcAddress);
|
|
instance->DiscoverAdapters(&adapterOptions);
|
|
#endif // DAWN_ENABLE_BACKEND_OPENGL
|
|
|
|
return instance;
|
|
}
|
|
|
|
void DawnTestEnvironment::SelectPreferredAdapterProperties(const dawn_native::Instance* instance) {
|
|
// Get the first available preferred device type.
|
|
dawn_native::DeviceType preferredDeviceType = static_cast<dawn_native::DeviceType>(-1);
|
|
bool hasDevicePreference = false;
|
|
for (dawn_native::DeviceType devicePreference : mDevicePreferences) {
|
|
for (const dawn_native::Adapter& adapter : instance->GetAdapters()) {
|
|
wgpu::AdapterProperties properties;
|
|
adapter.GetProperties(&properties);
|
|
|
|
if (adapter.GetDeviceType() == devicePreference) {
|
|
preferredDeviceType = devicePreference;
|
|
hasDevicePreference = true;
|
|
break;
|
|
}
|
|
}
|
|
if (hasDevicePreference) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (const dawn_native::Adapter& adapter : instance->GetAdapters()) {
|
|
wgpu::AdapterProperties properties;
|
|
adapter.GetProperties(&properties);
|
|
|
|
// The adapter is selected if:
|
|
bool selected = false;
|
|
if (mHasVendorIdFilter) {
|
|
// It matches the vendor id, if present.
|
|
selected = mVendorIdFilter == properties.vendorID;
|
|
|
|
if (!mDevicePreferences.empty()) {
|
|
dawn::WarningLog() << "Vendor ID filter provided. Ignoring device type preference.";
|
|
}
|
|
} else if (hasDevicePreference) {
|
|
// There is a device preference and:
|
|
selected =
|
|
// The device type matches the first available preferred type for that backend, if
|
|
// present.
|
|
(adapter.GetDeviceType() == preferredDeviceType) ||
|
|
// Always select Unknown OpenGL adapters if we don't want a CPU adapter.
|
|
// OpenGL will usually be unknown because we can't query the device type.
|
|
// If we ever have Swiftshader GL (unlikely), we could set the DeviceType properly.
|
|
(preferredDeviceType != dawn_native::DeviceType::CPU &&
|
|
adapter.GetDeviceType() == dawn_native::DeviceType::Unknown &&
|
|
properties.backendType == wgpu::BackendType::OpenGL) ||
|
|
// Always select the Null backend. There are few tests on this backend, and they run
|
|
// quickly. This is temporary as to not lose coverage. We can group it with
|
|
// Swiftshader as a CPU adapter when we have Swiftshader tests.
|
|
(properties.backendType == wgpu::BackendType::Null);
|
|
} else {
|
|
// No vendor id or device preference was provided (select all).
|
|
selected = true;
|
|
}
|
|
|
|
mAdapterProperties.emplace_back(properties, selected);
|
|
}
|
|
}
|
|
|
|
std::vector<AdapterTestParam> DawnTestEnvironment::GetAvailableAdapterTestParamsForBackends(
|
|
const BackendTestConfig* params,
|
|
size_t numParams) {
|
|
std::vector<AdapterTestParam> testParams;
|
|
for (size_t i = 0; i < numParams; ++i) {
|
|
for (const auto& adapterProperties : mAdapterProperties) {
|
|
if (params[i].backendType == adapterProperties.backendType &&
|
|
adapterProperties.selected) {
|
|
testParams.push_back(AdapterTestParam(params[i], adapterProperties));
|
|
}
|
|
}
|
|
}
|
|
return testParams;
|
|
}
|
|
|
|
void DawnTestEnvironment::PrintTestConfigurationAndAdapterInfo() const {
|
|
dawn::LogMessage log = dawn::InfoLog();
|
|
log << "Testing configuration\n"
|
|
"---------------------\n"
|
|
"UseWire: "
|
|
<< (mUseWire ? "true" : "false")
|
|
<< "\n"
|
|
"EnableBackendValidation: "
|
|
<< (mEnableBackendValidation ? "true" : "false")
|
|
<< "\n"
|
|
"SkipDawnValidation: "
|
|
<< (mSkipDawnValidation ? "true" : "false")
|
|
<< "\n"
|
|
"UseSpvc: "
|
|
<< (mUseSpvc ? "true" : "false")
|
|
<< "\n"
|
|
"UseSpvcParser: "
|
|
<< (mUseSpvcParser ? "true" : "false")
|
|
<< "\n"
|
|
"BeginCaptureOnStartup: "
|
|
<< (mBeginCaptureOnStartup ? "true" : "false")
|
|
<< "\n"
|
|
"\n"
|
|
<< "System adapters: \n";
|
|
|
|
for (const TestAdapterProperties& properties : mAdapterProperties) {
|
|
std::ostringstream vendorId;
|
|
std::ostringstream deviceId;
|
|
vendorId << std::setfill('0') << std::uppercase << std::internal << std::hex << std::setw(4)
|
|
<< properties.vendorID;
|
|
deviceId << std::setfill('0') << std::uppercase << std::internal << std::hex << std::setw(4)
|
|
<< properties.deviceID;
|
|
|
|
// Preparing for outputting hex numbers
|
|
log << std::showbase << std::hex << std::setfill('0') << std::setw(4)
|
|
|
|
<< " - \"" << properties.adapterName << "\"\n"
|
|
<< " type: " << AdapterTypeName(properties.adapterType)
|
|
<< ", backend: " << ParamName(properties.backendType) << "\n"
|
|
<< " vendorId: 0x" << vendorId.str() << ", deviceId: 0x" << deviceId.str()
|
|
<< (properties.selected ? " [Selected]" : "") << "\n";
|
|
}
|
|
}
|
|
|
|
void DawnTestEnvironment::SetUp() {
|
|
mInstance = CreateInstanceAndDiscoverAdapters();
|
|
ASSERT(mInstance);
|
|
}
|
|
|
|
void DawnTestEnvironment::TearDown() {
|
|
// When Vulkan validation layers are enabled, it's unsafe to call Vulkan APIs in the destructor
|
|
// of a static/global variable, so the instance must be manually released beforehand.
|
|
mInstance.reset();
|
|
}
|
|
|
|
bool DawnTestEnvironment::UsesWire() const {
|
|
return mUseWire;
|
|
}
|
|
|
|
bool DawnTestEnvironment::IsBackendValidationEnabled() const {
|
|
return mEnableBackendValidation;
|
|
}
|
|
|
|
bool DawnTestEnvironment::IsDawnValidationSkipped() const {
|
|
return mSkipDawnValidation;
|
|
}
|
|
|
|
bool DawnTestEnvironment::IsSpvcBeingUsed() const {
|
|
return mUseSpvc;
|
|
}
|
|
|
|
bool DawnTestEnvironment::IsSpvcParserBeingUsed() const {
|
|
return mUseSpvcParser;
|
|
}
|
|
|
|
dawn_native::Instance* DawnTestEnvironment::GetInstance() const {
|
|
return mInstance.get();
|
|
}
|
|
|
|
bool DawnTestEnvironment::HasVendorIdFilter() const {
|
|
return mHasVendorIdFilter;
|
|
}
|
|
|
|
uint32_t DawnTestEnvironment::GetVendorIdFilter() const {
|
|
return mVendorIdFilter;
|
|
}
|
|
|
|
const char* DawnTestEnvironment::GetWireTraceDir() const {
|
|
if (mWireTraceDir.length() == 0) {
|
|
return nullptr;
|
|
}
|
|
return mWireTraceDir.c_str();
|
|
}
|
|
|
|
class WireServerTraceLayer : public dawn_wire::CommandHandler {
|
|
public:
|
|
WireServerTraceLayer(const char* file, dawn_wire::CommandHandler* handler)
|
|
: dawn_wire::CommandHandler(), mHandler(handler) {
|
|
mFile.open(file, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
|
|
}
|
|
|
|
const volatile char* HandleCommands(const volatile char* commands, size_t size) override {
|
|
mFile.write(const_cast<const char*>(commands), size);
|
|
return mHandler->HandleCommands(commands, size);
|
|
}
|
|
|
|
private:
|
|
dawn_wire::CommandHandler* mHandler;
|
|
std::ofstream mFile;
|
|
};
|
|
|
|
// Implementation of DawnTest
|
|
|
|
DawnTestBase::DawnTestBase(const AdapterTestParam& param) : mParam(param) {
|
|
}
|
|
|
|
DawnTestBase::~DawnTestBase() {
|
|
// We need to destroy child objects before the Device
|
|
mReadbackSlots.clear();
|
|
queue = wgpu::Queue();
|
|
device = wgpu::Device();
|
|
|
|
mWireClient = nullptr;
|
|
mWireServer = nullptr;
|
|
if (gTestEnv->UsesWire()) {
|
|
backendProcs.deviceRelease(backendDevice);
|
|
}
|
|
|
|
dawnProcSetProcs(nullptr);
|
|
}
|
|
|
|
bool DawnTestBase::IsD3D12() const {
|
|
return mParam.adapterProperties.backendType == wgpu::BackendType::D3D12;
|
|
}
|
|
|
|
bool DawnTestBase::IsMetal() const {
|
|
return mParam.adapterProperties.backendType == wgpu::BackendType::Metal;
|
|
}
|
|
|
|
bool DawnTestBase::IsNull() const {
|
|
return mParam.adapterProperties.backendType == wgpu::BackendType::Null;
|
|
}
|
|
|
|
bool DawnTestBase::IsOpenGL() const {
|
|
return mParam.adapterProperties.backendType == wgpu::BackendType::OpenGL;
|
|
}
|
|
|
|
bool DawnTestBase::IsVulkan() const {
|
|
return mParam.adapterProperties.backendType == wgpu::BackendType::Vulkan;
|
|
}
|
|
|
|
bool DawnTestBase::IsAMD() const {
|
|
return gpu_info::IsAMD(mParam.adapterProperties.vendorID);
|
|
}
|
|
|
|
bool DawnTestBase::IsARM() const {
|
|
return gpu_info::IsARM(mParam.adapterProperties.vendorID);
|
|
}
|
|
|
|
bool DawnTestBase::IsImgTec() const {
|
|
return gpu_info::IsImgTec(mParam.adapterProperties.vendorID);
|
|
}
|
|
|
|
bool DawnTestBase::IsIntel() const {
|
|
return gpu_info::IsIntel(mParam.adapterProperties.vendorID);
|
|
}
|
|
|
|
bool DawnTestBase::IsNvidia() const {
|
|
return gpu_info::IsNvidia(mParam.adapterProperties.vendorID);
|
|
}
|
|
|
|
bool DawnTestBase::IsQualcomm() const {
|
|
return gpu_info::IsQualcomm(mParam.adapterProperties.vendorID);
|
|
}
|
|
|
|
bool DawnTestBase::IsSwiftshader() const {
|
|
return gpu_info::IsSwiftshader(mParam.adapterProperties.vendorID,
|
|
mParam.adapterProperties.deviceID);
|
|
}
|
|
|
|
bool DawnTestBase::IsWindows() const {
|
|
#ifdef DAWN_PLATFORM_WINDOWS
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool DawnTestBase::IsLinux() const {
|
|
#ifdef DAWN_PLATFORM_LINUX
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool DawnTestBase::IsMacOS() const {
|
|
#ifdef DAWN_PLATFORM_APPLE
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool DawnTestBase::UsesWire() const {
|
|
return gTestEnv->UsesWire();
|
|
}
|
|
|
|
bool DawnTestBase::IsBackendValidationEnabled() const {
|
|
return gTestEnv->IsBackendValidationEnabled();
|
|
}
|
|
|
|
bool DawnTestBase::IsDawnValidationSkipped() const {
|
|
return gTestEnv->IsDawnValidationSkipped();
|
|
}
|
|
|
|
bool DawnTestBase::IsSpvcBeingUsed() const {
|
|
return gTestEnv->IsSpvcBeingUsed();
|
|
}
|
|
|
|
bool DawnTestBase::IsSpvcParserBeingUsed() const {
|
|
return gTestEnv->IsSpvcParserBeingUsed();
|
|
}
|
|
|
|
bool DawnTestBase::HasVendorIdFilter() const {
|
|
return gTestEnv->HasVendorIdFilter();
|
|
}
|
|
|
|
uint32_t DawnTestBase::GetVendorIdFilter() const {
|
|
return gTestEnv->GetVendorIdFilter();
|
|
}
|
|
|
|
wgpu::Instance DawnTestBase::GetInstance() const {
|
|
return gTestEnv->GetInstance()->Get();
|
|
}
|
|
|
|
dawn_native::Adapter DawnTestBase::GetAdapter() const {
|
|
return mBackendAdapter;
|
|
}
|
|
|
|
std::vector<const char*> DawnTestBase::GetRequiredExtensions() {
|
|
return {};
|
|
}
|
|
|
|
const wgpu::AdapterProperties& DawnTestBase::GetAdapterProperties() const {
|
|
return mParam.adapterProperties;
|
|
}
|
|
|
|
bool DawnTestBase::SupportsExtensions(const std::vector<const char*>& extensions) {
|
|
ASSERT(mBackendAdapter);
|
|
std::set<std::string> supportedExtensionsSet;
|
|
for (const char* supportedExtensionName : mBackendAdapter.GetSupportedExtensions()) {
|
|
supportedExtensionsSet.insert(supportedExtensionName);
|
|
}
|
|
|
|
for (const char* extensionName : extensions) {
|
|
if (supportedExtensionsSet.find(extensionName) == supportedExtensionsSet.end()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void DawnTestBase::SetUp() {
|
|
{
|
|
// Find the adapter that exactly matches our adapter properties.
|
|
const auto& adapters = gTestEnv->GetInstance()->GetAdapters();
|
|
const auto& it = std::find_if(
|
|
adapters.begin(), adapters.end(), [&](const dawn_native::Adapter& adapter) {
|
|
wgpu::AdapterProperties properties;
|
|
adapter.GetProperties(&properties);
|
|
|
|
return (mParam.adapterProperties.selected &&
|
|
properties.deviceID == mParam.adapterProperties.deviceID &&
|
|
properties.vendorID == mParam.adapterProperties.vendorID &&
|
|
properties.adapterType == mParam.adapterProperties.adapterType &&
|
|
properties.backendType == mParam.adapterProperties.backendType &&
|
|
strcmp(properties.name, mParam.adapterProperties.adapterName.c_str()) == 0);
|
|
});
|
|
ASSERT(it != adapters.end());
|
|
mBackendAdapter = *it;
|
|
}
|
|
|
|
// Create the device from the adapter
|
|
for (const char* forceEnabledWorkaround : mParam.forceEnabledWorkarounds) {
|
|
ASSERT(gTestEnv->GetInstance()->GetToggleInfo(forceEnabledWorkaround) != nullptr);
|
|
}
|
|
for (const char* forceDisabledWorkaround : mParam.forceDisabledWorkarounds) {
|
|
ASSERT(gTestEnv->GetInstance()->GetToggleInfo(forceDisabledWorkaround) != nullptr);
|
|
}
|
|
dawn_native::DeviceDescriptor deviceDescriptor;
|
|
deviceDescriptor.forceEnabledToggles = mParam.forceEnabledWorkarounds;
|
|
deviceDescriptor.forceDisabledToggles = mParam.forceDisabledWorkarounds;
|
|
deviceDescriptor.requiredExtensions = GetRequiredExtensions();
|
|
|
|
static constexpr char kSkipValidationToggle[] = "skip_validation";
|
|
if (gTestEnv->IsDawnValidationSkipped()) {
|
|
ASSERT(gTestEnv->GetInstance()->GetToggleInfo(kSkipValidationToggle) != nullptr);
|
|
deviceDescriptor.forceEnabledToggles.push_back(kSkipValidationToggle);
|
|
}
|
|
|
|
static constexpr char kUseSpvcToggle[] = "use_spvc";
|
|
static constexpr char kUseSpvcParserToggle[] = "use_spvc_parser";
|
|
if (gTestEnv->IsSpvcBeingUsed()) {
|
|
ASSERT(gTestEnv->GetInstance()->GetToggleInfo(kUseSpvcToggle) != nullptr);
|
|
deviceDescriptor.forceEnabledToggles.push_back(kUseSpvcToggle);
|
|
|
|
if (gTestEnv->IsSpvcParserBeingUsed()) {
|
|
ASSERT(gTestEnv->GetInstance()->GetToggleInfo(kUseSpvcParserToggle) != nullptr);
|
|
deviceDescriptor.forceEnabledToggles.push_back(kUseSpvcParserToggle);
|
|
}
|
|
|
|
} else {
|
|
// Turning on spvc parser should always turn on spvc.
|
|
ASSERT(!gTestEnv->IsSpvcParserBeingUsed());
|
|
ASSERT(gTestEnv->GetInstance()->GetToggleInfo(kUseSpvcToggle) != nullptr);
|
|
deviceDescriptor.forceDisabledToggles.push_back(kUseSpvcToggle);
|
|
}
|
|
|
|
backendDevice = mBackendAdapter.CreateDevice(&deviceDescriptor);
|
|
ASSERT_NE(nullptr, backendDevice);
|
|
|
|
backendProcs = dawn_native::GetProcs();
|
|
|
|
// Choose whether to use the backend procs and devices directly, or set up the wire.
|
|
WGPUDevice cDevice = nullptr;
|
|
DawnProcTable procs;
|
|
|
|
if (gTestEnv->UsesWire()) {
|
|
mC2sBuf = std::make_unique<utils::TerribleCommandBuffer>();
|
|
mS2cBuf = std::make_unique<utils::TerribleCommandBuffer>();
|
|
|
|
dawn_wire::WireServerDescriptor serverDesc = {};
|
|
serverDesc.device = backendDevice;
|
|
serverDesc.procs = &backendProcs;
|
|
serverDesc.serializer = mS2cBuf.get();
|
|
|
|
mWireServer.reset(new dawn_wire::WireServer(serverDesc));
|
|
mC2sBuf->SetHandler(mWireServer.get());
|
|
|
|
if (gTestEnv->GetWireTraceDir() != nullptr) {
|
|
std::string file =
|
|
std::string(
|
|
::testing::UnitTest::GetInstance()->current_test_info()->test_suite_name()) +
|
|
"_" + ::testing::UnitTest::GetInstance()->current_test_info()->name();
|
|
// Replace slashes in gtest names with underscores so everything is in one directory.
|
|
std::replace(file.begin(), file.end(), '/', '_');
|
|
|
|
std::string fullPath = gTestEnv->GetWireTraceDir() + file;
|
|
|
|
mWireServerTraceLayer.reset(
|
|
new WireServerTraceLayer(fullPath.c_str(), mWireServer.get()));
|
|
mC2sBuf->SetHandler(mWireServerTraceLayer.get());
|
|
}
|
|
|
|
dawn_wire::WireClientDescriptor clientDesc = {};
|
|
clientDesc.serializer = mC2sBuf.get();
|
|
|
|
mWireClient.reset(new dawn_wire::WireClient(clientDesc));
|
|
WGPUDevice clientDevice = mWireClient->GetDevice();
|
|
DawnProcTable clientProcs = dawn_wire::WireClient::GetProcs();
|
|
mS2cBuf->SetHandler(mWireClient.get());
|
|
|
|
procs = clientProcs;
|
|
cDevice = clientDevice;
|
|
} else {
|
|
procs = backendProcs;
|
|
cDevice = backendDevice;
|
|
}
|
|
|
|
// Set up the device and queue because all tests need them, and DawnTestBase needs them too for
|
|
// the deferred expectations.
|
|
dawnProcSetProcs(&procs);
|
|
device = wgpu::Device::Acquire(cDevice);
|
|
queue = device.GetDefaultQueue();
|
|
|
|
device.SetUncapturedErrorCallback(OnDeviceError, this);
|
|
device.SetDeviceLostCallback(OnDeviceLost, this);
|
|
}
|
|
|
|
void DawnTestBase::TearDown() {
|
|
FlushWire();
|
|
|
|
MapSlotsSynchronously();
|
|
ResolveExpectations();
|
|
|
|
for (size_t i = 0; i < mReadbackSlots.size(); ++i) {
|
|
mReadbackSlots[i].buffer.Unmap();
|
|
}
|
|
}
|
|
|
|
void DawnTestBase::StartExpectDeviceError() {
|
|
mExpectError = true;
|
|
mError = false;
|
|
}
|
|
bool DawnTestBase::EndExpectDeviceError() {
|
|
mExpectError = false;
|
|
return mError;
|
|
}
|
|
|
|
// static
|
|
void DawnTestBase::OnDeviceError(WGPUErrorType type, const char* message, void* userdata) {
|
|
ASSERT(type != WGPUErrorType_NoError);
|
|
DawnTestBase* self = static_cast<DawnTestBase*>(userdata);
|
|
|
|
ASSERT_TRUE(self->mExpectError) << "Got unexpected device error: " << message;
|
|
ASSERT_FALSE(self->mError) << "Got two errors in expect block";
|
|
self->mError = true;
|
|
}
|
|
|
|
void DawnTestBase::OnDeviceLost(const char* message, void* userdata) {
|
|
FAIL() << "Device Lost during test: " << message;
|
|
}
|
|
|
|
std::ostringstream& DawnTestBase::AddBufferExpectation(const char* file,
|
|
int line,
|
|
const wgpu::Buffer& buffer,
|
|
uint64_t offset,
|
|
uint64_t size,
|
|
detail::Expectation* expectation) {
|
|
auto readback = ReserveReadback(size);
|
|
|
|
// We need to enqueue the copy immediately because by the time we resolve the expectation,
|
|
// the buffer might have been modified.
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(buffer, offset, readback.buffer, readback.offset, size);
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
DeferredExpectation deferred;
|
|
deferred.file = file;
|
|
deferred.line = line;
|
|
deferred.readbackSlot = readback.slot;
|
|
deferred.readbackOffset = readback.offset;
|
|
deferred.size = size;
|
|
deferred.rowBytes = size;
|
|
deferred.bytesPerRow = size;
|
|
deferred.expectation.reset(expectation);
|
|
|
|
mDeferredExpectations.push_back(std::move(deferred));
|
|
mDeferredExpectations.back().message = std::make_unique<std::ostringstream>();
|
|
return *(mDeferredExpectations.back().message.get());
|
|
}
|
|
|
|
std::ostringstream& DawnTestBase::AddTextureExpectation(const char* file,
|
|
int line,
|
|
const wgpu::Texture& texture,
|
|
uint32_t x,
|
|
uint32_t y,
|
|
uint32_t width,
|
|
uint32_t height,
|
|
uint32_t level,
|
|
uint32_t slice,
|
|
uint32_t pixelSize,
|
|
detail::Expectation* expectation) {
|
|
uint32_t bytesPerRow = Align(width * pixelSize, kTextureBytesPerRowAlignment);
|
|
uint32_t size = bytesPerRow * (height - 1) + width * pixelSize;
|
|
|
|
auto readback = ReserveReadback(size);
|
|
|
|
// We need to enqueue the copy immediately because by the time we resolve the expectation,
|
|
// the texture might have been modified.
|
|
wgpu::TextureCopyView textureCopyView =
|
|
utils::CreateTextureCopyView(texture, level, slice, {x, y, 0});
|
|
wgpu::BufferCopyView bufferCopyView =
|
|
utils::CreateBufferCopyView(readback.buffer, readback.offset, bytesPerRow, 0);
|
|
wgpu::Extent3D copySize = {width, height, 1};
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size);
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
DeferredExpectation deferred;
|
|
deferred.file = file;
|
|
deferred.line = line;
|
|
deferred.readbackSlot = readback.slot;
|
|
deferred.readbackOffset = readback.offset;
|
|
deferred.size = size;
|
|
deferred.rowBytes = width * pixelSize;
|
|
deferred.bytesPerRow = bytesPerRow;
|
|
deferred.expectation.reset(expectation);
|
|
|
|
mDeferredExpectations.push_back(std::move(deferred));
|
|
mDeferredExpectations.back().message = std::make_unique<std::ostringstream>();
|
|
return *(mDeferredExpectations.back().message.get());
|
|
}
|
|
|
|
void DawnTestBase::WaitABit() {
|
|
device.Tick();
|
|
FlushWire();
|
|
|
|
utils::USleep(100);
|
|
}
|
|
|
|
void DawnTestBase::FlushWire() {
|
|
if (gTestEnv->UsesWire()) {
|
|
bool C2SFlushed = mC2sBuf->Flush();
|
|
bool S2CFlushed = mS2cBuf->Flush();
|
|
ASSERT(C2SFlushed);
|
|
ASSERT(S2CFlushed);
|
|
}
|
|
}
|
|
|
|
DawnTestBase::ReadbackReservation DawnTestBase::ReserveReadback(uint64_t readbackSize) {
|
|
// For now create a new MapRead buffer for each readback
|
|
// TODO(cwallez@chromium.org): eventually make bigger buffers and allocate linearly?
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = readbackSize;
|
|
descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
|
|
|
|
ReadbackSlot slot;
|
|
slot.bufferSize = readbackSize;
|
|
slot.buffer = device.CreateBuffer(&descriptor);
|
|
|
|
ReadbackReservation reservation;
|
|
reservation.buffer = slot.buffer;
|
|
reservation.slot = mReadbackSlots.size();
|
|
reservation.offset = 0;
|
|
|
|
mReadbackSlots.push_back(std::move(slot));
|
|
return reservation;
|
|
}
|
|
|
|
void DawnTestBase::MapSlotsSynchronously() {
|
|
// Initialize numPendingMapOperations before mapping, just in case the callback is called
|
|
// immediately.
|
|
mNumPendingMapOperations = mReadbackSlots.size();
|
|
|
|
// Map all readback slots
|
|
for (size_t i = 0; i < mReadbackSlots.size(); ++i) {
|
|
MapReadUserdata* userdata = new MapReadUserdata{this, i};
|
|
|
|
auto& slot = mReadbackSlots[i];
|
|
slot.buffer.MapReadAsync(SlotMapReadCallback, userdata);
|
|
}
|
|
|
|
// Busy wait until all map operations are done.
|
|
while (mNumPendingMapOperations != 0) {
|
|
WaitABit();
|
|
}
|
|
}
|
|
|
|
// static
|
|
void DawnTestBase::SlotMapReadCallback(WGPUBufferMapAsyncStatus status,
|
|
const void* data,
|
|
uint64_t,
|
|
void* userdata_) {
|
|
DAWN_ASSERT(status == WGPUBufferMapAsyncStatus_Success);
|
|
|
|
auto userdata = static_cast<MapReadUserdata*>(userdata_);
|
|
userdata->test->mReadbackSlots[userdata->slot].mappedData = data;
|
|
userdata->test->mNumPendingMapOperations--;
|
|
|
|
delete userdata;
|
|
}
|
|
|
|
void DawnTestBase::ResolveExpectations() {
|
|
for (const auto& expectation : mDeferredExpectations) {
|
|
DAWN_ASSERT(mReadbackSlots[expectation.readbackSlot].mappedData != nullptr);
|
|
|
|
// Get a pointer to the mapped copy of the data for the expectation.
|
|
const char* data =
|
|
static_cast<const char*>(mReadbackSlots[expectation.readbackSlot].mappedData);
|
|
data += expectation.readbackOffset;
|
|
|
|
uint32_t size;
|
|
std::vector<char> packedData;
|
|
if (expectation.rowBytes != expectation.bytesPerRow) {
|
|
DAWN_ASSERT(expectation.bytesPerRow > expectation.rowBytes);
|
|
uint32_t rowCount =
|
|
(expectation.size + expectation.bytesPerRow - 1) / expectation.bytesPerRow;
|
|
uint32_t packedSize = rowCount * expectation.rowBytes;
|
|
packedData.resize(packedSize);
|
|
for (uint32_t r = 0; r < rowCount; ++r) {
|
|
for (uint32_t i = 0; i < expectation.rowBytes; ++i) {
|
|
packedData[i + r * expectation.rowBytes] =
|
|
data[i + r * expectation.bytesPerRow];
|
|
}
|
|
}
|
|
data = packedData.data();
|
|
size = packedSize;
|
|
} else {
|
|
size = expectation.size;
|
|
}
|
|
|
|
// Get the result for the expectation and add context to failures
|
|
testing::AssertionResult result = expectation.expectation->Check(data, size);
|
|
if (!result) {
|
|
result << " Expectation created at " << expectation.file << ":" << expectation.line
|
|
<< std::endl;
|
|
result << expectation.message->str();
|
|
}
|
|
|
|
EXPECT_TRUE(result);
|
|
}
|
|
}
|
|
|
|
bool RGBA8::operator==(const RGBA8& other) const {
|
|
return r == other.r && g == other.g && b == other.b && a == other.a;
|
|
}
|
|
|
|
bool RGBA8::operator!=(const RGBA8& other) const {
|
|
return !(*this == other);
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& stream, const RGBA8& color) {
|
|
return stream << "RGBA8(" << static_cast<int>(color.r) << ", " << static_cast<int>(color.g)
|
|
<< ", " << static_cast<int>(color.b) << ", " << static_cast<int>(color.a) << ")";
|
|
}
|
|
|
|
namespace detail {
|
|
std::vector<AdapterTestParam> GetAvailableAdapterTestParamsForBackends(
|
|
const BackendTestConfig* params,
|
|
size_t numParams) {
|
|
ASSERT(gTestEnv != nullptr);
|
|
return gTestEnv->GetAvailableAdapterTestParamsForBackends(params, numParams);
|
|
}
|
|
|
|
// Helper classes to set expectations
|
|
|
|
template <typename T>
|
|
ExpectEq<T>::ExpectEq(T singleValue) {
|
|
mExpected.push_back(singleValue);
|
|
}
|
|
|
|
template <typename T>
|
|
ExpectEq<T>::ExpectEq(const T* values, const unsigned int count) {
|
|
mExpected.assign(values, values + count);
|
|
}
|
|
|
|
template <typename T>
|
|
testing::AssertionResult ExpectEq<T>::Check(const void* data, size_t size) {
|
|
DAWN_ASSERT(size == sizeof(T) * mExpected.size());
|
|
|
|
const T* actual = static_cast<const T*>(data);
|
|
|
|
for (size_t i = 0; i < mExpected.size(); ++i) {
|
|
if (actual[i] != mExpected[i]) {
|
|
testing::AssertionResult result = testing::AssertionFailure()
|
|
<< "Expected data[" << i << "] to be "
|
|
<< mExpected[i] << ", actual " << actual[i]
|
|
<< std::endl;
|
|
|
|
auto printBuffer = [&](const T* buffer) {
|
|
static constexpr unsigned int kBytes = sizeof(T);
|
|
|
|
for (size_t index = 0; index < mExpected.size(); ++index) {
|
|
auto byteView = reinterpret_cast<const uint8_t*>(buffer + index);
|
|
for (unsigned int b = 0; b < kBytes; ++b) {
|
|
char buf[4];
|
|
sprintf(buf, "%02X ", byteView[b]);
|
|
result << buf;
|
|
}
|
|
}
|
|
result << std::endl;
|
|
};
|
|
|
|
if (mExpected.size() <= 1024) {
|
|
result << "Expected:" << std::endl;
|
|
printBuffer(mExpected.data());
|
|
|
|
result << "Actual:" << std::endl;
|
|
printBuffer(actual);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return testing::AssertionSuccess();
|
|
}
|
|
|
|
template class ExpectEq<uint8_t>;
|
|
template class ExpectEq<uint32_t>;
|
|
template class ExpectEq<RGBA8>;
|
|
template class ExpectEq<float>;
|
|
} // namespace detail
|