// 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 "dawn/native/opengl/BackendGL.h" #include "dawn/common/GPUInfo.h" #include "dawn/common/Log.h" #include "dawn/native/Instance.h" #include "dawn/native/OpenGLBackend.h" #include "dawn/native/opengl/DeviceGL.h" #include namespace dawn::native::opengl { namespace { struct Vendor { const char* vendorName; uint32_t vendorId; }; const Vendor kVendors[] = {{"ATI", gpu_info::kVendorID_AMD}, {"ARM", gpu_info::kVendorID_ARM}, {"Imagination", gpu_info::kVendorID_ImgTec}, {"Intel", gpu_info::kVendorID_Intel}, {"NVIDIA", gpu_info::kVendorID_Nvidia}, {"Qualcomm", gpu_info::kVendorID_Qualcomm}}; uint32_t GetVendorIdFromVendors(const char* vendor) { uint32_t vendorId = 0; for (const auto& it : kVendors) { // Matching vendor name with vendor string if (strstr(vendor, it.vendorName) != nullptr) { vendorId = it.vendorId; break; } } return vendorId; } void KHRONOS_APIENTRY OnGLDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { const char* sourceText; switch (source) { case GL_DEBUG_SOURCE_API: sourceText = "OpenGL"; break; case GL_DEBUG_SOURCE_WINDOW_SYSTEM: sourceText = "Window System"; break; case GL_DEBUG_SOURCE_SHADER_COMPILER: sourceText = "Shader Compiler"; break; case GL_DEBUG_SOURCE_THIRD_PARTY: sourceText = "Third Party"; break; case GL_DEBUG_SOURCE_APPLICATION: sourceText = "Application"; break; case GL_DEBUG_SOURCE_OTHER: sourceText = "Other"; break; default: sourceText = "UNKNOWN"; break; } const char* severityText; switch (severity) { case GL_DEBUG_SEVERITY_HIGH: severityText = "High"; break; case GL_DEBUG_SEVERITY_MEDIUM: severityText = "Medium"; break; case GL_DEBUG_SEVERITY_LOW: severityText = "Low"; break; case GL_DEBUG_SEVERITY_NOTIFICATION: severityText = "Notification"; break; default: severityText = "UNKNOWN"; break; } if (type == GL_DEBUG_TYPE_ERROR) { dawn::WarningLog() << "OpenGL error:" << "\n Source: " << sourceText // << "\n ID: " << id // << "\n Severity: " << severityText // << "\n Message: " << message; // Abort on an error when in Debug mode. UNREACHABLE(); } } } // anonymous namespace // The OpenGL backend's Adapter. class Adapter : public AdapterBase { public: Adapter(InstanceBase* instance, wgpu::BackendType backendType) : AdapterBase(instance, backendType) { } MaybeError InitializeGLFunctions(void* (*getProc)(const char*)) { // Use getProc to populate the dispatch table return mFunctions.Initialize(getProc); } ~Adapter() override = default; // AdapterBase Implementation bool SupportsExternalImages() const override { // Via dawn::native::opengl::WrapExternalEGLImage return GetBackendType() == wgpu::BackendType::OpenGLES; } private: MaybeError InitializeImpl() override { if (mFunctions.GetVersion().IsES()) { ASSERT(GetBackendType() == wgpu::BackendType::OpenGLES); } else { ASSERT(GetBackendType() == wgpu::BackendType::OpenGL); } // Use the debug output functionality to get notified about GL errors // TODO(cwallez@chromium.org): add support for the KHR_debug and ARB_debug_output // extensions bool hasDebugOutput = mFunctions.IsAtLeastGL(4, 3) || mFunctions.IsAtLeastGLES(3, 2); if (GetInstance()->IsBackendValidationEnabled() && hasDebugOutput) { mFunctions.Enable(GL_DEBUG_OUTPUT); mFunctions.Enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); // Any GL error; dangerous undefined behavior; any shader compiler and linker errors mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE); // Severe performance warnings; GLSL or other shader compiler and linker warnings; // use of currently deprecated behavior mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, GL_TRUE); // Performance warnings from redundant state changes; trivial undefined behavior // This is disabled because we do an incredible amount of redundant state changes. mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, nullptr, GL_FALSE); // Any message which is not an error or performance concern mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE); mFunctions.DebugMessageCallback(&OnGLDebugMessage, nullptr); } // Set state that never changes between devices. mFunctions.Enable(GL_DEPTH_TEST); mFunctions.Enable(GL_SCISSOR_TEST); mFunctions.Enable(GL_PRIMITIVE_RESTART_FIXED_INDEX); if (mFunctions.GetVersion().IsDesktop()) { // These are not necessary on GLES. The functionality is enabled by default, and // works by specifying sample counts and SRGB textures, respectively. mFunctions.Enable(GL_MULTISAMPLE); mFunctions.Enable(GL_FRAMEBUFFER_SRGB); } mFunctions.Enable(GL_SAMPLE_MASK); mName = reinterpret_cast(mFunctions.GetString(GL_RENDERER)); // Workaroud to find vendor id from vendor name const char* vendor = reinterpret_cast(mFunctions.GetString(GL_VENDOR)); mVendorId = GetVendorIdFromVendors(vendor); mDriverDescription = std::string("OpenGL version ") + reinterpret_cast(mFunctions.GetString(GL_VERSION)); if (mName.find("SwiftShader") != std::string::npos) { mAdapterType = wgpu::AdapterType::CPU; } return {}; } MaybeError InitializeSupportedFeaturesImpl() override { // TextureCompressionBC { // BC1, BC2 and BC3 are not supported in OpenGL or OpenGL ES core features. bool supportsS3TC = mFunctions.IsGLExtensionSupported("GL_EXT_texture_compression_s3tc") || (mFunctions.IsGLExtensionSupported("GL_EXT_texture_compression_dxt1") && mFunctions.IsGLExtensionSupported("GL_ANGLE_texture_compression_dxt3") && mFunctions.IsGLExtensionSupported("GL_ANGLE_texture_compression_dxt5")); // COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT and // COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT requires both GL_EXT_texture_sRGB and // GL_EXT_texture_compression_s3tc on desktop OpenGL drivers. // (https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_sRGB.txt) bool supportsTextureSRGB = mFunctions.IsGLExtensionSupported("GL_EXT_texture_sRGB"); // GL_EXT_texture_compression_s3tc_srgb is an extension in OpenGL ES. // NVidia GLES drivers don't support this extension, but they do support // GL_NV_sRGB_formats. (Note that GL_EXT_texture_sRGB does not exist on ES. // GL_EXT_sRGB does (core in ES 3.0), but it does not automatically provide S3TC // SRGB support even if S3TC is supported; see // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_sRGB.txt.) bool supportsS3TCSRGB = mFunctions.IsGLExtensionSupported("GL_EXT_texture_compression_s3tc_srgb") || mFunctions.IsGLExtensionSupported("GL_NV_sRGB_formats"); // BC4 and BC5 bool supportsRGTC = mFunctions.IsAtLeastGL(3, 0) || mFunctions.IsGLExtensionSupported("GL_ARB_texture_compression_rgtc") || mFunctions.IsGLExtensionSupported("GL_EXT_texture_compression_rgtc"); // BC6 and BC7 bool supportsBPTC = mFunctions.IsAtLeastGL(4, 2) || mFunctions.IsGLExtensionSupported("GL_ARB_texture_compression_bptc") || mFunctions.IsGLExtensionSupported("GL_EXT_texture_compression_bptc"); if (supportsS3TC && (supportsTextureSRGB || supportsS3TCSRGB) && supportsRGTC && supportsBPTC) { mSupportedFeatures.EnableFeature(dawn::native::Feature::TextureCompressionBC); } } return {}; } MaybeError InitializeSupportedLimitsImpl(CombinedLimits* limits) override { GetDefaultLimits(&limits->v1); return {}; } ResultOrError> 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 Device::Create(this, descriptor, mFunctions); } OpenGLFunctions mFunctions; }; // Implementation of the OpenGL backend's BackendConnection Backend::Backend(InstanceBase* instance, wgpu::BackendType backendType) : BackendConnection(instance, backendType) { } std::vector> Backend::DiscoverDefaultAdapters() { // The OpenGL backend needs at least "getProcAddress" to discover an adapter. return {}; } ResultOrError>> Backend::DiscoverAdapters( const AdapterDiscoveryOptionsBase* optionsBase) { // TODO(cwallez@chromium.org): For now only create a single OpenGL adapter because don't // know how to handle MakeCurrent. DAWN_INVALID_IF(mCreatedAdapter, "The OpenGL backend can only create a single adapter."); ASSERT(static_cast(optionsBase->backendType) == GetType()); const AdapterDiscoveryOptions* options = static_cast(optionsBase); DAWN_INVALID_IF(options->getProc == nullptr, "AdapterDiscoveryOptions::getProc must be set"); Ref adapter = AcquireRef( new Adapter(GetInstance(), static_cast(optionsBase->backendType))); DAWN_TRY(adapter->InitializeGLFunctions(options->getProc)); DAWN_TRY(adapter->Initialize()); mCreatedAdapter = true; std::vector> adapters{std::move(adapter)}; return std::move(adapters); } BackendConnection* Connect(InstanceBase* instance, wgpu::BackendType backendType) { return new Backend(instance, backendType); } } // namespace dawn::native::opengl