Emulate store and multisample resolve on Metal

This patch implements "store and MSAA resolve" store operation on Metal
drivers that don't support MTLStoreActionStoreAndMultisampleResolve with
a workaround that does MSAA resolve in another render pass.

Driver workaround is one type of Dawn Toggles. Dawn Toggles will include
other optional optimizations and features that can be configured to use
or not when we create Dawn Devices.

As all Metal try bots don't need this toggle, to better test this
patch on the try bots:
1. We add the support of forcing enabling a workaround when starting an
   Dawn end2end test so that we can test the workaround on the platforms
   where the workaround is disabled.
2. We add an optional parameter DeviceDescriptor to CreateDevice() so
   that we can custom the toggles the Dawn device should use.

This patch also adds the support of querying toggle details from Instance
and the names of the toggles in use from Device. These APIs are tested in
the Dawn unittests added in this patch.

BUG=dawn:56
TEST=dawn_end2end_tests
TEST=dawn_unittests

Change-Id: Iae31d2ded6057eee638b6099d3061e9d78b04d55
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/6620
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
This commit is contained in:
Jiawei Shao 2019-04-26 07:52:57 +00:00 committed by Commit Bot service account
parent 8e97b4c8a5
commit 15d4c2e63b
34 changed files with 505 additions and 97 deletions

View File

@ -153,6 +153,7 @@ source_set("libdawn_native_sources") {
"src/dawn_native/Texture.cpp",
"src/dawn_native/Texture.h",
"src/dawn_native/ToBackend.h",
"src/dawn_native/Toggles.h",
"src/dawn_native/dawn_platform.h",
]
@ -581,6 +582,7 @@ test("dawn_unittests") {
"src/tests/unittests/validation/ShaderModuleValidationTests.cpp",
"src/tests/unittests/validation/TextureValidationTests.cpp",
"src/tests/unittests/validation/TextureViewValidationTests.cpp",
"src/tests/unittests/validation/ToggleValidationTests.cpp",
"src/tests/unittests/validation/ValidationTest.cpp",
"src/tests/unittests/validation/ValidationTest.h",
"src/tests/unittests/validation/VertexBufferValidationTests.cpp",

View File

@ -38,20 +38,21 @@ namespace dawn_native {
return mInstance;
}
DeviceBase* AdapterBase::CreateDevice() {
DeviceBase* AdapterBase::CreateDevice(const DeviceDescriptor* descriptor) {
DeviceBase* result = nullptr;
if (mInstance->ConsumedError(CreateDeviceInternal(&result))) {
if (mInstance->ConsumedError(CreateDeviceInternal(&result, descriptor))) {
return nullptr;
}
return result;
}
MaybeError AdapterBase::CreateDeviceInternal(DeviceBase** result) {
MaybeError AdapterBase::CreateDeviceInternal(DeviceBase** result,
const DeviceDescriptor* descriptor) {
// TODO(cwallez@chromium.org): This will eventually have validation that the device
// descriptor is valid and is a subset what's allowed on this adapter.
DAWN_TRY_ASSIGN(*result, CreateDeviceImpl());
DAWN_TRY_ASSIGN(*result, CreateDeviceImpl(descriptor));
return {};
}

View File

@ -33,16 +33,16 @@ namespace dawn_native {
const PCIInfo& GetPCIInfo() const;
InstanceBase* GetInstance() const;
DeviceBase* CreateDevice();
DeviceBase* CreateDevice(const DeviceDescriptor* descriptor = nullptr);
protected:
PCIInfo mPCIInfo = {};
DeviceType mDeviceType = DeviceType::Unknown;
private:
virtual ResultOrError<DeviceBase*> CreateDeviceImpl() = 0;
virtual ResultOrError<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) = 0;
MaybeError CreateDeviceInternal(DeviceBase** result);
MaybeError CreateDeviceInternal(DeviceBase** result, const DeviceDescriptor* descriptor);
InstanceBase* mInstance = nullptr;
BackendType mBackend;

View File

@ -26,6 +26,12 @@ namespace dawn_native {
return GetProcsAutogen();
}
std::vector<const char*> GetTogglesUsed(DawnDevice device) {
const dawn_native::DeviceBase* deviceBase =
reinterpret_cast<const dawn_native::DeviceBase*>(device);
return deviceBase->GetTogglesUsed();
}
// Adapter
Adapter::Adapter() = default;
@ -53,8 +59,8 @@ namespace dawn_native {
return mImpl != nullptr;
}
DawnDevice Adapter::CreateDevice() {
return reinterpret_cast<DawnDevice>(mImpl->CreateDevice());
DawnDevice Adapter::CreateDevice(const DeviceDescriptor* deviceDescriptor) {
return reinterpret_cast<DawnDevice>(mImpl->CreateDevice(deviceDescriptor));
}
// AdapterDiscoverOptionsBase
@ -89,4 +95,8 @@ namespace dawn_native {
return adapters;
}
const ToggleInfo* Instance::GetToggleInfo(const char* toggleName) {
return mImpl->GetToggleInfo(toggleName);
}
} // namespace dawn_native

View File

@ -25,6 +25,7 @@
#include "dawn_native/ErrorData.h"
#include "dawn_native/Fence.h"
#include "dawn_native/FenceSignalTracker.h"
#include "dawn_native/Instance.h"
#include "dawn_native/PipelineLayout.h"
#include "dawn_native/Queue.h"
#include "dawn_native/RenderPipeline.h"
@ -50,7 +51,8 @@ namespace dawn_native {
// DeviceBase
DeviceBase::DeviceBase(AdapterBase* adapter) : mAdapter(adapter) {
DeviceBase::DeviceBase(AdapterBase* adapter, const DeviceDescriptor* descriptor)
: mAdapter(adapter) {
mCaches = std::make_unique<DeviceBase::Caches>();
mFenceSignalTracker = std::make_unique<FenceSignalTracker>(this);
mDynamicUploader = std::make_unique<DynamicUploader>(this);
@ -257,6 +259,42 @@ namespace dawn_native {
}
}
void DeviceBase::ApplyToggleOverrides(const DeviceDescriptor* deviceDescriptor) {
ASSERT(deviceDescriptor);
for (const char* toggleName : deviceDescriptor->forceEnabledToggles) {
Toggle toggle = GetAdapter()->GetInstance()->ToggleNameToEnum(toggleName);
if (toggle != Toggle::InvalidEnum) {
mTogglesSet.SetToggle(toggle, true);
}
}
for (const char* toggleName : deviceDescriptor->forceDisabledToggles) {
Toggle toggle = GetAdapter()->GetInstance()->ToggleNameToEnum(toggleName);
if (toggle != Toggle::InvalidEnum) {
mTogglesSet.SetToggle(toggle, false);
}
}
}
std::vector<const char*> DeviceBase::GetTogglesUsed() const {
std::vector<const char*> togglesNameInUse(mTogglesSet.toggleBitset.count());
uint32_t index = 0;
for (uint32_t i : IterateBitSet(mTogglesSet.toggleBitset)) {
const char* toggleName =
GetAdapter()->GetInstance()->ToggleEnumToName(static_cast<Toggle>(i));
togglesNameInUse[index] = toggleName;
++index;
}
return togglesNameInUse;
}
bool DeviceBase::IsToggleEnabled(Toggle toggle) const {
return mTogglesSet.IsEnabled(toggle);
}
// Implementation details of object creation
MaybeError DeviceBase::CreateBindGroupInternal(BindGroupBase** result,
@ -361,4 +399,8 @@ namespace dawn_native {
return mDynamicUploader.get();
}
void DeviceBase::SetToggle(Toggle toggle, bool isEnabled) {
mTogglesSet.SetToggle(toggle, isEnabled);
}
} // namespace dawn_native

View File

@ -19,6 +19,7 @@
#include "dawn_native/Error.h"
#include "dawn_native/Forward.h"
#include "dawn_native/ObjectBase.h"
#include "dawn_native/Toggles.h"
#include "dawn_native/DawnNative.h"
#include "dawn_native/dawn_platform.h"
@ -36,7 +37,7 @@ namespace dawn_native {
class DeviceBase {
public:
DeviceBase(AdapterBase* adapter);
DeviceBase(AdapterBase* adapter, const DeviceDescriptor* descriptor);
virtual ~DeviceBase();
void HandleError(const char* message);
@ -115,7 +116,13 @@ namespace dawn_native {
ResultOrError<DynamicUploader*> GetDynamicUploader() const;
std::vector<const char*> GetTogglesUsed() const;
bool IsToggleEnabled(Toggle toggle) const;
protected:
void SetToggle(Toggle toggle, bool isEnabled);
void ApplyToggleOverrides(const DeviceDescriptor* deviceDescriptor);
std::unique_ptr<DynamicUploader> mDynamicUploader;
private:
@ -179,6 +186,8 @@ namespace dawn_native {
dawn::DeviceErrorCallback mErrorCallback = nullptr;
dawn::CallbackUserdata mErrorUserdata = 0;
uint32_t mRefCount = 1;
TogglesSet mTogglesSet;
};
} // namespace dawn_native

View File

@ -49,6 +49,28 @@ namespace dawn_native {
}
#endif // defined(DAWN_ENABLE_BACKEND_VULKAN)
namespace {
struct ToggleEnumAndInfo {
Toggle toggle;
ToggleInfo info;
};
using ToggleEnumAndInfoList =
std::array<ToggleEnumAndInfo, static_cast<size_t>(Toggle::EnumCount)>;
static constexpr ToggleEnumAndInfoList kToggleNameAndInfoList = {
{{Toggle::EmulateStoreAndMSAAResolve,
{"emulate_store_and_msaa_resolve",
"Emulate storing into multisampled color attachments and doing MSAA resolve "
"simultaneously. This workaround is enabled by default on the Metal drivers that do "
"not support MTLStoreActionStoreAndMultisampleResolve. To support StoreOp::Store on "
"those platforms, we should do MSAA resolve in another render pass after ending the "
"previous one.",
"https://bugs.chromium.org/p/dawn/issues/detail?id=56"}}}};
} // anonymous namespace
// InstanceBase
void InstanceBase::DiscoverDefaultAdapters() {
@ -78,6 +100,53 @@ namespace dawn_native {
return !ConsumedError(DiscoverAdaptersInternal(options));
}
const char* InstanceBase::ToggleEnumToName(Toggle toggle) {
ASSERT(toggle != Toggle::InvalidEnum);
const ToggleEnumAndInfo& toggleNameAndInfo =
kToggleNameAndInfoList[static_cast<size_t>(toggle)];
ASSERT(toggleNameAndInfo.toggle == toggle);
return toggleNameAndInfo.info.name;
}
const ToggleInfo* InstanceBase::GetToggleInfo(const char* toggleName) {
ASSERT(toggleName);
EnsureToggleNameToEnumMapInitialized();
const auto& iter = mToggleNameToEnumMap.find(toggleName);
if (iter != mToggleNameToEnumMap.cend()) {
return &kToggleNameAndInfoList[static_cast<size_t>(iter->second)].info;
}
return nullptr;
}
Toggle InstanceBase::ToggleNameToEnum(const char* toggleName) {
ASSERT(toggleName);
EnsureToggleNameToEnumMapInitialized();
const auto& iter = mToggleNameToEnumMap.find(toggleName);
if (iter != mToggleNameToEnumMap.cend()) {
return kToggleNameAndInfoList[static_cast<size_t>(iter->second)].toggle;
}
return Toggle::InvalidEnum;
}
void InstanceBase::EnsureToggleNameToEnumMapInitialized() {
if (mToggleNameToEnumMapInitialized) {
return;
}
for (size_t index = 0; index < kToggleNameAndInfoList.size(); ++index) {
const ToggleEnumAndInfo& toggleNameAndInfo = kToggleNameAndInfoList[index];
ASSERT(index == static_cast<size_t>(toggleNameAndInfo.toggle));
mToggleNameToEnumMap[toggleNameAndInfo.info.name] = toggleNameAndInfo.toggle;
}
mToggleNameToEnumMapInitialized = true;
}
const std::vector<std::unique_ptr<AdapterBase>>& InstanceBase::GetAdapters() const {
return mAdapters;
}

View File

@ -17,8 +17,11 @@
#include "dawn_native/Adapter.h"
#include "dawn_native/BackendConnection.h"
#include "dawn_native/Toggles.h"
#include <array>
#include <memory>
#include <unordered_map>
#include <vector>
namespace dawn_native {
@ -41,19 +44,33 @@ namespace dawn_native {
// Used to handle error that happen up to device creation.
bool ConsumedError(MaybeError maybeError);
// Used to query the details of a toggle. Return nullptr if toggleName is not a valid name
// of a toggle supported in Dawn.
const ToggleInfo* GetToggleInfo(const char* toggleName);
Toggle ToggleNameToEnum(const char* toggleName);
const char* ToggleEnumToName(Toggle toggle);
private:
// Lazily creates connections to all backends that have been compiled.
void EnsureBackendConnections();
// Finds the BackendConnection for `type` or returns an error.
ResultOrError<BackendConnection*> FindBackend(BackendType type);
MaybeError DiscoverAdaptersInternal(const AdapterDiscoveryOptionsBase* options);
void EnsureToggleNameToEnumMapInitialized();
bool mBackendsConnected = false;
bool mDiscoveredDefaultAdapters = false;
bool mToggleNameToEnumMapInitialized = false;
std::vector<std::unique_ptr<BackendConnection>> mBackends;
std::vector<std::unique_ptr<AdapterBase>> mAdapters;
std::unordered_map<std::string, Toggle> mToggleNameToEnumMap;
};
} // namespace dawn_native

51
src/dawn_native/Toggles.h Normal file
View File

@ -0,0 +1,51 @@
// Copyright 2019 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.
#ifndef DAWNNATIVE_TOGGLES_H_
#define DAWNNATIVE_TOGGLES_H_
#include <bitset>
#include "dawn_native/DawnNative.h"
namespace dawn_native {
enum class Toggle {
EmulateStoreAndMSAAResolve = 0,
EnumCount = 1,
InvalidEnum = EnumCount,
};
// A wrapper of the bitset to store if a toggle is enabled or not. This wrapper provides the
// convenience to convert the enums of enum class Toggle to the indices of a bitset.
struct TogglesSet {
std::bitset<static_cast<size_t>(Toggle::EnumCount)> toggleBitset;
void SetToggle(Toggle toggle, bool enabled) {
ASSERT(toggle != Toggle::InvalidEnum);
const size_t toggleIndex = static_cast<size_t>(toggle);
toggleBitset.set(toggleIndex, enabled);
}
bool IsEnabled(Toggle toggle) const {
ASSERT(toggle != Toggle::InvalidEnum);
const size_t toggleIndex = static_cast<size_t>(toggle);
return toggleBitset.test(toggleIndex);
}
};
} // namespace dawn_native
#endif // DAWNNATIVE_TOGGLES_H_

View File

@ -59,7 +59,7 @@ namespace dawn_native { namespace d3d12 {
return mBackend;
}
ResultOrError<DeviceBase*> Adapter::CreateDeviceImpl() {
ResultOrError<DeviceBase*> Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) {
ComPtr<ID3D12Device> d3d12Device;
if (FAILED(mBackend->GetFunctions()->d3d12CreateDevice(
mHardwareAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&d3d12Device)))) {
@ -67,7 +67,7 @@ namespace dawn_native { namespace d3d12 {
}
ASSERT(d3d12Device != nullptr);
return new Device(this, d3d12Device);
return new Device(this, d3d12Device, descriptor);
}
}} // namespace dawn_native::d3d12

View File

@ -31,7 +31,7 @@ namespace dawn_native { namespace d3d12 {
Backend* GetBackend() const;
private:
ResultOrError<DeviceBase*> CreateDeviceImpl() override;
ResultOrError<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) override;
ComPtr<IDXGIAdapter1> mHardwareAdapter;
Backend* mBackend;

View File

@ -43,8 +43,10 @@ namespace dawn_native { namespace d3d12 {
ASSERT(SUCCEEDED(hr));
}
Device::Device(Adapter* adapter, ComPtr<ID3D12Device> d3d12Device)
: DeviceBase(adapter), mD3d12Device(d3d12Device) {
Device::Device(Adapter* adapter,
ComPtr<ID3D12Device> d3d12Device,
const DeviceDescriptor* descriptor)
: DeviceBase(adapter, descriptor), mD3d12Device(d3d12Device) {
// Create device-global objects
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;

View File

@ -37,7 +37,9 @@ namespace dawn_native { namespace d3d12 {
// Definition of backend types
class Device : public DeviceBase {
public:
Device(Adapter* adapter, ComPtr<ID3D12Device> d3d12Device);
Device(Adapter* adapter,
ComPtr<ID3D12Device> d3d12Device,
const DeviceDescriptor* descriptor);
~Device();
CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder) override;

View File

@ -142,8 +142,8 @@ namespace dawn_native { namespace metal {
}
private:
ResultOrError<DeviceBase*> CreateDeviceImpl() override {
return {new Device(this, mDevice)};
ResultOrError<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) override {
return {new Device(this, mDevice, descriptor)};
}
id<MTLDevice> mDevice = nil;

View File

@ -47,7 +47,9 @@ namespace dawn_native { namespace metal {
};
// Creates an autoreleased MTLRenderPassDescriptor matching desc
MTLRenderPassDescriptor* CreateMTLRenderPassDescriptor(BeginRenderPassCmd* renderPass) {
MTLRenderPassDescriptor* CreateMTLRenderPassDescriptor(
BeginRenderPassCmd* renderPass,
bool shouldEmulateStoreAndMSAAResolve) {
MTLRenderPassDescriptor* descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) {
@ -67,20 +69,20 @@ namespace dawn_native { namespace metal {
descriptor.colorAttachments[i].level = attachmentInfo.view->GetBaseMipLevel();
descriptor.colorAttachments[i].slice = attachmentInfo.view->GetBaseArrayLayer();
ASSERT(attachmentInfo.storeOp == dawn::StoreOp::Store);
// TODO(jiawei.shao@intel.com): emulate MTLStoreActionStoreAndMultisampleResolve on
// the platforms that do not support this store action.
if (attachmentInfo.resolveTarget.Get() != nullptr) {
descriptor.colorAttachments[i].resolveTexture =
ToBackend(attachmentInfo.resolveTarget->GetTexture())->GetMTLTexture();
descriptor.colorAttachments[i].resolveLevel =
attachmentInfo.resolveTarget->GetBaseMipLevel();
descriptor.colorAttachments[i].resolveSlice =
attachmentInfo.resolveTarget->GetBaseArrayLayer();
descriptor.colorAttachments[i].storeAction =
MTLStoreActionStoreAndMultisampleResolve;
} else {
descriptor.colorAttachments[i].storeAction = MTLStoreActionStore;
if (attachmentInfo.storeOp == dawn::StoreOp::Store) {
if (attachmentInfo.resolveTarget.Get() != nullptr &&
!shouldEmulateStoreAndMSAAResolve) {
descriptor.colorAttachments[i].resolveTexture =
ToBackend(attachmentInfo.resolveTarget->GetTexture())->GetMTLTexture();
descriptor.colorAttachments[i].resolveLevel =
attachmentInfo.resolveTarget->GetBaseMipLevel();
descriptor.colorAttachments[i].resolveSlice =
attachmentInfo.resolveTarget->GetBaseArrayLayer();
descriptor.colorAttachments[i].storeAction =
MTLStoreActionStoreAndMultisampleResolve;
} else {
descriptor.colorAttachments[i].storeAction = MTLStoreActionStore;
}
}
}
@ -120,6 +122,43 @@ namespace dawn_native { namespace metal {
return descriptor;
}
// Do MSAA resolve in another render pass.
void ResolveInAnotherRenderPass(id<MTLCommandBuffer> commandBuffer,
BeginRenderPassCmd* renderPass) {
ASSERT(renderPass->sampleCount > 1);
MTLRenderPassDescriptor* renderPassForResolve = nil;
for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) {
auto& attachmentInfo = renderPass->colorAttachments[i];
if (attachmentInfo.resolveTarget.Get() == nil ||
attachmentInfo.storeOp != dawn::StoreOp::Store) {
continue;
}
if (renderPassForResolve == nil) {
renderPassForResolve = [MTLRenderPassDescriptor renderPassDescriptor];
}
renderPassForResolve.colorAttachments[i].texture =
ToBackend(attachmentInfo.view->GetTexture())->GetMTLTexture();
renderPassForResolve.colorAttachments[i].level = 0;
renderPassForResolve.colorAttachments[i].slice = 0;
renderPassForResolve.colorAttachments[i].storeAction =
MTLStoreActionMultisampleResolve;
renderPassForResolve.colorAttachments[i].resolveTexture =
ToBackend(attachmentInfo.resolveTarget->GetTexture())->GetMTLTexture();
renderPassForResolve.colorAttachments[i].resolveLevel =
attachmentInfo.resolveTarget->GetBaseMipLevel();
renderPassForResolve.colorAttachments[i].resolveSlice =
attachmentInfo.resolveTarget->GetBaseArrayLayer();
}
if (renderPassForResolve != nil) {
id<MTLRenderCommandEncoder> encoder =
[commandBuffer renderCommandEncoderWithDescriptor:renderPassForResolve];
[encoder endEncoding];
}
}
// Handles a call to SetBindGroup, directing the commands to the correct encoder.
// There is a single function that takes both encoders to factor code. Other approaches like
// templates wouldn't work because the name of methods are different between the two encoder
@ -599,9 +638,13 @@ namespace dawn_native { namespace metal {
std::array<uint32_t, kMaxPushConstants> vertexPushConstants;
std::array<uint32_t, kMaxPushConstants> fragmentPushConstants;
bool shouldEmulateStoreAndMSAAResolve =
GetDevice()->IsToggleEnabled(Toggle::EmulateStoreAndMSAAResolve);
// This will be autoreleased
id<MTLRenderCommandEncoder> encoder = [commandBuffer
renderCommandEncoderWithDescriptor:CreateMTLRenderPassDescriptor(renderPassCmd)];
renderCommandEncoderWithDescriptor:CreateMTLRenderPassDescriptor(
renderPassCmd,
shouldEmulateStoreAndMSAAResolve)];
// Set default values for push constants
vertexPushConstants.fill(0);
@ -620,6 +663,9 @@ namespace dawn_native { namespace metal {
case Command::EndRenderPass: {
mCommands.NextCommand<EndRenderPassCmd>();
[encoder endEncoding];
if (renderPassCmd->sampleCount > 1 && shouldEmulateStoreAndMSAAResolve) {
ResolveInAnotherRenderPass(commandBuffer, renderPassCmd);
}
return;
} break;

View File

@ -34,7 +34,7 @@ namespace dawn_native { namespace metal {
class Device : public DeviceBase {
public:
Device(AdapterBase* adapter, id<MTLDevice> mtlDevice);
Device(AdapterBase* adapter, id<MTLDevice> mtlDevice, const DeviceDescriptor* descriptor);
~Device();
CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder) override;
@ -86,6 +86,8 @@ namespace dawn_native { namespace metal {
TextureBase* texture,
const TextureViewDescriptor* descriptor) override;
void InitTogglesFromDriver();
id<MTLDevice> mMtlDevice = nil;
id<MTLCommandQueue> mCommandQueue = nil;
std::unique_ptr<MapRequestTracker> mMapTracker;

View File

@ -34,13 +34,20 @@
namespace dawn_native { namespace metal {
Device::Device(AdapterBase* adapter, id<MTLDevice> mtlDevice)
: DeviceBase(adapter),
Device::Device(AdapterBase* adapter,
id<MTLDevice> mtlDevice,
const DeviceDescriptor* descriptor)
: DeviceBase(adapter, descriptor),
mMtlDevice([mtlDevice retain]),
mMapTracker(new MapRequestTracker(this)),
mCompletedSerial(0) {
[mMtlDevice retain];
mCommandQueue = [mMtlDevice newCommandQueue];
InitTogglesFromDriver();
if (descriptor != nil) {
ApplyToggleOverrides(descriptor);
}
}
Device::~Device() {
@ -67,6 +74,13 @@ namespace dawn_native { namespace metal {
mMtlDevice = nil;
}
void Device::InitTogglesFromDriver() {
// TODO(jiawei.shao@intel.com): check iOS feature sets
bool emulateStoreAndMSAAResolve =
![mMtlDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v2];
SetToggle(Toggle::EmulateStoreAndMSAAResolve, emulateStoreAndMSAAResolve);
}
ResultOrError<BindGroupBase*> Device::CreateBindGroupImpl(
const BindGroupDescriptor* descriptor) {
return new BindGroup(this, descriptor);

View File

@ -33,8 +33,8 @@ namespace dawn_native { namespace null {
virtual ~Adapter() = default;
private:
ResultOrError<DeviceBase*> CreateDeviceImpl() override {
return {new Device(this)};
ResultOrError<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) override {
return {new Device(this, descriptor)};
}
};
@ -58,7 +58,12 @@ namespace dawn_native { namespace null {
// Device
Device::Device(Adapter* adapter) : DeviceBase(adapter) {
Device::Device(Adapter* adapter, const DeviceDescriptor* descriptor)
: DeviceBase(adapter, descriptor) {
// Apply toggle overrides if necessary for test
if (descriptor != nullptr) {
ApplyToggleOverrides(descriptor);
}
}
Device::~Device() {

View File

@ -82,7 +82,7 @@ namespace dawn_native { namespace null {
class Device : public DeviceBase {
public:
Device(Adapter* adapter);
Device(Adapter* adapter, const DeviceDescriptor* descriptor);
~Device();
CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder) override;

View File

@ -39,10 +39,10 @@ namespace dawn_native { namespace opengl {
virtual ~Adapter() = default;
private:
ResultOrError<DeviceBase*> CreateDeviceImpl() override {
ResultOrError<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) override {
// There is no limit on the number of devices created from this adapter because they can
// all share the same backing OpenGL context.
return {new Device(this)};
return {new Device(this, descriptor)};
}
};

View File

@ -31,7 +31,8 @@
namespace dawn_native { namespace opengl {
Device::Device(AdapterBase* adapter) : DeviceBase(adapter) {
Device::Device(AdapterBase* adapter, const DeviceDescriptor* descriptor)
: DeviceBase(adapter, descriptor) {
}
Device::~Device() {

View File

@ -34,7 +34,7 @@ namespace dawn_native { namespace opengl {
class Device : public DeviceBase {
public:
Device(AdapterBase* adapter);
Device(AdapterBase* adapter, const DeviceDescriptor* descriptor);
~Device();
void SubmitFenceSync();

View File

@ -62,8 +62,8 @@ namespace dawn_native { namespace vulkan {
return {};
}
ResultOrError<DeviceBase*> Adapter::CreateDeviceImpl() {
std::unique_ptr<Device> device = std::make_unique<Device>(this);
ResultOrError<DeviceBase*> Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) {
std::unique_ptr<Device> device = std::make_unique<Device>(this, descriptor);
DAWN_TRY(device->Initialize());
return device.release();
}

View File

@ -36,7 +36,7 @@ namespace dawn_native { namespace vulkan {
MaybeError Initialize();
private:
ResultOrError<DeviceBase*> CreateDeviceImpl() override;
ResultOrError<DeviceBase*> CreateDeviceImpl(const DeviceDescriptor* descriptor) override;
VkPhysicalDevice mPhysicalDevice;
Backend* mBackend;

View File

@ -40,7 +40,8 @@
namespace dawn_native { namespace vulkan {
Device::Device(Adapter* adapter) : DeviceBase(adapter) {
Device::Device(Adapter* adapter, const DeviceDescriptor* descriptor)
: DeviceBase(adapter, descriptor) {
}
MaybeError Device::Initialize() {

View File

@ -38,7 +38,7 @@ namespace dawn_native { namespace vulkan {
class Device : public DeviceBase {
public:
Device(Adapter* adapter);
Device(Adapter* adapter, const DeviceDescriptor* descriptor);
~Device();
MaybeError Initialize();

View File

@ -47,6 +47,22 @@ namespace dawn_native {
class InstanceBase;
class AdapterBase;
// An optional parameter of Adapter::CreateDevice() to send additional information when creating
// a Device. For example, we can use it to enable a workaround, optimization or feature.
struct DAWN_NATIVE_EXPORT DeviceDescriptor {
std::vector<const char*> forceEnabledToggles;
std::vector<const char*> forceDisabledToggles;
};
// A struct to record the information of a toggle. A toggle is a code path in Dawn device that
// can be manually configured to run or not outside Dawn, including workarounds, special
// features and optimizations.
struct ToggleInfo {
const char* name;
const char* description;
const char* url;
};
// An adapter is an object that represent on possibility of creating devices in the system.
// Most of the time it will represent a combination of a physical GPU and an API. Not that the
// same GPU can be represented by multiple adapters but on different APIs.
@ -68,7 +84,7 @@ namespace dawn_native {
// Create a device on this adapter, note that the interface will change to include at least
// a device descriptor and a pointer to backend specific options.
// On an error, nullptr is returned.
DawnDevice CreateDevice();
DawnDevice CreateDevice(const DeviceDescriptor* deviceDescriptor = nullptr);
private:
AdapterBase* mImpl = nullptr;
@ -107,6 +123,8 @@ namespace dawn_native {
// Returns all the adapters that the instance knows about.
std::vector<Adapter> GetAdapters() const;
const ToggleInfo* GetToggleInfo(const char* toggleName);
private:
InstanceBase* mImpl = nullptr;
};
@ -114,6 +132,9 @@ namespace dawn_native {
// Backend-agnostic API for dawn_native
DAWN_NATIVE_EXPORT DawnProcTable GetProcs();
// Query the names of all the toggles that are enabled in device
DAWN_NATIVE_EXPORT std::vector<const char*> GetTogglesUsed(DawnDevice device);
} // namespace dawn_native
#endif // DAWNNATIVE_DAWNNATIVE_H_

View File

@ -67,6 +67,12 @@ namespace {
}
}
dawn_native::DeviceDescriptor InitWorkaround(const char* forceEnabledWorkaround) {
dawn_native::DeviceDescriptor deviceDescriptor;
deviceDescriptor.forceEnabledToggles.push_back(forceEnabledWorkaround);
return deviceDescriptor;
}
struct MapReadUserdata {
DawnTest* test;
size_t slot;
@ -83,6 +89,12 @@ namespace {
} // namespace
DawnTestParam ForceWorkaround(const DawnTestParam& originParam, const char* workaround) {
DawnTestParam newTestParam = originParam;
newTestParam.forceEnabledWorkaround = workaround;
return newTestParam;
}
// Implementation of DawnTestEnvironment
void InitDawnEnd2EndTestEnvironment(int argc, char** argv) {
@ -112,10 +124,10 @@ void DawnTestEnvironment::SetUp() {
mInstance = std::make_unique<dawn_native::Instance>();
static constexpr dawn_native::BackendType kAllBackends[] = {
D3D12Backend,
MetalBackend,
OpenGLBackend,
VulkanBackend,
dawn_native::BackendType::D3D12,
dawn_native::BackendType::Metal,
dawn_native::BackendType::OpenGL,
dawn_native::BackendType::Vulkan,
};
// Create a test window for each backend and discover an adapter using it.
@ -191,19 +203,19 @@ DawnTest::~DawnTest() {
}
bool DawnTest::IsD3D12() const {
return GetParam() == D3D12Backend;
return GetParam().backendType == dawn_native::BackendType::D3D12;
}
bool DawnTest::IsMetal() const {
return GetParam() == MetalBackend;
return GetParam().backendType == dawn_native::BackendType::Metal;
}
bool DawnTest::IsOpenGL() const {
return GetParam() == OpenGLBackend;
return GetParam().backendType == dawn_native::BackendType::OpenGL;
}
bool DawnTest::IsVulkan() const {
return GetParam() == VulkanBackend;
return GetParam().backendType == dawn_native::BackendType::Vulkan;
}
bool DawnTest::IsAMD() const {
@ -257,19 +269,20 @@ bool DawnTest::IsMacOS() const {
void DawnTest::SetUp() {
// Get an adapter for the backend to use, and create the device.
dawn_native::Adapter backendAdapter;
const dawn_native::BackendType backendType = GetParam().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() == GetParam()) {
if (adapter.GetBackendType() == backendType) {
backendAdapter = 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 (GetParam() != MetalBackend) {
if (backendType != dawn_native::BackendType::Metal) {
break;
}
}
@ -279,13 +292,22 @@ void DawnTest::SetUp() {
}
mPCIInfo = backendAdapter.GetPCIInfo();
DawnDevice backendDevice = backendAdapter.CreateDevice();
DawnDevice backendDevice;
const char* forceEnabledWorkaround = GetParam().forceEnabledWorkaround;
if (forceEnabledWorkaround != nullptr) {
dawn_native::DeviceDescriptor deviceDescriptor = InitWorkaround(forceEnabledWorkaround);
backendDevice = backendAdapter.CreateDevice(&deviceDescriptor);
} else {
backendDevice = backendAdapter.CreateDevice(nullptr);
}
DawnProcTable backendProcs = dawn_native::GetProcs();
// Get the test window and create the device using it (esp. for OpenGL)
GLFWwindow* testWindow = gTestEnv->GetWindowForBackend(GetParam());
GLFWwindow* testWindow = gTestEnv->GetWindowForBackend(backendType);
DAWN_ASSERT(testWindow != nullptr);
mBinding.reset(utils::CreateBinding(GetParam(), testWindow, backendDevice));
mBinding.reset(utils::CreateBinding(backendType, testWindow, backendDevice));
DAWN_ASSERT(mBinding != nullptr);
// Choose whether to use the backend procs and devices directly, or set up the wire.
@ -586,20 +608,26 @@ namespace detail {
}
}
std::vector<dawn_native::BackendType> FilterBackends(const dawn_native::BackendType* types,
size_t numParams) {
std::vector<dawn_native::BackendType> backends;
std::vector<DawnTestParam> FilterBackends(const DawnTestParam* params, size_t numParams) {
std::vector<DawnTestParam> backends;
for (size_t i = 0; i < numParams; ++i) {
if (IsBackendAvailable(types[i])) {
backends.push_back(types[i]);
if (IsBackendAvailable(params[i].backendType)) {
backends.push_back(params[i]);
}
}
return backends;
}
std::string GetParamName(const testing::TestParamInfo<dawn_native::BackendType>& info) {
return ParamName(info.param);
std::string GetParamName(const testing::TestParamInfo<DawnTestParam>& info) {
std::ostringstream ostream;
ostream << ParamName(info.param.backendType);
if (info.param.forceEnabledWorkaround != nullptr) {
ostream << "_" << info.param.forceEnabledWorkaround;
}
return ostream.str();
}
// Helper classes to set expectations

View File

@ -16,6 +16,7 @@
#include "dawn_native/DawnNative.h"
#include <gtest/gtest.h>
#include <memory>
#include <unordered_map>
@ -60,11 +61,24 @@ struct RGBA8 {
};
std::ostream& operator<<(std::ostream& stream, const RGBA8& color);
struct DawnTestParam {
constexpr explicit DawnTestParam(dawn_native::BackendType backendType)
: backendType(backendType) {
}
dawn_native::BackendType backendType;
// TODO(jiawei.shao@intel.com): support enabling and disabling multiple workarounds.
const char* forceEnabledWorkaround = nullptr;
};
// Shorthands for backend types used in the DAWN_INSTANTIATE_TEST
static constexpr dawn_native::BackendType D3D12Backend = dawn_native::BackendType::D3D12;
static constexpr dawn_native::BackendType MetalBackend = dawn_native::BackendType::Metal;
static constexpr dawn_native::BackendType OpenGLBackend = dawn_native::BackendType::OpenGL;
static constexpr dawn_native::BackendType VulkanBackend = dawn_native::BackendType::Vulkan;
static constexpr DawnTestParam D3D12Backend(dawn_native::BackendType::D3D12);
static constexpr DawnTestParam MetalBackend(dawn_native::BackendType::Metal);
static constexpr DawnTestParam OpenGLBackend(dawn_native::BackendType::OpenGL);
static constexpr DawnTestParam VulkanBackend(dawn_native::BackendType::Vulkan);
DawnTestParam ForceWorkaround(const DawnTestParam& originParam, const char* workaround);
struct GLFWwindow;
@ -107,7 +121,7 @@ class DawnTestEnvironment : public testing::Environment {
std::unordered_map<dawn_native::BackendType, GLFWwindow*> mWindows;
};
class DawnTest : public ::testing::TestWithParam<dawn_native::BackendType> {
class DawnTest : public ::testing::TestWithParam<DawnTestParam> {
public:
DawnTest();
~DawnTest();
@ -242,9 +256,8 @@ class DawnTest : public ::testing::TestWithParam<dawn_native::BackendType> {
namespace detail {
// Helper functions used for DAWN_INSTANTIATE_TEST
bool IsBackendAvailable(dawn_native::BackendType type);
std::vector<dawn_native::BackendType> FilterBackends(const dawn_native::BackendType* types,
size_t numParams);
std::string GetParamName(const testing::TestParamInfo<dawn_native::BackendType>& info);
std::vector<DawnTestParam> FilterBackends(const DawnTestParam* params, size_t numParams);
std::string GetParamName(const testing::TestParamInfo<DawnTestParam>& info);
// All classes used to implement the deferred expectations should inherit from this.
class Expectation {

View File

@ -510,4 +510,9 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) {
VerifyResolveTarget(kGreen, resolveTexture2, kBaseMipLevel2, kBaseArrayLayer2);
}
DAWN_INSTANTIATE_TEST(MultisampledRenderingTest, D3D12Backend, OpenGLBackend, MetalBackend, VulkanBackend);
DAWN_INSTANTIATE_TEST(MultisampledRenderingTest,
D3D12Backend,
MetalBackend,
OpenGLBackend,
VulkanBackend,
ForceWorkaround(MetalBackend, "emulate_store_and_msaa_resolve"));

View File

@ -34,7 +34,7 @@ class RingBufferTests : public testing::Test {
protected:
void SetUp() override {
// TODO(bryan.bernhart@intel.com): Create this device through the adapter.
mDevice = std::make_unique<null::Device>(/*adapter*/ nullptr);
mDevice = std::make_unique<null::Device>(/*adapter*/ nullptr, /*deviceDescriptor*/ nullptr);
}
null::Device* GetDevice() const {

View File

@ -0,0 +1,72 @@
// Copyright 2019 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/unittests/validation/ValidationTest.h"
namespace {
class ToggleValidationTest : public ValidationTest {
};
// Tests querying the detail of a toggle from dawn_native::InstanceBase works correctly.
TEST_F(ToggleValidationTest, QueryToggleInfo) {
// Query with a valid toggle name
{
const char* kValidToggleName = "emulate_store_and_msaa_resolve";
const dawn_native::ToggleInfo* toggleInfo = instance->GetToggleInfo(kValidToggleName);
ASSERT_NE(nullptr, toggleInfo);
ASSERT_NE(nullptr, toggleInfo->name);
ASSERT_NE(nullptr, toggleInfo->description);
ASSERT_NE(nullptr, toggleInfo->url);
}
// Query with an invalid toggle name
{
const char* kInvalidToggleName = "!@#$%^&*";
const dawn_native::ToggleInfo* toggleInfo = instance->GetToggleInfo(kInvalidToggleName);
ASSERT_EQ(nullptr, toggleInfo);
}
}
// Tests overriding toggles when creating a device works correctly.
TEST_F(ToggleValidationTest, OverrideToggleUsage) {
// Dawn unittests use null Adapters, so there should be no toggles that are enabled by default
{
std::vector<const char*> toggleNames = dawn_native::GetTogglesUsed(device.Get());
ASSERT_EQ(0u, toggleNames.size());
}
// Create device with a valid name of a toggle
{
const char* kValidToggleName = "emulate_store_and_msaa_resolve";
dawn_native::DeviceDescriptor descriptor;
descriptor.forceEnabledToggles.push_back(kValidToggleName);
DawnDevice deviceWithToggle = adapter.CreateDevice(&descriptor);
std::vector<const char*> toggleNames = dawn_native::GetTogglesUsed(deviceWithToggle);
ASSERT_EQ(1u, toggleNames.size());
ASSERT_EQ(0, strcmp(kValidToggleName, toggleNames[0]));
}
// Create device with an invalid toggle name
{
dawn_native::DeviceDescriptor descriptor;
descriptor.forceEnabledToggles.push_back("!@#$%^&*");
DawnDevice deviceWithToggle = adapter.CreateDevice(&descriptor);
std::vector<const char*> toggleNames = dawn_native::GetTogglesUsed(deviceWithToggle);
ASSERT_EQ(0u, toggleNames.size());
}
}
} // anonymous namespace

View File

@ -16,28 +16,26 @@
#include "common/Assert.h"
#include "dawn/dawn.h"
#include "dawn_native/DawnNative.h"
#include "dawn_native/NullBackend.h"
ValidationTest::ValidationTest() {
mInstance = std::make_unique<dawn_native::Instance>();
mInstance->DiscoverDefaultAdapters();
instance = std::make_unique<dawn_native::Instance>();
instance->DiscoverDefaultAdapters();
std::vector<dawn_native::Adapter> adapters = mInstance->GetAdapters();
std::vector<dawn_native::Adapter> adapters = instance->GetAdapters();
// Validation tests run against the null backend, find the corresponding adapter
bool foundNullAdapter = false;
dawn_native::Adapter nullAdapter;
for (auto adapter : adapters) {
if (adapter.GetBackendType() == dawn_native::BackendType::Null) {
nullAdapter = adapter;
for (auto &currentAdapter : adapters) {
if (currentAdapter.GetBackendType() == dawn_native::BackendType::Null) {
adapter = currentAdapter;
foundNullAdapter = true;
break;
}
}
ASSERT(foundNullAdapter);
device = dawn::Device::Acquire(nullAdapter.CreateDevice());
device = dawn::Device::Acquire(adapter.CreateDevice());
DawnProcTable procs = dawn_native::GetProcs();
dawnSetProcs(&procs);

View File

@ -17,10 +17,7 @@
#include "gtest/gtest.h"
#include "dawn/dawncpp.h"
namespace dawn_native {
class Instance;
}
#include "dawn_native/DawnNative.h"
#define ASSERT_DEVICE_ERROR(statement) \
StartExpectDeviceError(); \
@ -55,10 +52,10 @@ class ValidationTest : public testing::Test {
protected:
dawn::Device device;
dawn_native::Adapter adapter;
std::unique_ptr<dawn_native::Instance> instance;
private:
std::unique_ptr<dawn_native::Instance> mInstance;
static void OnDeviceError(const char* message, DawnCallbackUserdata userdata);
std::string mDeviceErrorMessage;
bool mExpectError = false;