| // 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/Constants.h" |
| #include "common/Math.h" |
| #include "common/Platform.h" |
| #include "dawn_native/DawnNative.h" |
| #include "dawn_wire/WireClient.h" |
| #include "dawn_wire/WireServer.h" |
| #include "utils/DawnHelpers.h" |
| #include "utils/SystemUtils.h" |
| #include "utils/TerribleCommandBuffer.h" |
| |
| #include <algorithm> |
| #include <iomanip> |
| #include <iostream> |
| #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(dawn_native::BackendType type) { |
| switch (type) { |
| case dawn_native::BackendType::D3D12: |
| return "D3D12"; |
| case dawn_native::BackendType::Metal: |
| return "Metal"; |
| case dawn_native::BackendType::Null: |
| return "Null"; |
| case dawn_native::BackendType::OpenGL: |
| return "OpenGL"; |
| case dawn_native::BackendType::Vulkan: |
| return "Vulkan"; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| const char* DeviceTypeName(dawn_native::DeviceType type) { |
| switch (type) { |
| case dawn_native::DeviceType::DiscreteGPU: |
| return "Discrete GPU"; |
| case dawn_native::DeviceType::IntegratedGPU: |
| return "Integrated GPU"; |
| case dawn_native::DeviceType::CPU: |
| return "CPU"; |
| case dawn_native::DeviceType::Unknown: |
| return "Unknown"; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| struct MapReadUserdata { |
| DawnTestBase* test; |
| size_t slot; |
| }; |
| |
| DawnTestEnvironment* gTestEnv = nullptr; |
| |
| } // namespace |
| |
| const DawnTestParam D3D12Backend(dawn_native::BackendType::D3D12); |
| const DawnTestParam MetalBackend(dawn_native::BackendType::Metal); |
| const DawnTestParam OpenGLBackend(dawn_native::BackendType::OpenGL); |
| const DawnTestParam VulkanBackend(dawn_native::BackendType::Vulkan); |
| |
| DawnTestParam ForceWorkarounds(const DawnTestParam& originParam, |
| std::initializer_list<const char*> forceEnabledWorkarounds, |
| std::initializer_list<const char*> forceDisabledWorkarounds) { |
| DawnTestParam newTestParam = originParam; |
| newTestParam.forceEnabledWorkarounds = forceEnabledWorkarounds; |
| newTestParam.forceDisabledWorkarounds = forceDisabledWorkarounds; |
| return newTestParam; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, const DawnTestParam& param) { |
| os << ParamName(param.backendType); |
| 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); |
| } |
| |
| DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) { |
| 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 (strstr(argv[i], "--adapter-vendor-id") != nullptr) { |
| const char* value = strchr(argv[i], '='); |
| if (value != nullptr) { |
| mVendorIdFilter = strtoul(value + 1, nullptr, 16); |
| // Set filter flag if vendor id is non-zero. |
| mHasVendorIdFilter = mVendorIdFilter != 0; |
| } |
| continue; |
| } |
| |
| if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) { |
| std::cout << "\n\nUsage: " << argv[0] |
| << " [GTEST_FLAGS...] [-w] [-d] [-c] [--adapter-vendor-id=x]\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" |
| " --adapter-vendor-id: Select adapter by vendor id to run end2end tests" |
| "on multi-GPU systems \n" |
| << std::endl; |
| continue; |
| } |
| } |
| } |
| |
| // static |
| void DawnTestEnvironment::SetEnvironment(DawnTestEnvironment* env) { |
| gTestEnv = env; |
| } |
| |
| void DawnTestEnvironment::SetUp() { |
| mInstance = std::make_unique<dawn_native::Instance>(); |
| mInstance->EnableBackendValidation(mEnableBackendValidation); |
| mInstance->EnableBeginCaptureOnStartup(mBeginCaptureOnStartup); |
| |
| static constexpr dawn_native::BackendType kWindowlessBackends[] = { |
| dawn_native::BackendType::D3D12, |
| dawn_native::BackendType::Metal, |
| dawn_native::BackendType::Vulkan, |
| }; |
| for (dawn_native::BackendType backend : kWindowlessBackends) { |
| if (detail::IsBackendAvailable(backend)) { |
| mInstance.get()->DiscoverDefaultAdapters(); |
| } |
| } |
| |
| if (detail::IsBackendAvailable(dawn_native::BackendType::OpenGL)) { |
| DiscoverOpenGLAdapter(); |
| } |
| |
| std::cout << "Testing configuration\n" |
| "---------------------\n" |
| "UseWire: " |
| << (mUseWire ? "true" : "false") |
| << "\n" |
| "EnableBackendValidation: " |
| << (mEnableBackendValidation ? "true" : "false") |
| << "\n" |
| "BeginCaptureOnStartup: " |
| << (mBeginCaptureOnStartup ? "true" : "false") |
| << "\n" |
| "\n"; |
| |
| // Preparing for outputting hex numbers |
| std::cout << std::showbase << std::hex << std::setfill('0') << std::setw(4); |
| |
| std::cout << "System adapters: \n"; |
| for (const dawn_native::Adapter& adapter : mInstance->GetAdapters()) { |
| const dawn_native::PCIInfo& pci = adapter.GetPCIInfo(); |
| |
| std::ostringstream vendorId; |
| std::ostringstream deviceId; |
| vendorId << std::setfill('0') << std::uppercase << std::internal << std::hex << std::setw(4) |
| << pci.vendorId; |
| deviceId << std::setfill('0') << std::uppercase << std::internal << std::hex << std::setw(4) |
| << pci.deviceId; |
| |
| std::cout << " - \"" << pci.name << "\"\n"; |
| std::cout << " type: " << DeviceTypeName(adapter.GetDeviceType()) |
| << ", backend: " << ParamName(adapter.GetBackendType()) << "\n"; |
| std::cout << " vendorId: 0x" << vendorId.str() << ", deviceId: 0x" << deviceId.str() |
| << (mHasVendorIdFilter && mVendorIdFilter == pci.vendorId ? " [Selected]" : "") |
| << "\n"; |
| } |
| std::cout << std::endl; |
| } |
| |
| bool DawnTestEnvironment::UsesWire() const { |
| return mUseWire; |
| } |
| |
| bool DawnTestEnvironment::IsBackendValidationEnabled() const { |
| return mEnableBackendValidation; |
| } |
| |
| dawn_native::Instance* DawnTestEnvironment::GetInstance() const { |
| return mInstance.get(); |
| } |
| |
| bool DawnTestEnvironment::HasVendorIdFilter() const { |
| return mHasVendorIdFilter; |
| } |
| |
| uint32_t DawnTestEnvironment::GetVendorIdFilter() const { |
| return mVendorIdFilter; |
| } |
| |
| void DawnTestEnvironment::DiscoverOpenGLAdapter() { |
| #ifdef DAWN_ENABLE_BACKEND_OPENGL |
| ASSERT_TRUE(glfwInit()); |
| 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); |
| mInstance->DiscoverAdapters(&adapterOptions); |
| #endif // DAWN_ENABLE_BACKEND_OPENGL |
| } |
| |
| // Implementation of DawnTest |
| |
| DawnTestBase::DawnTestBase(const DawnTestParam& param) : mParam(param) { |
| } |
| |
| DawnTestBase::~DawnTestBase() { |
| // We need to destroy child objects before the Device |
| mReadbackSlots.clear(); |
| queue = dawn::Queue(); |
| device = dawn::Device(); |
| |
| mWireClient = nullptr; |
| mWireServer = nullptr; |
| if (gTestEnv->UsesWire()) { |
| backendProcs.deviceRelease(backendDevice); |
| } |
| |
| dawnSetProcs(nullptr); |
| } |
| |
| bool DawnTestBase::IsD3D12() const { |
| return mParam.backendType == dawn_native::BackendType::D3D12; |
| } |
| |
| bool DawnTestBase::IsMetal() const { |
| return mParam.backendType == dawn_native::BackendType::Metal; |
| } |
| |
| bool DawnTestBase::IsOpenGL() const { |
| return mParam.backendType == dawn_native::BackendType::OpenGL; |
| } |
| |
| bool DawnTestBase::IsVulkan() const { |
| return mParam.backendType == dawn_native::BackendType::Vulkan; |
| } |
| |
| bool DawnTestBase::IsAMD() const { |
| return mPCIInfo.vendorId == kVendorID_AMD; |
| } |
| |
| bool DawnTestBase::IsARM() const { |
| return mPCIInfo.vendorId == kVendorID_ARM; |
| } |
| |
| bool DawnTestBase::IsImgTec() const { |
| return mPCIInfo.vendorId == kVendorID_ImgTec; |
| } |
| |
| bool DawnTestBase::IsIntel() const { |
| return mPCIInfo.vendorId == kVendorID_Intel; |
| } |
| |
| bool DawnTestBase::IsNvidia() const { |
| return mPCIInfo.vendorId == kVendorID_Nvidia; |
| } |
| |
| bool DawnTestBase::IsQualcomm() const { |
| return mPCIInfo.vendorId == kVendorID_Qualcomm; |
| } |
| |
| 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::HasVendorIdFilter() const { |
| return gTestEnv->HasVendorIdFilter(); |
| } |
| |
| uint32_t DawnTestBase::GetVendorIdFilter() const { |
| return gTestEnv->GetVendorIdFilter(); |
| } |
| |
| std::vector<const char*> DawnTestBase::GetRequiredExtensions() { |
| return {}; |
| } |
| |
| // This function can only be called after SetUp() because it requires mBackendAdapter to be |
| // initialized. |
| 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() { |
| // Initialize mBackendAdapter, and create the device. |
| const dawn_native::BackendType backendType = mParam.backendType; |
| { |
| dawn_native::Instance* instance = gTestEnv->GetInstance(); |
| std::vector<dawn_native::Adapter> adapters = instance->GetAdapters(); |
| |
| for (const dawn_native::Adapter& adapter : adapters) { |
| if (adapter.GetBackendType() == backendType) { |
| if (HasVendorIdFilter()) { |
| if (adapter.GetPCIInfo().vendorId == GetVendorIdFilter()) { |
| mBackendAdapter = adapter; |
| break; |
| } |
| } else { |
| mBackendAdapter = adapter; |
| |
| // On Metal, select the last adapter so that the discrete GPU is tested on |
| // multi-GPU systems. |
| // TODO(cwallez@chromium.org): Replace this with command line arguments |
| // requesting a specific device / vendor ID once the macOS 10.13 SDK is rolled |
| // and correct PCI info collection is implemented on Metal. |
| if (backendType != dawn_native::BackendType::Metal) { |
| break; |
| } |
| } |
| } |
| } |
| |
| ASSERT(mBackendAdapter); |
| } |
| |
| mPCIInfo = mBackendAdapter.GetPCIInfo(); |
| |
| 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(); |
| 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. |
| DawnDevice 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()); |
| |
| dawn_wire::WireClientDescriptor clientDesc = {}; |
| clientDesc.serializer = mC2sBuf.get(); |
| |
| mWireClient.reset(new dawn_wire::WireClient(clientDesc)); |
| DawnDevice clientDevice = mWireClient->GetDevice(); |
| DawnProcTable clientProcs = mWireClient->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. |
| dawnSetProcs(&procs); |
| device = dawn::Device::Acquire(cDevice); |
| queue = device.CreateQueue(); |
| |
| device.SetUncapturedErrorCallback(OnDeviceError, 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(DawnErrorType type, const char* message, void* userdata) { |
| ASSERT(type != DAWN_ERROR_TYPE_NO_ERROR); |
| 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; |
| } |
| |
| std::ostringstream& DawnTestBase::AddBufferExpectation(const char* file, |
| int line, |
| const dawn::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. |
| dawn::CommandEncoder encoder = device.CreateCommandEncoder(); |
| encoder.CopyBufferToBuffer(buffer, offset, readback.buffer, readback.offset, size); |
| |
| dawn::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.rowPitch = 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 dawn::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 rowPitch = Align(width * pixelSize, kTextureRowPitchAlignment); |
| uint32_t size = rowPitch * (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. |
| dawn::TextureCopyView textureCopyView = |
| utils::CreateTextureCopyView(texture, level, slice, {x, y, 0}); |
| dawn::BufferCopyView bufferCopyView = |
| utils::CreateBufferCopyView(readback.buffer, readback.offset, rowPitch, 0); |
| dawn::Extent3D copySize = {width, height, 1}; |
| |
| dawn::CommandEncoder encoder = device.CreateCommandEncoder(); |
| encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); |
| |
| dawn::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.rowPitch = rowPitch; |
| 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? |
| dawn::BufferDescriptor descriptor; |
| descriptor.size = readbackSize; |
| descriptor.usage = dawn::BufferUsage::MapRead | dawn::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(DawnBufferMapAsyncStatus status, |
| const void* data, |
| uint64_t, |
| void* userdata_) { |
| DAWN_ASSERT(status == DAWN_BUFFER_MAP_ASYNC_STATUS_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.rowPitch) { |
| DAWN_ASSERT(expectation.rowPitch > expectation.rowBytes); |
| uint32_t rowCount = |
| (expectation.size + expectation.rowPitch - 1) / expectation.rowPitch; |
| 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.rowPitch]; |
| } |
| } |
| 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 { |
| bool IsBackendAvailable(dawn_native::BackendType type) { |
| switch (type) { |
| #if defined(DAWN_ENABLE_BACKEND_D3D12) |
| case dawn_native::BackendType::D3D12: |
| #endif |
| #if defined(DAWN_ENABLE_BACKEND_METAL) |
| case dawn_native::BackendType::Metal: |
| #endif |
| #if defined(DAWN_ENABLE_BACKEND_OPENGL) |
| case dawn_native::BackendType::OpenGL: |
| #endif |
| #if defined(DAWN_ENABLE_BACKEND_VULKAN) |
| case dawn_native::BackendType::Vulkan: |
| #endif |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| std::vector<DawnTestParam> FilterBackends(const DawnTestParam* params, size_t numParams) { |
| std::vector<DawnTestParam> backends; |
| |
| for (size_t i = 0; i < numParams; ++i) { |
| if (IsBackendAvailable(params[i].backendType)) { |
| backends.push_back(params[i]); |
| } |
| } |
| return backends; |
| } |
| |
| // 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>; |
| } // namespace detail |