| // 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 "common/GPUInfo.h" | |
| #include "common/Log.h" | |
| #include "dawn_native/Instance.h" | |
| #include "dawn_native/OpenGLBackend.h" | |
| #include "dawn_native/opengl/DeviceGL.h" | |
| #include <cstring> | |
| namespace dawn_native { namespace 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); | |
| mPCIInfo.name = reinterpret_cast<const char*>(mFunctions.GetString(GL_RENDERER)); | |
| // Workaroud to find vendor id from vendor name | |
| const char* vendor = reinterpret_cast<const char*>(mFunctions.GetString(GL_VENDOR)); | |
| mPCIInfo.vendorId = GetVendorIdFromVendors(vendor); | |
| mDriverDescription = std::string("OpenGL version ") + | |
| reinterpret_cast<const char*>(mFunctions.GetString(GL_VERSION)); | |
| if (mPCIInfo.name.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<DeviceBase*> CreateDeviceImpl( | |
| const DawnDeviceDescriptor* 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<std::unique_ptr<AdapterBase>> Backend::DiscoverDefaultAdapters() { | |
| // The OpenGL backend needs at least "getProcAddress" to discover an adapter. | |
| return {}; | |
| } | |
| ResultOrError<std::vector<std::unique_ptr<AdapterBase>>> 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<wgpu::BackendType>(optionsBase->backendType) == GetType()); | |
| const AdapterDiscoveryOptions* options = | |
| static_cast<const AdapterDiscoveryOptions*>(optionsBase); | |
| DAWN_INVALID_IF(options->getProc == nullptr, | |
| "AdapterDiscoveryOptions::getProc must be set"); | |
| std::unique_ptr<Adapter> adapter = std::make_unique<Adapter>( | |
| GetInstance(), static_cast<wgpu::BackendType>(optionsBase->backendType)); | |
| DAWN_TRY(adapter->InitializeGLFunctions(options->getProc)); | |
| DAWN_TRY(adapter->Initialize()); | |
| mCreatedAdapter = true; | |
| std::vector<std::unique_ptr<AdapterBase>> adapters; | |
| adapters.push_back(std::unique_ptr<AdapterBase>(adapter.release())); | |
| return std::move(adapters); | |
| } | |
| BackendConnection* Connect(InstanceBase* instance, wgpu::BackendType backendType) { | |
| return new Backend(instance, backendType); | |
| } | |
| }} // namespace dawn_native::opengl |