diff options
Diffstat (limited to 'widget/cocoa/GfxInfo.mm')
-rw-r--r-- | widget/cocoa/GfxInfo.mm | 560 |
1 files changed, 560 insertions, 0 deletions
diff --git a/widget/cocoa/GfxInfo.mm b/widget/cocoa/GfxInfo.mm new file mode 100644 index 0000000000..7035e74875 --- /dev/null +++ b/widget/cocoa/GfxInfo.mm @@ -0,0 +1,560 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <OpenGL/OpenGL.h> +#include <OpenGL/CGLRenderers.h> + +#include "mozilla/ArrayUtils.h" + +#include "GfxInfo.h" +#include "nsUnicharUtils.h" +#include "nsExceptionHandler.h" +#include "nsCocoaFeatures.h" +#include "nsCocoaUtils.h" +#include "mozilla/Preferences.h" +#include <algorithm> + +#import <Foundation/Foundation.h> +#import <IOKit/IOKitLib.h> +#import <Cocoa/Cocoa.h> + +#include "jsapi.h" + +#define NS_CRASHREPORTER_CONTRACTID "@mozilla.org/toolkit/crash-reporter;1" + +using namespace mozilla; +using namespace mozilla::widget; + +#ifdef DEBUG +NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, GfxInfoBase, nsIGfxInfoDebug) +#endif + +GfxInfo::GfxInfo() : mNumGPUsDetected(0), mOSXVersion{0} { mAdapterRAM[0] = mAdapterRAM[1] = 0; } + +static OperatingSystem OSXVersionToOperatingSystem(uint32_t aOSXVersion) { + switch (nsCocoaFeatures::ExtractMajorVersion(aOSXVersion)) { + case 10: + switch (nsCocoaFeatures::ExtractMinorVersion(aOSXVersion)) { + case 6: + return OperatingSystem::OSX10_6; + case 7: + return OperatingSystem::OSX10_7; + case 8: + return OperatingSystem::OSX10_8; + case 9: + return OperatingSystem::OSX10_9; + case 10: + return OperatingSystem::OSX10_10; + case 11: + return OperatingSystem::OSX10_11; + case 12: + return OperatingSystem::OSX10_12; + case 13: + return OperatingSystem::OSX10_13; + case 14: + return OperatingSystem::OSX10_14; + case 15: + return OperatingSystem::OSX10_15; + case 16: + // Depending on the SDK version, we either get 10.16 or 11.0. + // Normalize this to 11.0. + return OperatingSystem::OSX11_0; + default: + break; + } + break; + case 11: + switch (nsCocoaFeatures::ExtractMinorVersion(aOSXVersion)) { + case 0: + return OperatingSystem::OSX11_0; + default: + break; + } + break; + } + + return OperatingSystem::Unknown; +} +// The following three functions are derived from Chromium code +static CFTypeRef SearchPortForProperty(io_registry_entry_t dspPort, CFStringRef propertyName) { + return IORegistryEntrySearchCFProperty(dspPort, kIOServicePlane, propertyName, + kCFAllocatorDefault, + kIORegistryIterateRecursively | kIORegistryIterateParents); +} + +static uint32_t IntValueOfCFData(CFDataRef d) { + uint32_t value = 0; + + if (d) { + const uint32_t* vp = reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(d)); + if (vp != NULL) value = *vp; + } + + return value; +} + +void GfxInfo::GetDeviceInfo() { + mNumGPUsDetected = 0; + + CFMutableDictionaryRef pci_dev_dict = IOServiceMatching("IOPCIDevice"); + io_iterator_t io_iter; + if (IOServiceGetMatchingServices(kIOMasterPortDefault, pci_dev_dict, &io_iter) != + kIOReturnSuccess) { + MOZ_DIAGNOSTIC_ASSERT(false, + "Failed to detect any GPUs (couldn't enumerate IOPCIDevice services)"); + return; + } + + io_registry_entry_t entry = IO_OBJECT_NULL; + while ((entry = IOIteratorNext(io_iter)) != IO_OBJECT_NULL) { + constexpr uint32_t kClassCodeDisplayVGA = 0x30000; + CFTypeRef class_code_ref = SearchPortForProperty(entry, CFSTR("class-code")); + if (class_code_ref) { + const uint32_t class_code = IntValueOfCFData((CFDataRef)class_code_ref); + CFRelease(class_code_ref); + + if (class_code == kClassCodeDisplayVGA) { + CFTypeRef vendor_id_ref = SearchPortForProperty(entry, CFSTR("vendor-id")); + if (vendor_id_ref) { + mAdapterVendorID[mNumGPUsDetected].AppendPrintf( + "0x%04x", IntValueOfCFData((CFDataRef)vendor_id_ref)); + CFRelease(vendor_id_ref); + } + CFTypeRef device_id_ref = SearchPortForProperty(entry, CFSTR("device-id")); + if (device_id_ref) { + mAdapterDeviceID[mNumGPUsDetected].AppendPrintf( + "0x%04x", IntValueOfCFData((CFDataRef)device_id_ref)); + CFRelease(device_id_ref); + } + ++mNumGPUsDetected; + } + } + IOObjectRelease(entry); + if (mNumGPUsDetected == 2) { + break; + } + } + IOObjectRelease(io_iter); + +#if defined(__aarch64__) + // If we found IOPCI VGA devices, don't look for AGXAccelerator devices + if (mNumGPUsDetected > 0) { + return; + } + + CFMutableDictionaryRef agx_dev_dict = IOServiceMatching("AGXAccelerator"); + if (IOServiceGetMatchingServices(kIOMasterPortDefault, agx_dev_dict, &io_iter) == + kIOReturnSuccess) { + io_registry_entry_t entry = IO_OBJECT_NULL; + while ((entry = IOIteratorNext(io_iter)) != IO_OBJECT_NULL) { + CFTypeRef vendor_id_ref = SearchPortForProperty(entry, CFSTR("vendor-id")); + if (vendor_id_ref) { + mAdapterVendorID[mNumGPUsDetected].AppendPrintf("0x%04x", + IntValueOfCFData((CFDataRef)vendor_id_ref)); + CFRelease(vendor_id_ref); + ++mNumGPUsDetected; + } + IOObjectRelease(entry); + } + + IOObjectRelease(io_iter); + } +#endif + + MOZ_DIAGNOSTIC_ASSERT(mNumGPUsDetected > 0, "Failed to detect any GPUs"); +} + +nsresult GfxInfo::Init() { + nsresult rv = GfxInfoBase::Init(); + + // Calling CGLQueryRendererInfo causes us to switch to the discrete GPU + // even when we don't want to. We'll avoid doing so for now and just + // use the device ids. + + GetDeviceInfo(); + + AddCrashReportAnnotations(); + + mOSXVersion = nsCocoaFeatures::macOSVersion(); + + return rv; +} + +NS_IMETHODIMP +GfxInfo::GetD2DEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; } + +NS_IMETHODIMP +GfxInfo::GetDWriteEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; } + +/* readonly attribute bool HasBattery; */ +NS_IMETHODIMP GfxInfo::GetHasBattery(bool* aHasBattery) { return NS_ERROR_NOT_IMPLEMENTED; } + +/* readonly attribute DOMString DWriteVersion; */ +NS_IMETHODIMP +GfxInfo::GetDWriteVersion(nsAString& aDwriteVersion) { return NS_ERROR_FAILURE; } + +NS_IMETHODIMP +GfxInfo::GetEmbeddedInFirefoxReality(bool* aEmbeddedInFirefoxReality) { return NS_ERROR_FAILURE; } + +/* readonly attribute DOMString cleartypeParameters; */ +NS_IMETHODIMP +GfxInfo::GetCleartypeParameters(nsAString& aCleartypeParams) { return NS_ERROR_FAILURE; } + +/* readonly attribute DOMString windowProtocol; */ +NS_IMETHODIMP +GfxInfo::GetWindowProtocol(nsAString& aWindowProtocol) { return NS_ERROR_NOT_IMPLEMENTED; } + +/* readonly attribute DOMString desktopEnvironment; */ +NS_IMETHODIMP +GfxInfo::GetDesktopEnvironment(nsAString& aDesktopEnvironment) { return NS_ERROR_NOT_IMPLEMENTED; } + +/* readonly attribute DOMString adapterDescription; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDescription(nsAString& aAdapterDescription) { + aAdapterDescription.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDescription2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDescription2(nsAString& aAdapterDescription) { + if (mNumGPUsDetected < 2) { + return NS_ERROR_FAILURE; + } + aAdapterDescription.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterRAM; */ +NS_IMETHODIMP +GfxInfo::GetAdapterRAM(uint32_t* aAdapterRAM) { + *aAdapterRAM = mAdapterRAM[0]; + return NS_OK; +} + +/* readonly attribute DOMString adapterRAM2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterRAM2(uint32_t* aAdapterRAM) { + if (mNumGPUsDetected < 2) { + return NS_ERROR_FAILURE; + } + *aAdapterRAM = mAdapterRAM[1]; + return NS_OK; +} + +/* readonly attribute DOMString adapterDriver; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriver(nsAString& aAdapterDriver) { + aAdapterDriver.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDriver2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriver2(nsAString& aAdapterDriver) { + if (mNumGPUsDetected < 2) { + return NS_ERROR_FAILURE; + } + aAdapterDriver.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDriverVendor; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriverVendor(nsAString& aAdapterDriverVendor) { + aAdapterDriverVendor.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDriverVendor2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriverVendor2(nsAString& aAdapterDriverVendor) { + if (mNumGPUsDetected < 2) { + return NS_ERROR_FAILURE; + } + aAdapterDriverVendor.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDriverVersion; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriverVersion(nsAString& aAdapterDriverVersion) { + aAdapterDriverVersion.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDriverVersion2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriverVersion2(nsAString& aAdapterDriverVersion) { + if (mNumGPUsDetected < 2) { + return NS_ERROR_FAILURE; + } + aAdapterDriverVersion.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDriverDate; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriverDate(nsAString& aAdapterDriverDate) { + aAdapterDriverDate.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDriverDate2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriverDate2(nsAString& aAdapterDriverDate) { + if (mNumGPUsDetected < 2) { + return NS_ERROR_FAILURE; + } + aAdapterDriverDate.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterVendorID; */ +NS_IMETHODIMP +GfxInfo::GetAdapterVendorID(nsAString& aAdapterVendorID) { + aAdapterVendorID = mAdapterVendorID[0]; + return NS_OK; +} + +/* readonly attribute DOMString adapterVendorID2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterVendorID2(nsAString& aAdapterVendorID) { + if (mNumGPUsDetected < 2) { + return NS_ERROR_FAILURE; + } + aAdapterVendorID = mAdapterVendorID[1]; + return NS_OK; +} + +/* readonly attribute DOMString adapterDeviceID; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDeviceID(nsAString& aAdapterDeviceID) { + aAdapterDeviceID = mAdapterDeviceID[0]; + return NS_OK; +} + +/* readonly attribute DOMString adapterDeviceID2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDeviceID2(nsAString& aAdapterDeviceID) { + if (mNumGPUsDetected < 2) { + return NS_ERROR_FAILURE; + } + aAdapterDeviceID = mAdapterDeviceID[1]; + return NS_OK; +} + +/* readonly attribute DOMString adapterSubsysID; */ +NS_IMETHODIMP +GfxInfo::GetAdapterSubsysID(nsAString& aAdapterSubsysID) { return NS_ERROR_FAILURE; } + +/* readonly attribute DOMString adapterSubsysID2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterSubsysID2(nsAString& aAdapterSubsysID) { return NS_ERROR_FAILURE; } + +/* readonly attribute Array<DOMString> displayInfo; */ +NS_IMETHODIMP +GfxInfo::GetDisplayInfo(nsTArray<nsString>& aDisplayInfo) { + nsAutoreleasePool localPool; + for (NSScreen* screen in [NSScreen screens]) { + NSRect rect = [screen frame]; + nsString desc; + desc.AppendPrintf("%dx%d scale:%f", (int32_t)rect.size.width, (int32_t)rect.size.height, + nsCocoaUtils::GetBackingScaleFactor(screen)); + aDisplayInfo.AppendElement(desc); + } + + return NS_OK; +} + +NS_IMETHODIMP +GfxInfo::GetDisplayWidth(nsTArray<uint32_t>& aDisplayWidth) { + nsAutoreleasePool localPool; + for (NSScreen* screen in [NSScreen screens]) { + NSRect rect = [screen frame]; + aDisplayWidth.AppendElement((uint32_t)rect.size.width); + } + return NS_OK; +} + +NS_IMETHODIMP +GfxInfo::GetDisplayHeight(nsTArray<uint32_t>& aDisplayHeight) { + nsAutoreleasePool localPool; + for (NSScreen* screen in [NSScreen screens]) { + NSRect rect = [screen frame]; + aDisplayHeight.AppendElement((uint32_t)rect.size.height); + } + return NS_OK; +} + +NS_IMETHODIMP +GfxInfo::GetDrmRenderDevice(nsACString& aDrmRenderDevice) { return NS_ERROR_NOT_IMPLEMENTED; } + +/* readonly attribute boolean isGPU2Active; */ +NS_IMETHODIMP +GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active) { return NS_ERROR_FAILURE; } + +void GfxInfo::AddCrashReportAnnotations() { + nsString deviceID, vendorID, driverVersion; + nsAutoCString narrowDeviceID, narrowVendorID, narrowDriverVersion; + + GetAdapterDeviceID(deviceID); + CopyUTF16toUTF8(deviceID, narrowDeviceID); + GetAdapterVendorID(vendorID); + CopyUTF16toUTF8(vendorID, narrowVendorID); + GetAdapterDriverVersion(driverVersion); + CopyUTF16toUTF8(driverVersion, narrowDriverVersion); + + CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID, narrowVendorID); + CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID, narrowDeviceID); + CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDriverVersion, + narrowDriverVersion); +} + +// We don't support checking driver versions on Mac. +#define IMPLEMENT_MAC_DRIVER_BLOCKLIST(os, device, features, blockOn, ruleId) \ + APPEND_TO_DRIVER_BLOCKLIST(os, device, features, blockOn, DRIVER_COMPARISON_IGNORED, \ + V(0, 0, 0, 0), ruleId, "") + +const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() { + if (!sDriverInfo->Length()) { + IMPLEMENT_MAC_DRIVER_BLOCKLIST( + OperatingSystem::OSX, DeviceFamily::RadeonX1000, nsIGfxInfo::FEATURE_OPENGL_LAYERS, + nsIGfxInfo::FEATURE_BLOCKED_DEVICE, "FEATURE_FAILURE_MAC_RADEONX1000_NO_TEXTURE2D"); + IMPLEMENT_MAC_DRIVER_BLOCKLIST( + OperatingSystem::OSX, DeviceFamily::Geforce7300GT, nsIGfxInfo::FEATURE_WEBGL_OPENGL, + nsIGfxInfo::FEATURE_BLOCKED_DEVICE, "FEATURE_FAILURE_MAC_7300_NO_WEBGL"); + IMPLEMENT_MAC_DRIVER_BLOCKLIST(OperatingSystem::OSX, DeviceFamily::IntelHDGraphicsToIvyBridge, + nsIGfxInfo::FEATURE_GL_SWIZZLE, + nsIGfxInfo::FEATURE_BLOCKED_DEVICE, + "FEATURE_FAILURE_MAC_INTELHD4000_NO_SWIZZLE"); + // We block texture swizzling everwhere on mac because it's broken in some configurations + // and we want to support GPU switching. + IMPLEMENT_MAC_DRIVER_BLOCKLIST( + OperatingSystem::OSX, DeviceFamily::All, nsIGfxInfo::FEATURE_GL_SWIZZLE, + nsIGfxInfo::FEATURE_BLOCKED_DEVICE, "FEATURE_FAILURE_MAC_GPU_SWITCHING_NO_SWIZZLE"); + + // FEATURE_WEBRENDER - ALLOWLIST + IMPLEMENT_MAC_DRIVER_BLOCKLIST(OperatingSystem::OSX, DeviceFamily::IntelRolloutWebRender, + nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, + "FEATURE_ROLLOUT_INTEL_MAC"); + // Intel HD3000 disabled due to bug 1661505 + IMPLEMENT_MAC_DRIVER_BLOCKLIST( + OperatingSystem::OSX, DeviceFamily::IntelSandyBridge, nsIGfxInfo::FEATURE_WEBRENDER, + nsIGfxInfo::FEATURE_BLOCKED_DEVICE, "FEATURE_FAILURE_INTEL_MAC_HD3000_NO_WEBRENDER"); + IMPLEMENT_MAC_DRIVER_BLOCKLIST(OperatingSystem::OSX, DeviceFamily::AtiRolloutWebRender, + nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, + "FEATURE_ROLLOUT_AMD_MAC"); + IMPLEMENT_MAC_DRIVER_BLOCKLIST(OperatingSystem::OSX, DeviceFamily::NvidiaRolloutWebRender, + nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, + "FEATURE_ROLLOUT_NVIDIA_MAC"); + IMPLEMENT_MAC_DRIVER_BLOCKLIST(OperatingSystem::OSX, DeviceFamily::AppleAll, + nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, + "FEATURE_ROLLOUT_APPLE_SILICON_MAC"); + } + return *sDriverInfo; +} + +nsresult GfxInfo::GetFeatureStatusImpl(int32_t aFeature, int32_t* aStatus, + nsAString& aSuggestedDriverVersion, + const nsTArray<GfxDriverInfo>& aDriverInfo, + nsACString& aFailureId, + OperatingSystem* aOS /* = nullptr */) { + NS_ENSURE_ARG_POINTER(aStatus); + aSuggestedDriverVersion.SetIsVoid(true); + *aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN; + OperatingSystem os = OSXVersionToOperatingSystem(mOSXVersion); + if (aOS) *aOS = os; + + if (sShutdownOccurred) { + return NS_OK; + } + + // Don't evaluate special cases when we're evaluating the downloaded blocklist. + if (!aDriverInfo.Length()) { + if (aFeature == nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION) { + // See bug 1249659 + switch (os) { + case OperatingSystem::OSX10_5: + case OperatingSystem::OSX10_6: + case OperatingSystem::OSX10_7: + *aStatus = nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION; + aFailureId = "FEATURE_FAILURE_CANVAS_OSX_VERSION"; + break; + default: + *aStatus = nsIGfxInfo::FEATURE_STATUS_OK; + break; + } + return NS_OK; + } else if (aFeature == nsIGfxInfo::FEATURE_WEBRENDER && + nsCocoaFeatures::ProcessIsRosettaTranslated()) { + *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE; + aFailureId = "FEATURE_UNQUALIFIED_WEBRENDER_MAC_ROSETTA"; + return NS_OK; + } + } + + return GfxInfoBase::GetFeatureStatusImpl(aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, + aFailureId, &os); +} + +nsresult GfxInfo::FindMonitors(JSContext* aCx, JS::HandleObject aOutArray) { + nsAutoreleasePool localPool; + // Getting the refresh rate is a little hard on OS X. We could use + // CVDisplayLinkGetNominalOutputVideoRefreshPeriod, but that's a little + // involved. Ideally we could query it from vsync. For now, we leave it out. + int32_t deviceCount = 0; + for (NSScreen* screen in [NSScreen screens]) { + NSRect rect = [screen frame]; + + JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx)); + + JS::Rooted<JS::Value> screenWidth(aCx, JS::Int32Value((int)rect.size.width)); + JS_SetProperty(aCx, obj, "screenWidth", screenWidth); + + JS::Rooted<JS::Value> screenHeight(aCx, JS::Int32Value((int)rect.size.height)); + JS_SetProperty(aCx, obj, "screenHeight", screenHeight); + + JS::Rooted<JS::Value> scale(aCx, JS::NumberValue(nsCocoaUtils::GetBackingScaleFactor(screen))); + JS_SetProperty(aCx, obj, "scale", scale); + + JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj)); + JS_SetElement(aCx, aOutArray, deviceCount++, element); + } + return NS_OK; +} + +#ifdef DEBUG + +// Implement nsIGfxInfoDebug + +/* void spoofVendorID (in DOMString aVendorID); */ +NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString& aVendorID) { + mAdapterVendorID[0] = aVendorID; + return NS_OK; +} + +/* void spoofDeviceID (in unsigned long aDeviceID); */ +NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString& aDeviceID) { + mAdapterDeviceID[0] = aDeviceID; + return NS_OK; +} + +/* void spoofDriverVersion (in DOMString aDriverVersion); */ +NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString& aDriverVersion) { + mDriverVersion[0] = aDriverVersion; + return NS_OK; +} + +/* void spoofOSVersion (in unsigned long aVersion); */ +NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion) { + mOSXVersion = aVersion; + return NS_OK; +} + +/* void fireTestProcess (); */ +NS_IMETHODIMP GfxInfo::FireTestProcess() { return NS_OK; } + +#endif |