blob: 1907162e8257bea735cf90820df0676a5c30ed3a [file] [log] [blame] [edit]
// 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 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<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