From 7105be1194ae6396eb14c5a88a0e9a40ab2f190f Mon Sep 17 00:00:00 2001 From: "Bernhart, Bryan" Date: Fri, 31 Mar 2023 10:24:57 -0700 Subject: [PATCH] Move printing of types to only when debugger is attached. --- src/gpgmm/common/EventMessage.cpp | 32 ++++++------ src/gpgmm/common/EventMessage.h | 26 +++++----- src/gpgmm/common/MemoryAllocator.cpp | 6 +-- src/gpgmm/common/SlabMemoryAllocator.cpp | 8 +-- src/gpgmm/d3d12/ResidencyManagerD3D12.cpp | 10 ++-- src/gpgmm/d3d12/ResourceAllocationD3D12.cpp | 4 +- src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp | 10 ++-- src/gpgmm/utils/Log.cpp | 57 ++++++++++++--------- src/gpgmm/utils/Log.h | 24 ++++++--- src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp | 2 +- src/gpgmm/vk/ResourceAllocatorVk.cpp | 2 +- 11 files changed, 99 insertions(+), 82 deletions(-) diff --git a/src/gpgmm/common/EventMessage.cpp b/src/gpgmm/common/EventMessage.cpp index bb74ff068..7d078dcc2 100644 --- a/src/gpgmm/common/EventMessage.cpp +++ b/src/gpgmm/common/EventMessage.cpp @@ -46,43 +46,41 @@ namespace gpgmm { // EventMessage - EventMessage::EventMessage(const MessageSeverity& level, - const char* name, - const void* object, - MessageId messageId) - : mSeverity(level), mName(name), mObject(object), mMessageId(messageId) { + EventMessage::EventMessage(const MessageSeverity& severity, + MessageId messageId, + const ObjectBase* object) + : mSeverity(severity), mMessageId(messageId), mObject(object) { } EventMessage::~EventMessage() { const std::string description = mStream.str(); - gpgmm::Log(mSeverity, mMessageId) - << mName << "=" << ToString(mObject) << ": " << description; + gpgmm::Log(mSeverity, mMessageId, mObject) << ": " << description; #if defined(GPGMM_ENABLE_ASSERT_ON_WARNING) ASSERT(mSeverity < MessageSeverity::kWarning); #endif - if (mSeverity >= GetEventMessageLevel()) { + if (mSeverity >= GetEventMessageLevel() && mObject != nullptr) { GPGMM_TRACE_EVENT_OBJECT_CALL( - mName, MessageInfo({description.c_str(), mMessageId, mSeverity})); + mObject->GetTypename(), MessageInfo({description.c_str(), mMessageId, mSeverity})); } } - EventMessage DebugEvent(const ObjectBase* object, MessageId messageId) { - return {MessageSeverity::kDebug, object->GetTypename(), object, messageId}; + EventMessage DebugEvent(MessageId messageId, const ObjectBase* object) { + return {MessageSeverity::kDebug, messageId, object}; } - EventMessage InfoEvent(const ObjectBase* object, MessageId messageId) { - return {MessageSeverity::kInfo, object->GetTypename(), object, messageId}; + EventMessage InfoEvent(MessageId messageId, const ObjectBase* object) { + return {MessageSeverity::kInfo, messageId, object}; } - EventMessage WarnEvent(const ObjectBase* object, MessageId messageId) { - return {MessageSeverity::kWarning, object->GetTypename(), object, messageId}; + EventMessage WarnEvent(MessageId messageId, const ObjectBase* object) { + return {MessageSeverity::kWarning, messageId, object}; } - EventMessage ErrorEvent(const ObjectBase* object, MessageId messageId) { - return {MessageSeverity::kError, object->GetTypename(), object, messageId}; + EventMessage ErrorEvent(MessageId messageId, const ObjectBase* object) { + return {MessageSeverity::kError, messageId, object}; } } // namespace gpgmm diff --git a/src/gpgmm/common/EventMessage.h b/src/gpgmm/common/EventMessage.h index dacccd39c..03df0def8 100644 --- a/src/gpgmm/common/EventMessage.h +++ b/src/gpgmm/common/EventMessage.h @@ -27,10 +27,9 @@ namespace gpgmm { class EventMessage { public: - EventMessage(const MessageSeverity& level, - const char* name, - const void* object, - MessageId messageId = MessageId::kUnknown); + EventMessage(const MessageSeverity& severity, + MessageId messageId, + const ObjectBase* object); ~EventMessage(); EventMessage(EventMessage&& other) = default; @@ -44,20 +43,21 @@ namespace gpgmm { private: MessageSeverity mSeverity; - const char* mName = nullptr; - const void* mObject = nullptr; MessageId mMessageId = MessageId::kUnknown; + const ObjectBase* mObject = nullptr; std::ostringstream mStream; }; - EventMessage DebugEvent(const ObjectBase* object, MessageId messageId = MessageId::kUnknown); - - EventMessage InfoEvent(const ObjectBase* object, MessageId messageId = MessageId::kUnknown); - - EventMessage WarnEvent(const ObjectBase* object, MessageId messageId = MessageId::kUnknown); - - EventMessage ErrorEvent(const ObjectBase* object, MessageId messageId = MessageId::kUnknown); + // Short-hands to create a EventMessage with the respective severity. + EventMessage DebugEvent(MessageId messageId = MessageId::kUnknown, + const ObjectBase* object = nullptr); + EventMessage InfoEvent(MessageId messageId = MessageId::kUnknown, + const ObjectBase* object = nullptr); + EventMessage WarnEvent(MessageId messageId = MessageId::kUnknown, + const ObjectBase* object = nullptr); + EventMessage ErrorEvent(MessageId messageId = MessageId::kUnknown, + const ObjectBase* object = nullptr); // Messages of a given severity to be recorded. void SetEventMessageLevel(const MessageSeverity& level); diff --git a/src/gpgmm/common/MemoryAllocator.cpp b/src/gpgmm/common/MemoryAllocator.cpp index b7edd830a..ae8ef9ac7 100644 --- a/src/gpgmm/common/MemoryAllocator.cpp +++ b/src/gpgmm/common/MemoryAllocator.cpp @@ -165,7 +165,7 @@ namespace gpgmm { // Check request size cannot overflow. if (request.SizeInBytes > std::numeric_limits::max() - (request.Alignment - 1)) { - DebugEvent(this, MessageId::kSizeExceeded) + DebugEvent(MessageId::kSizeExceeded, this) << "Requested size rejected due to overflow: " + std::to_string(request.SizeInBytes) << " bytes."; return false; @@ -174,7 +174,7 @@ namespace gpgmm { // Check request size cannot overflow |this| memory allocator. const uint64_t alignedSize = AlignTo(request.SizeInBytes, request.Alignment); if (GetMemorySize() != kInvalidSize && alignedSize > GetMemorySize()) { - DebugEvent(this, MessageId::kSizeExceeded) + DebugEvent(MessageId::kSizeExceeded, this) << "Requested size exceeds memory size (" + std::to_string(alignedSize) + " vs " + std::to_string(GetMemorySize()) + " bytes)."; return false; @@ -184,7 +184,7 @@ namespace gpgmm { // Alignment value of 1 means no alignment required. if (GetMemoryAlignment() == 0 || (GetMemoryAlignment() > 1 && !IsAligned(GetMemoryAlignment(), request.Alignment))) { - DebugEvent(this, MessageId::kAlignmentMismatch) + DebugEvent(MessageId::kAlignmentMismatch, this) << "Requested alignment exceeds memory alignment (" + std::to_string(request.Alignment) + " vs " + std::to_string(GetMemoryAlignment()) + " bytes)."; diff --git a/src/gpgmm/common/SlabMemoryAllocator.cpp b/src/gpgmm/common/SlabMemoryAllocator.cpp index b3165f87d..97584b6e5 100644 --- a/src/gpgmm/common/SlabMemoryAllocator.cpp +++ b/src/gpgmm/common/SlabMemoryAllocator.cpp @@ -148,7 +148,7 @@ namespace gpgmm { if (availableForAllocation < slabSize) { const uint64_t slabSizeUnderBudget = FindNextFreeSlabOfSize(requestSize); if (slabSizeUnderBudget == kInvalidSize) { - DebugEvent(this, MessageId::kSizeExceeded) + DebugEvent(MessageId::kSizeExceeded, this) << "Slab size exceeded available memory: " << GPGMM_BYTES_TO_MB(slabSize) << " vs " << GPGMM_BYTES_TO_MB(availableForAllocation) << " MBs."; return kInvalidSize; @@ -209,7 +209,7 @@ namespace gpgmm { // Slab cannot exceed memory size. if (slabSize > mMaxSlabSize) { - gpgmm::DebugEvent(this, MessageId::kSizeExceeded) + gpgmm::DebugEvent(MessageId::kSizeExceeded, this) << "Slab allocation was disabled because the slab size exceeded the max slab size " "allowed: " << GPGMM_BYTES_TO_MB(slabSize) << " vs " << GPGMM_BYTES_TO_MB(mMaxSlabSize) @@ -295,7 +295,7 @@ namespace gpgmm { } if (prefetchedSlabAllocation != nullptr) { - DebugEvent(this, MessageId::kPrefetchFailed) + DebugEvent(MessageId::kPrefetchFailed, this) << "Pre-fetching failed because the slab size did not match: " << GPGMM_BYTES_TO_MB(slabSize) << " vs " << GPGMM_BYTES_TO_MB(prefetchedSlabAllocation->GetSize()) @@ -476,7 +476,7 @@ namespace gpgmm { SafeDivide(mStats.PrefetchedMemoryMissesEliminated, mStats.PrefetchedMemoryMissesEliminated + mStats.PrefetchedMemoryMisses); if (currentCoverage < kPrefetchCoverageWarnMinThreshold) { - WarnEvent(this, MessageId::kPrefetchFailed) + WarnEvent(MessageId::kPrefetchFailed, this) << "Prefetch coverage is below threshold (%): " << currentCoverage * 100 << " vs " << kPrefetchCoverageWarnMinThreshold * 100; return false; diff --git a/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp b/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp index e365c7343..788df4786 100644 --- a/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp +++ b/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp @@ -541,7 +541,7 @@ namespace gpgmm::d3d12 { // Ignore when no budget was specified. if (pVideoMemoryInfo->Budget > 0 && pVideoMemoryInfo->CurrentUsage > pVideoMemoryInfo->Budget) { - WarnEvent(this, MessageId::kBudgetExceeded) + WarnEvent(MessageId::kBudgetExceeded, this) << GetMemorySegmentName(memorySegmentGroup, mIsUMA) << " GPU memory usage exceeds budget: " << GPGMM_BYTES_TO_MB(pVideoMemoryInfo->CurrentUsage) << " vs " @@ -551,7 +551,7 @@ namespace gpgmm::d3d12 { SafeDivide(pVideoMemoryInfo->CurrentUsage, pVideoMemoryInfo->Budget); if (pVideoMemoryInfo->Budget > 0 && currentUsageOfBudget > kMinCurrentUsageOfBudgetReportingThreshold) { - EventMessage message = WarnEvent(this, MessageId::kBudgetExceeded); + EventMessage message = WarnEvent(MessageId::kBudgetExceeded, this); message << GetMemorySegmentName(memorySegmentGroup, mIsUMA) << " GPU memory usage is above budget threshold: " << uint64_t(currentUsageOfBudget * 100) << "% vs " @@ -638,7 +638,7 @@ namespace gpgmm::d3d12 { // If a budget wasn't provided, it not possible to evict. This is because either the budget // update event has not happened yet or was invalid. if (pVideoMemoryInfo->Budget == 0) { - WarnEvent(this, MessageId::kBudgetInvalid) + WarnEvent(MessageId::kBudgetInvalid, this) << "GPU memory segment (" << GetMemorySegmentName(DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, IsUMA()) << ") was unable to evict memory because a budget was not specified."; @@ -711,7 +711,7 @@ namespace gpgmm::d3d12 { const uint32_t objectEvictCount = static_cast(objectsToEvict.size()); ReturnIfFailedDevice(mDevice->Evict(objectEvictCount, objectsToEvict.data()), mDevice); - DebugEvent(this, MessageId::kBudgetExceeded) + DebugEvent(MessageId::kBudgetExceeded, this) << "GPU page-out. Number of allocations: " << objectsToEvict.size() << " (" << bytesEvicted << " bytes)."; } @@ -872,7 +872,7 @@ namespace gpgmm::d3d12 { ReturnIfFailed(EvictInternal(sizeToMakeResident, memorySegmentGroup, nullptr)); - DebugEvent(this, MessageId::kBudgetExceeded) + DebugEvent(MessageId::kBudgetExceeded, this) << "GPU page-in. Number of allocations: " << numberOfObjectsToMakeResident << " (" << sizeToMakeResident << " bytes)."; diff --git a/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp index ffb00e2b5..0a8132b8d 100644 --- a/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp @@ -78,7 +78,7 @@ namespace gpgmm::d3d12 { // Allocation coordinates relative to the resource cannot be used when specifying // subresource-relative coordinates. if (subresource > 0 && GetMethod() == AllocationMethod::kSubAllocatedWithin) { - gpgmm::ErrorEvent(this, MessageId::kBadOperation) + gpgmm::ErrorEvent(MessageId::kBadOperation, this) << "Mapping a sub-allocation within a resource cannot use " "non-zero subresource-relative coordinates."; return E_INVALIDARG; @@ -114,7 +114,7 @@ namespace gpgmm::d3d12 { // Allocation coordinates relative to the resource cannot be used when specifying // subresource-relative coordinates. if (subresource > 0 && GetMethod() == AllocationMethod::kSubAllocatedWithin) { - gpgmm::ErrorEvent(this, MessageId::kBadOperation) + gpgmm::ErrorEvent(MessageId::kBadOperation, this) << "Unmapping a sub-allocation within a resource cannot use " "non-zero subresource-relative coordinates."; return; diff --git a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp index 36f816bd1..85480e835 100644 --- a/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocatorD3D12.cpp @@ -315,7 +315,7 @@ namespace gpgmm::d3d12 { if (FAILED(result.GetErrorCode())) { // NeverAllocate always fails, so suppress it. if (!request.NeverAllocate) { - DebugEvent(allocator, MessageId::kAllocatorFailed) + DebugEvent(MessageId::kAllocatorFailed, allocator) << "Unable to allocate memory for request."; } return static_cast(result.GetErrorCode()); @@ -326,7 +326,7 @@ namespace gpgmm::d3d12 { HRESULT hr = createResourceFn(*allocation); if (FAILED(hr)) { - InfoEvent(allocator, MessageId::kAllocatorFailed) + InfoEvent(MessageId::kAllocatorFailed, allocator) << "Failed to create resource using allocation."; allocator->DeallocateMemory(std::move(allocation)); } @@ -1299,7 +1299,7 @@ namespace gpgmm::d3d12 { return E_OUTOFMEMORY; } - InfoEvent(this, MessageId::kAllocatorFailed) + InfoEvent(MessageId::kAllocatorFailed, this) << "Unable to allocate memory for a resource by using a heap, falling back to a " "committed resource."; } @@ -1527,7 +1527,7 @@ namespace gpgmm::d3d12 { // sub-allocation is used. const uint64_t blocksPerHeap = SafeDivide(result.UsedBlockCount, result.UsedMemoryCount); if (blocksPerHeap > 1 && blocksPerHeap < kMinBlockToMemoryCountReportingThreshold) { - gpgmm::WarnEvent(this, MessageId::kPerformanceWarning) + gpgmm::WarnEvent(MessageId::kPerformanceWarning, this) << "Average number of resource allocations per heap is below threshold: " << blocksPerHeap << " blocks per heap (vs " << kMinBlockToMemoryCountReportingThreshold @@ -1541,7 +1541,7 @@ namespace gpgmm::d3d12 { 100; if (allocationUsagePct > 0 && allocationUsagePct < kMinAllocationUsageReportingThreshold * 100) { - gpgmm::WarnEvent(this, MessageId::kPerformanceWarning) + gpgmm::WarnEvent(MessageId::kPerformanceWarning, this) << "Average resource allocation usage is below threshold: " << allocationUsagePct << "% vs " << uint64_t(kMinAllocationUsageReportingThreshold * 100) << "%. This either means memory has become fragmented or the working set has " diff --git a/src/gpgmm/utils/Log.cpp b/src/gpgmm/utils/Log.cpp index a069eb723..a9cddcf55 100644 --- a/src/gpgmm/utils/Log.cpp +++ b/src/gpgmm/utils/Log.cpp @@ -95,8 +95,10 @@ namespace gpgmm { // LogMessage - LogMessage::LogMessage(MessageSeverity severity, MessageId messageId) noexcept - : mSeverity(severity), mMessageId(messageId) { + LogMessage::LogMessage(MessageSeverity severity, + MessageId messageId, + const ObjectBase* object) noexcept + : mSeverity(severity), mMessageId(messageId), mObject(object) { } LogMessage::~LogMessage() { @@ -121,14 +123,21 @@ namespace gpgmm { if (IsDebuggerPresent()) { std::string outputString; if (mMessageId != MessageId::kUnknown) { - outputString = std::string(kLogTag) + " " + std::string(severityName) + - "(tid: " + ToString(std::this_thread::get_id()) + - "): " + fullMessage + "[" + GetMessageFromID(mMessageId) + "]" + - "\n"; + outputString = + std::string(kLogTag) + " " + std::string(severityName) + + "(tid: " + ToString(std::this_thread::get_id()) + "): " + + ((mObject != nullptr) + ? ((std::string(mObject->GetTypename()) + "=") + ToString(mObject) + ", ") + : "") + + fullMessage + "[" + GetMessageFromID(mMessageId) + "]" + "\n"; } else { - outputString = std::string(kLogTag) + " " + std::string(severityName) + - "(tid: " + ToString(std::this_thread::get_id()) + - "): " + fullMessage + "\n"; + outputString = + std::string(kLogTag) + " " + std::string(severityName) + + "(tid: " + ToString(std::this_thread::get_id()) + "): " + + ((mObject != nullptr) + ? ((std::string(mObject->GetTypename()) + "=") + ToString(mObject) + ", ") + : "") + + fullMessage + "\n"; } OutputDebugStringA(outputString.c_str()); @@ -163,41 +172,41 @@ namespace gpgmm { #endif } - LogMessage DebugLog(MessageId messageId) { - return {MessageSeverity::kDebug, messageId}; + LogMessage DebugLog(MessageId messageId, const ObjectBase* object) { + return {MessageSeverity::kDebug, messageId, object}; } - LogMessage InfoLog(MessageId messageId) { - return {MessageSeverity::kInfo, messageId}; + LogMessage InfoLog(MessageId messageId, const ObjectBase* object) { + return {MessageSeverity::kInfo, messageId, object}; } - LogMessage WarningLog(MessageId messageId) { - return {MessageSeverity::kWarning, messageId}; + LogMessage WarningLog(MessageId messageId, const ObjectBase* object) { + return {MessageSeverity::kWarning, messageId, object}; } - LogMessage ErrorLog(MessageId messageId) { - return {MessageSeverity::kError, messageId}; + LogMessage ErrorLog(MessageId messageId, const ObjectBase* object) { + return {MessageSeverity::kError, messageId, object}; } LogMessage DebugLog(const char* file, const char* function, int line) { - LogMessage message = DebugLog(); + LogMessage message = DebugLog(MessageId::kUnknown, nullptr); message << file << ":" << line << "(" << function << ")"; return message; } - LogMessage Log(MessageSeverity severity, MessageId messageId) { + LogMessage Log(MessageSeverity severity, MessageId messageId, const ObjectBase* object) { switch (severity) { case MessageSeverity::kDebug: - return DebugLog(messageId); + return DebugLog(messageId, object); case MessageSeverity::kInfo: - return InfoLog(messageId); + return InfoLog(messageId, object); case MessageSeverity::kWarning: - return WarningLog(messageId); + return WarningLog(messageId, object); case MessageSeverity::kError: - return ErrorLog(messageId); + return ErrorLog(messageId, object); default: UNREACHABLE(); - return {severity, messageId}; + return {severity, messageId, object}; } } diff --git a/src/gpgmm/utils/Log.h b/src/gpgmm/utils/Log.h index 06e6eb764..dc2fc9f68 100644 --- a/src/gpgmm/utils/Log.h +++ b/src/gpgmm/utils/Log.h @@ -45,6 +45,7 @@ // GPGMM_DEBUG() << texture.GetFormat(); #include "gpgmm/common/Message.h" +#include "gpgmm/common/Object.h" #include @@ -57,7 +58,9 @@ namespace gpgmm { // Essentially an ostringstream that will print itself in its destructor. class LogMessage { public: - LogMessage(MessageSeverity severity, MessageId messageId) noexcept; + LogMessage(MessageSeverity severity, + MessageId messageId, + const ObjectBase* object) noexcept; ~LogMessage(); LogMessage(LogMessage&& other) = default; @@ -74,18 +77,25 @@ namespace gpgmm { LogMessage& operator=(const LogMessage& other) = delete; MessageSeverity mSeverity; - MessageId mMessageId; + MessageId mMessageId = MessageId::kUnknown; + const ObjectBase* mObject = nullptr; std::ostringstream mStream; }; // Short-hands to create a LogMessage with the respective severity. - LogMessage DebugLog(MessageId messageId = MessageId::kUnknown); - LogMessage InfoLog(MessageId messageId = MessageId::kUnknown); - LogMessage WarningLog(MessageId messageId = MessageId::kUnknown); - LogMessage ErrorLog(MessageId messageId = MessageId::kUnknown); + LogMessage DebugLog(MessageId messageId = MessageId::kUnknown, + const ObjectBase* object = nullptr); + LogMessage InfoLog(MessageId messageId = MessageId::kUnknown, + const ObjectBase* object = nullptr); + LogMessage WarningLog(MessageId messageId = MessageId::kUnknown, + const ObjectBase* object = nullptr); + LogMessage ErrorLog(MessageId messageId = MessageId::kUnknown, + const ObjectBase* object = nullptr); // Create a LogMessage based on severity. - LogMessage Log(MessageSeverity severity, MessageId messageId); + LogMessage Log(MessageSeverity severity, + MessageId messageId = MessageId::kUnknown, + const ObjectBase* object = nullptr); // GPGMM_DEBUG is a helper macro that creates a DebugLog and outputs file/line/function // information diff --git a/src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp b/src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp index 282da51ad..9539341f4 100644 --- a/src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp +++ b/src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp @@ -39,7 +39,7 @@ namespace gpgmm::vk { const uint64_t maxDeviceMemoryAllocationCount = mResourceAllocator->GetCaps()->GetMaxDeviceAllocationCount(); if (mStats.UsedMemoryCount + 1 >= maxDeviceMemoryAllocationCount) { - DebugEvent(this, MessageId::kAllocatorFailed) + DebugEvent(MessageId::kAllocatorFailed, this) << "Device exceeded max number of device memory allocations (" + std::to_string(mStats.UsedMemoryCount) + " vs " + std::to_string(maxDeviceMemoryAllocationCount) + ")."; diff --git a/src/gpgmm/vk/ResourceAllocatorVk.cpp b/src/gpgmm/vk/ResourceAllocatorVk.cpp index 3e52b1eca..48f3d81f9 100644 --- a/src/gpgmm/vk/ResourceAllocatorVk.cpp +++ b/src/gpgmm/vk/ResourceAllocatorVk.cpp @@ -353,7 +353,7 @@ namespace gpgmm::vk { } if (!result.IsSuccess()) { - ErrorEvent(allocator, MessageId::kAllocatorFailed) + ErrorEvent(MessageId::kAllocatorFailed, allocator) << "Unable to allocate memory for resource."; return VK_ERROR_UNKNOWN;