1
/********************************************************************
2
KWin - the KDE window manager
3
This file is part of the KDE project.
5
Copyright (C) 2010 Fredrik Höglund <fredrik@kde.org>
7
This program is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2 of the License, or
10
(at your option) any later version.
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program. If not, see <http://www.gnu.org/licenses/>.
19
*********************************************************************/
21
#include "kwinglplatform.h"
22
#include "kwinglutils.h"
24
#include <kwinglobals.h>
27
#include <QStringList>
30
#include <sys/utsname.h>
36
#ifdef KWIN_HAVE_OPENGL
41
GLPlatform *GLPlatform::s_platform = 0;
43
static qint64 parseVersionString(const QByteArray &version)
45
// Skip any leading non digit
47
while (start < version.length() && !QChar(version[start]).isDigit())
50
// Strip any non digit, non '.' characters from the end
52
while (end < version.length() && (version[end] == '.' || QChar(version[end]).isDigit()))
55
const QByteArray result = version.mid(start, end-start);
56
const QList<QByteArray> tokens = result.split('.');
57
const qint64 major = tokens.at(0).toInt();
58
const qint64 minor = tokens.count() > 1 ? tokens.at(1).toInt() : 0;
59
const qint64 patch = tokens.count() > 2 ? tokens.at(2).toInt() : 0;
61
return kVersionNumber(major, minor, patch);
64
static qint64 getXServerVersion()
66
qint64 major, minor, patch;
68
Display *dpy = display();
69
if (strstr(ServerVendor(dpy), "X.Org")) {
70
const int release = VendorRelease(dpy);
71
major = (release / 10000000);
72
minor = (release / 100000) % 100;
73
patch = (release / 1000) % 100;
80
return kVersionNumber(major, minor, patch);
83
static qint64 getKernelVersion()
88
if (QByteArray(name.sysname) == "Linux")
89
return parseVersionString(name.release);
94
// Extracts the portion of a string that matches a regular expression
95
static QString extract(const QString &string, const QString &match, int offset = 0)
99
int pos = rx.indexIn(string, offset);
101
result = string.mid(pos, rx.matchedLength());
105
static ChipClass detectRadeonClass(const QString &chipset)
107
if (chipset.isEmpty())
108
return UnknownRadeon;
110
if (chipset.contains("R100") ||
111
chipset.contains("RV100") ||
112
chipset.contains("RS100"))
115
if (chipset.contains("RV200") ||
116
chipset.contains("RS200") ||
117
chipset.contains("R200") ||
118
chipset.contains("RV250") ||
119
chipset.contains("RS300") ||
120
chipset.contains("RV280"))
123
if (chipset.contains("R300") ||
124
chipset.contains("R350") ||
125
chipset.contains("R360") ||
126
chipset.contains("RV350") ||
127
chipset.contains("RV370") ||
128
chipset.contains("RV380"))
131
if (chipset.contains("R420") ||
132
chipset.contains("R423") ||
133
chipset.contains("R430") ||
134
chipset.contains("R480") ||
135
chipset.contains("R481") ||
136
chipset.contains("RV410") ||
137
chipset.contains("RS400") ||
138
chipset.contains("RC410") ||
139
chipset.contains("RS480") ||
140
chipset.contains("RS482") ||
141
chipset.contains("RS600") ||
142
chipset.contains("RS690") ||
143
chipset.contains("RS740"))
146
if (chipset.contains("RV515") ||
147
chipset.contains("R520") ||
148
chipset.contains("RV530") ||
149
chipset.contains("R580") ||
150
chipset.contains("RV560") ||
151
chipset.contains("RV570"))
154
if (chipset.contains("R600") ||
155
chipset.contains("RV610") ||
156
chipset.contains("RV630") ||
157
chipset.contains("RV670") ||
158
chipset.contains("RV620") ||
159
chipset.contains("RV635") ||
160
chipset.contains("RS780") ||
161
chipset.contains("RS880"))
164
if (chipset.contains("R700") ||
165
chipset.contains("RV770") ||
166
chipset.contains("RV730") ||
167
chipset.contains("RV710") ||
168
chipset.contains("RV740"))
171
if (chipset.contains("EVERGREEN") || // Not an actual chipset, but returned by R600G in 7.9
172
chipset.contains("CEDAR") ||
173
chipset.contains("REDWOOD") ||
174
chipset.contains("JUNIPER") ||
175
chipset.contains("CYPRESS") ||
176
chipset.contains("PALM") ||
177
chipset.contains("HEMLOCK"))
180
if (chipset.contains("BARTS") ||
181
chipset.contains("TURKS") ||
182
chipset.contains("CAICOS"))
183
return NorthernIslands;
185
QString name = extract(chipset, "HD [0-9]{4}"); // HD followed by a space and 4 digits
186
if (!name.isEmpty()) {
187
const int id = name.right(4).toInt();
188
if (id == 6250 || id == 6310) // Palm
191
if (id >= 6000 && id < 7000)
192
return NorthernIslands; // HD 6xxx
194
if (id >= 5000 && id < 6000)
195
return Evergreen; // HD 5xxx
197
if (id >= 4000 && id < 5000)
198
return R700; // HD 4xxx
200
if (id >= 2000 && id < 4000) // HD 2xxx/3xxx
203
return UnknownRadeon;
206
name = extract(chipset, "X[0-9]{3,4}"); // X followed by 3-4 digits
207
if (!name.isEmpty()) {
208
const int id = name.mid(1, -1).toInt();
214
// X7xx, X8xx, X12xx, 2100
215
if ((id >= 700 && id < 1000) || id >= 1200)
218
// X200, X3xx, X5xx, X6xx, X10xx, X11xx
219
if ((id >= 300 && id < 700) || (id >= 1000 && id < 1200))
222
return UnknownRadeon;
225
name = extract(chipset, "\\b[0-9]{4}\\b"); // A group of 4 digits
226
if (!name.isEmpty()) {
227
const int id = name.toInt();
230
if (id >= 7000 && id < 8000)
234
if (id >= 8000 && id < 9500)
245
return UnknownRadeon;
248
static ChipClass detectNVidiaClass(const QString &chipset)
250
QString name = extract(chipset, "\\bNV[0-9,A-F]{2}\\b"); // NV followed by two hexadecimal digits
251
if (!name.isEmpty()) {
252
const int id = chipset.mid(2, -1).toInt(0, 16); // Strip the 'NV' from the id
276
return UnknownNVidia;
280
if (chipset.contains("GeForce2") || chipset.contains("GeForce 256"))
283
if (chipset.contains("GeForce3"))
286
if (chipset.contains("GeForce4")) {
287
if (chipset.contains("MX 420") ||
288
chipset.contains("MX 440") || // including MX 440SE
289
chipset.contains("MX 460") ||
290
chipset.contains("MX 4000") ||
291
chipset.contains("PCX 4300"))
298
name = extract(chipset, "GeForce (FX |PCX |Go )?\\d{4}(M|\\b)").trimmed();
299
if (!name.isEmpty()) {
300
if (!name[name.length() - 1].isDigit())
303
const int id = name.right(4).toInt();
307
if (id >= 6000 && id < 8000)
313
return UnknownNVidia;
316
// GeForce 100/200/300/400/500
317
name = extract(chipset, "GeForce (G |GT |GTX |GTS )?\\d{3}(M|\\b)").trimmed();
318
if (!name.isEmpty()) {
319
if (!name[name.length() - 1].isDigit())
322
const int id = name.right(3).toInt();
323
if (id >= 100 && id < 600) {
329
return UnknownNVidia;
332
return UnknownNVidia;
335
static ChipClass detectIntelClass(const QByteArray &chipset)
338
if (chipset.contains("845G") ||
339
chipset.contains("830M") ||
340
chipset.contains("852GM/855GM") ||
341
chipset.contains("865G"))
344
// GL 1.4, DX 9.0, SM 2.0
345
if (chipset.contains("915G") ||
346
chipset.contains("E7221G") ||
347
chipset.contains("915GM") ||
348
chipset.contains("945G") || // DX 9.0c
349
chipset.contains("945GM") ||
350
chipset.contains("945GME") ||
351
chipset.contains("Q33") || // GL1.5
352
chipset.contains("Q35") ||
353
chipset.contains("G33") ||
354
chipset.contains("965Q") || // GMA 3000, but apparently considered gen 4 by the driver
355
chipset.contains("946GZ") || // GMA 3000, but apparently considered gen 4 by the driver
356
chipset.contains("IGD"))
359
// GL 2.0, DX 9.0c, SM 3.0
360
if (chipset.contains("965G") ||
361
chipset.contains("G45/G43") || // SM 4.0
362
chipset.contains("965GM") || // GL 2.1
363
chipset.contains("965GME/GLE") ||
364
chipset.contains("GM45") ||
365
chipset.contains("Q45/Q43") ||
366
chipset.contains("G41") ||
367
chipset.contains("B43") ||
368
chipset.contains("Ironlake") ||
369
chipset.contains("Sandybridge")) // GL 3.1, CL 1.1, DX 10.1
375
static QString versionToString(qint64 version)
377
int major = (version >> 32);
378
int minor = (version >> 16) & 0xffff;
379
int patch = version & 0xffff;
381
QString string = QString::number(major) + QChar('.') + QString::number(minor);
383
string += QChar('.') + QString::number(patch);
388
static QString driverToString(Driver driver)
409
case Driver_Catalyst:
412
return "Software rasterizer";
413
case Driver_Softpipe:
415
case Driver_Llvmpipe:
423
static QString chipClassToString(ChipClass chipClass)
442
case NorthernIslands:
465
return "SandyBridge";
478
GLPlatform::GLPlatform()
479
: m_driver(Driver_Unknown),
480
m_chipClass(UnknownChipClass),
483
m_looseBinding(false),
484
m_directRendering(false),
485
m_supportsGLSL(false),
490
GLPlatform::~GLPlatform()
494
void GLPlatform::detect()
496
m_vendor = (const char*)glGetString(GL_VENDOR);
497
m_renderer = (const char*)glGetString(GL_RENDERER);
498
m_version = (const char*)glGetString(GL_VERSION);
500
const QByteArray extensions = (const char*)glGetString(GL_EXTENSIONS);
501
m_extensions = QSet<QByteArray>::fromList(extensions.split(' '));
503
// Parse the OpenGL version
504
const QList<QByteArray> versionTokens = m_version.split(' ');
505
if (versionTokens.count() > 0) {
506
const QByteArray version = QByteArray(m_version);
507
m_glVersion = parseVersionString(version);
510
// Parse the Mesa version
511
const int mesaIndex = versionTokens.indexOf("Mesa");
512
if (mesaIndex != -1) {
513
const QByteArray version = versionTokens.at(mesaIndex + 1);
514
m_mesaVersion = parseVersionString(version);
517
#ifdef KWIN_HAVE_OPENGLES
518
m_directRendering = true;
519
m_supportsGLSL = true;
520
m_textureNPOT = true;
522
GLXContext ctx = glXGetCurrentContext();
523
m_directRendering = glXIsDirect(display(), ctx);
525
m_supportsGLSL = m_extensions.contains("GL_ARB_shading_language_100") &&
526
m_extensions.contains("GL_ARB_shader_objects") &&
527
m_extensions.contains("GL_ARB_fragment_shader") &&
528
m_extensions.contains("GL_ARB_vertex_shader");
530
m_textureNPOT = m_extensions.contains("GL_ARB_texture_non_power_of_two");
533
m_serverVersion = getXServerVersion();
534
m_kernelVersion = getKernelVersion();
537
m_glsl_version = QByteArray();
539
if (m_supportsGLSL) {
540
// Parse the GLSL version
541
m_glsl_version = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
542
m_glslVersion = parseVersionString(m_glsl_version);
545
m_chipset = "Unknown";
548
// Mesa classic drivers
549
// ====================================================
552
if (m_renderer.startsWith("Mesa DRI R")) {
553
// Sample renderer string: Mesa DRI R600 (RV740 94B3) 20090101 x86/MMX/SSE2 TCL DRI2
554
const QList<QByteArray> tokens = m_renderer.split(' ');
555
const QByteArray chipClass = tokens.at(2);
556
m_chipset = tokens.at(3).mid(1, -1); // Strip the leading '('
558
if (chipClass == "R100")
559
// Vendor: Tungsten Graphics, Inc.
560
m_driver = Driver_R100;
562
else if (chipClass == "R200")
563
// Vendor: Tungsten Graphics, Inc.
564
m_driver = Driver_R200;
566
else if (chipClass == "R300")
567
// Vendor: DRI R300 Project
568
m_driver = Driver_R300C;
570
else if (chipClass == "R600")
571
// Vendor: Advanced Micro Devices, Inc.
572
m_driver = Driver_R600C;
574
m_chipClass = detectRadeonClass(m_chipset);
578
else if (m_renderer.contains("Intel")) {
579
// Vendor: Tungsten Graphics, Inc.
580
// Sample renderer string: Mesa DRI Mobile Intel® GM45 Express Chipset GEM 20100328 2010Q1
583
if (m_renderer.startsWith("Intel(R) Integrated Graphics Device"))
586
chipset = m_renderer;
588
m_driver = Driver_Intel;
589
m_chipClass = detectIntelClass(chipset);
593
// ====================================================
594
else if (m_renderer.contains("Gallium")) {
595
// Sample renderer string: Gallium 0.4 on AMD RV740
596
const QList<QByteArray> tokens = m_renderer.split(' ');
597
m_galliumVersion = parseVersionString(tokens.at(1));
598
m_chipset = (tokens.at(3) == "AMD" || tokens.at(3) == "ATI") ?
599
tokens.at(4) : tokens.at(3);
602
if (m_vendor == "X.Org R300 Project") {
603
m_chipClass = detectRadeonClass(m_chipset);
604
m_driver = Driver_R300G;
608
else if (m_vendor == "X.Org" &&
609
(m_renderer.contains("R6") ||
610
m_renderer.contains("R7") ||
611
m_renderer.contains("RV6") ||
612
m_renderer.contains("RV7") ||
613
m_renderer.contains("RS780") ||
614
m_renderer.contains("RS880") ||
615
m_renderer.contains("CEDAR") ||
616
m_renderer.contains("REDWOOD") ||
617
m_renderer.contains("JUNIPER") ||
618
m_renderer.contains("CYPRESS") ||
619
m_renderer.contains("HEMLOCK") ||
620
m_renderer.contains("PALM") ||
621
m_renderer.contains("EVERGREEN") ||
622
m_renderer.contains("BARTS") ||
623
m_renderer.contains("TURKS") ||
624
m_renderer.contains("CAICOS"))) {
625
m_chipClass = detectRadeonClass(m_chipset);
626
m_driver = Driver_R600G;
630
else if (m_vendor == "nouveau") {
631
m_chipClass = detectNVidiaClass(m_chipset);
632
m_driver = Driver_Nouveau;
636
else if (m_vendor == "VMware, Inc." && m_chipset == "softpipe" ) {
637
m_driver = Driver_Softpipe;
641
else if (m_vendor == "VMware, Inc." && m_chipset == "llvmpipe") {
642
m_driver = Driver_Llvmpipe;
647
// Properietary drivers
648
// ====================================================
649
else if (m_vendor == "ATI Technologies Inc.") {
650
m_chipClass = detectRadeonClass(m_renderer);
651
m_driver = Driver_Catalyst;
653
if (versionTokens.count() > 1 && versionTokens.at(2)[0] == '(')
654
m_driverVersion = parseVersionString(versionTokens.at(1));
655
else if (versionTokens.count() > 0)
656
m_driverVersion = parseVersionString(versionTokens.at(0));
661
else if (m_vendor == "NVIDIA Corporation") {
662
m_chipClass = detectNVidiaClass(m_renderer);
663
m_driver = Driver_NVidia;
665
int index = versionTokens.indexOf("NVIDIA");
666
if (versionTokens.count() > index)
667
m_driverVersion = parseVersionString(versionTokens.at(index + 1));
673
// Driver/GPU specific features
674
// ====================================================
676
// R200 technically has a programmable pipeline, but since it's SM 1.4,
677
// it's too limited to to be of any practical value to us.
678
if (m_chipClass < R300)
679
m_supportsGLSL = false;
681
m_limitedGLSL = false;
682
m_limitedNPOT = false;
684
if (m_chipClass < R600) {
685
if (driver() == Driver_Catalyst)
686
m_textureNPOT = m_limitedNPOT = false; // Software fallback
687
else if (driver() == Driver_R300G)
688
m_limitedNPOT = m_textureNPOT;
690
m_limitedGLSL = m_supportsGLSL;
693
if (driver() == Driver_R600G ||
694
(driver() == Driver_R600C && m_renderer.contains("DRI2"))) {
695
m_looseBinding = true;
700
if (m_driver == Driver_NVidia && m_chipClass < NV40)
701
m_supportsGLSL = false; // High likelihood of software emulation
703
if (m_driver == Driver_NVidia)
704
m_looseBinding = true;
706
m_limitedNPOT = m_textureNPOT && m_chipClass < NV40;
707
m_limitedGLSL = m_supportsGLSL && m_chipClass < G80;
711
if (m_chipClass < I915)
712
m_supportsGLSL = false;
714
m_limitedGLSL = m_supportsGLSL && m_chipClass < I965;
717
// Loose binding is broken with Gallium drivers in Mesa 7.10
718
if (isGalliumDriver() && mesaVersion() >= kVersionNumber(7, 10))
719
m_looseBinding = false;
721
if (isSoftwareEmulation()) {
722
// Software emulation does not provide GLSL
723
m_limitedGLSL = m_supportsGLSL = false;
727
static void print(const QString &label, const QString &setting)
729
std::cout << std::setw(40) << std::left
730
<< qPrintable(label) << qPrintable(setting) << std::endl;
733
void GLPlatform::printResults() const
735
print("OpenGL vendor string:", m_vendor);
736
print("OpenGL renderer string:", m_renderer);
737
print("OpenGL version string:", m_version);
740
print("OpenGL shading language version string:", m_glsl_version);
742
print("Driver:", driverToString(m_driver));
744
print("Driver version:", versionToString(m_driverVersion));
746
print("GPU class:", chipClassToString(m_chipClass));
748
print("OpenGL version:", versionToString(m_glVersion));
751
print("GLSL version:", versionToString(m_glslVersion));
754
print("Mesa version:", versionToString(mesaVersion()));
755
//if (galliumVersion() > 0)
756
// print("Gallium version:", versionToString(m_galliumVersion));
757
if (serverVersion() > 0)
758
print("X server version:", versionToString(m_serverVersion));
759
if (kernelVersion() > 0)
760
print("Linux kernel version:", versionToString(m_kernelVersion));
762
print("Direct rendering:", m_directRendering ? "yes" : "no");
763
print("Requires strict binding:", !m_looseBinding ? "yes" : "no");
764
print("GLSL shaders:", m_supportsGLSL ? (m_limitedGLSL ? "limited" : "yes") : "no");
765
print("Texture NPOT support:", m_textureNPOT ? (m_limitedNPOT ? "limited" : "yes") : "no");
768
bool GLPlatform::supports(GLFeature feature) const
772
return m_looseBinding;
775
return m_supportsGLSL;
778
return m_limitedGLSL;
781
return m_textureNPOT;
784
return m_limitedNPOT;
791
qint64 GLPlatform::glVersion() const
796
qint64 GLPlatform::glslVersion() const
798
return m_glslVersion;
801
qint64 GLPlatform::mesaVersion() const
803
return m_mesaVersion;
806
qint64 GLPlatform::galliumVersion() const
808
return m_galliumVersion;
811
qint64 GLPlatform::serverVersion() const
813
return m_serverVersion;
816
qint64 GLPlatform::kernelVersion() const
818
return m_kernelVersion;
821
qint64 GLPlatform::driverVersion() const
824
return mesaVersion();
826
return m_driverVersion;
829
Driver GLPlatform::driver() const
834
ChipClass GLPlatform::chipClass() const
839
bool GLPlatform::isMesaDriver() const
841
return mesaVersion() > 0;
844
bool GLPlatform::isGalliumDriver() const
846
return galliumVersion() > 0;
849
bool GLPlatform::isRadeon() const
851
return m_chipClass >= R100 && m_chipClass <= UnknownRadeon;
854
bool GLPlatform::isNvidia() const
856
return m_chipClass >= NV10 && m_chipClass <= UnknownNVidia;
859
bool GLPlatform::isIntel() const
861
return m_chipClass >= I8XX && m_chipClass <= UnknownIntel;
864
bool GLPlatform::isSoftwareEmulation() const
866
return m_driver == Driver_Softpipe || m_driver == Driver_Swrast || m_driver == Driver_Llvmpipe;
871
#endif // KWIN_HAVE_OPENGL