Disables framebuffer invalidating by default for GL as some drivers perform it incorr...
[0ad.git] / source / renderer / backend / gl / Device.cpp
blob615641e36e98c99c1092c8dfcc7a58a83b6f8ba2
1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
20 #include "Device.h"
22 #include "lib/external_libraries/libsdl.h"
23 #include "lib/hash.h"
24 #include "lib/ogl.h"
25 #include "ps/CLogger.h"
26 #include "ps/ConfigDB.h"
27 #include "ps/Profile.h"
28 #include "renderer/backend/gl/DeviceCommandContext.h"
29 #include "renderer/backend/gl/PipelineState.h"
30 #include "renderer/backend/gl/Texture.h"
31 #include "scriptinterface/JSON.h"
32 #include "scriptinterface/Object.h"
33 #include "scriptinterface/ScriptInterface.h"
34 #include "scriptinterface/ScriptRequest.h"
36 #if OS_WIN
37 #include "lib/sysdep/os/win/wgfx.h"
39 // We can't include wutil directly because GL headers conflict with Windows
40 // until we use a proper GL loader.
41 extern void* wutil_GetAppHDC();
42 #endif
44 #include <algorithm>
45 #include <boost/algorithm/string/classification.hpp>
46 #include <boost/algorithm/string/split.hpp>
48 #if !CONFIG2_GLES && (defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND))
50 #if defined(SDL_VIDEO_DRIVER_X11)
51 #include <glad/glx.h>
52 #endif
53 #if defined(SDL_VIDEO_DRIVER_WAYLAND)
54 #include <glad/egl.h>
55 #endif
56 #include <SDL_syswm.h>
58 #endif // !CONFIG2_GLES && (defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND))
60 namespace Renderer
63 namespace Backend
66 namespace GL
69 namespace
72 std::string GetNameImpl()
74 // GL_VENDOR+GL_RENDERER are good enough here, so we don't use WMI to detect the cards.
75 // On top of that WMI can cause crashes with Nvidia Optimus and some netbooks
76 // see http://trac.wildfiregames.com/ticket/1952
77 // http://trac.wildfiregames.com/ticket/1575
78 char cardName[128];
79 const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
80 const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
81 // Happens if called before GL initialization.
82 if (!vendor || !renderer)
83 return {};
84 sprintf_s(cardName, std::size(cardName), "%s %s", vendor, renderer);
86 // Remove crap from vendor names. (don't dare touch the model name -
87 // it's too risky, there are too many different strings).
88 #define SHORTEN(what, charsToKeep) \
89 if (!strncmp(cardName, what, std::size(what) - 1)) \
90 memmove(cardName + charsToKeep, cardName + std::size(what) - 1, (strlen(cardName) - (std::size(what) - 1) + 1) * sizeof(char));
91 SHORTEN("ATI Technologies Inc.", 3);
92 SHORTEN("NVIDIA Corporation", 6);
93 SHORTEN("S3 Graphics", 2); // returned by EnumDisplayDevices
94 SHORTEN("S3 Graphics, Incorporated", 2); // returned by GL_VENDOR
95 #undef SHORTEN
97 return cardName;
100 std::string GetVersionImpl()
102 return reinterpret_cast<const char*>(glGetString(GL_VERSION));
105 std::string GetDriverInformationImpl()
107 const std::string version = GetVersionImpl();
109 std::string driverInfo;
110 #if OS_WIN
111 driverInfo = CStrW(wgfx_DriverInfo()).ToUTF8();
112 if (driverInfo.empty())
113 #endif
115 if (!version.empty())
117 // Add "OpenGL" to differentiate this from the real driver version
118 // (returned by platform-specific detect routines).
119 driverInfo = std::string("OpenGL ") + version;
123 if (driverInfo.empty())
124 return version;
125 return version + " " + driverInfo;
128 std::vector<std::string> GetExtensionsImpl()
130 std::vector<std::string> extensions;
131 const std::string exts = ogl_ExtensionString();
132 boost::split(extensions, exts, boost::algorithm::is_space(), boost::token_compress_on);
133 std::sort(extensions.begin(), extensions.end());
134 return extensions;
137 void GLAD_API_PTR OnDebugMessage(
138 GLenum source, GLenum type, GLuint id, GLenum severity,
139 GLsizei UNUSED(length), const GLchar* message, const void* UNUSED(user_param))
141 std::string debugSource = "unknown";
142 std::string debugType = "unknown";
143 std::string debugSeverity = "unknown";
145 switch (source)
147 case GL_DEBUG_SOURCE_API:
148 debugSource = "the API";
149 break;
150 case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
151 debugSource = "the window system";
152 break;
153 case GL_DEBUG_SOURCE_SHADER_COMPILER:
154 debugSource = "the shader compiler";
155 break;
156 case GL_DEBUG_SOURCE_THIRD_PARTY:
157 debugSource = "a third party";
158 break;
159 case GL_DEBUG_SOURCE_APPLICATION:
160 debugSource = "the application";
161 break;
162 case GL_DEBUG_SOURCE_OTHER:
163 debugSource = "somewhere";
164 break;
167 switch (type)
169 case GL_DEBUG_TYPE_ERROR:
170 debugType = "error";
171 break;
172 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
173 debugType = "deprecated behaviour";
174 break;
175 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
176 debugType = "undefined behaviour";
177 break;
178 case GL_DEBUG_TYPE_PORTABILITY:
179 debugType = "portability";
180 break;
181 case GL_DEBUG_TYPE_PERFORMANCE:
182 debugType = "performance";
183 break;
184 case GL_DEBUG_TYPE_OTHER:
185 debugType = "other";
186 break;
187 case GL_DEBUG_TYPE_MARKER:
188 debugType = "marker";
189 break;
190 case GL_DEBUG_TYPE_PUSH_GROUP:
191 debugType = "push group";
192 break;
193 case GL_DEBUG_TYPE_POP_GROUP:
194 debugType = "pop group";
195 break;
198 switch (severity)
200 case GL_DEBUG_SEVERITY_HIGH:
201 debugSeverity = "high";
202 break;
203 case GL_DEBUG_SEVERITY_MEDIUM:
204 debugSeverity = "medium";
205 break;
206 case GL_DEBUG_SEVERITY_LOW:
207 debugSeverity = "low";
208 break;
209 case GL_DEBUG_SEVERITY_NOTIFICATION:
210 debugSeverity = "notification";
211 break;
214 if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
216 debug_printf(
217 "OpenGL | %s: %s source: %s id %u: %s\n", debugSeverity.c_str(), debugType.c_str(), debugSource.c_str(), id, message);
219 else
221 LOGWARNING(
222 "OpenGL | %s: %s source: %s id %u: %s\n", debugSeverity.c_str(), debugType.c_str(), debugSource.c_str(), id, message);
226 } // anonymous namespace
228 // static
229 std::unique_ptr<IDevice> CDevice::Create(SDL_Window* window, const bool arb)
231 std::unique_ptr<CDevice> device(new CDevice());
233 if (window)
235 // According to https://wiki.libsdl.org/SDL_CreateWindow we don't need to
236 // call SDL_GL_LoadLibrary if we have a window with SDL_WINDOW_OPENGL,
237 // because it'll be called internally for the first created window.
238 device->m_Window = window;
239 device->m_Context = SDL_GL_CreateContext(device->m_Window);
240 if (!device->m_Context)
242 LOGERROR("SDL_GL_CreateContext failed: '%s'", SDL_GetError());
243 return nullptr;
245 SDL_GL_GetDrawableSize(window, &device->m_SurfaceDrawableWidth, &device->m_SurfaceDrawableHeight);
247 #if OS_WIN
248 ogl_Init(SDL_GL_GetProcAddress, wutil_GetAppHDC());
249 #elif (defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND)) && !CONFIG2_GLES
250 SDL_SysWMinfo wminfo;
251 // The info structure must be initialized with the SDL version.
252 SDL_VERSION(&wminfo.version);
253 if (!SDL_GetWindowWMInfo(window, &wminfo))
255 LOGERROR("Failed to query SDL WM info: %s", SDL_GetError());
256 return nullptr;
258 switch (wminfo.subsystem)
260 #if defined(SDL_VIDEO_DRIVER_WAYLAND)
261 case SDL_SYSWM_WAYLAND:
262 // TODO: maybe we need to load X11 functions
263 // dynamically as well.
264 ogl_Init(SDL_GL_GetProcAddress,
265 GetWaylandDisplay(device->m_Window),
266 static_cast<int>(wminfo.subsystem));
267 break;
268 #endif
269 #if defined(SDL_VIDEO_DRIVER_X11)
270 case SDL_SYSWM_X11:
271 ogl_Init(SDL_GL_GetProcAddress,
272 GetX11Display(device->m_Window),
273 static_cast<int>(wminfo.subsystem));
274 break;
275 #endif
276 default:
277 ogl_Init(SDL_GL_GetProcAddress, nullptr,
278 static_cast<int>(wminfo.subsystem));
279 break;
281 #else
282 ogl_Init(SDL_GL_GetProcAddress);
283 #endif
285 else
287 #if OS_WIN
288 ogl_Init(SDL_GL_GetProcAddress, wutil_GetAppHDC());
289 #elif (defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND)) && !CONFIG2_GLES
290 bool initialized = false;
291 // Currently we don't have access to the backend type without
292 // the window. So we use hack to detect X11.
293 #if defined(SDL_VIDEO_DRIVER_X11)
294 Display* display = XOpenDisplay(NULL);
295 if (display)
297 ogl_Init(SDL_GL_GetProcAddress, display, static_cast<int>(SDL_SYSWM_X11));
298 initialized = true;
300 #endif
301 #if defined(SDL_VIDEO_DRIVER_WAYLAND)
302 if (!initialized)
304 // glad will find default EGLDisplay internally.
305 ogl_Init(SDL_GL_GetProcAddress, nullptr, static_cast<int>(SDL_SYSWM_WAYLAND));
306 initialized = true;
308 #endif
309 if (!initialized)
311 LOGERROR("Can't initialize GL");
312 return nullptr;
314 #else
315 ogl_Init(SDL_GL_GetProcAddress);
316 #endif
318 #if OS_WIN || defined(SDL_VIDEO_DRIVER_X11) && !CONFIG2_GLES
319 // Hack to stop things looking very ugly when scrolling in Atlas.
320 ogl_SetVsyncEnabled(true);
321 #endif
324 // If we don't have GL2.0 then we don't have GLSL in core.
325 if (!arb && !ogl_HaveVersion(2, 0))
326 return nullptr;
328 if ((ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", nullptr) // ARB
329 && !ogl_HaveVersion(2, 0)) // GLSL
330 || !ogl_HaveExtension("GL_ARB_vertex_buffer_object") // VBO
331 || ogl_HaveExtensions(0, "GL_ARB_multitexture", "GL_EXT_draw_range_elements", nullptr)
332 || (!ogl_HaveExtension("GL_EXT_framebuffer_object") && !ogl_HaveExtension("GL_ARB_framebuffer_object")))
334 // It doesn't make sense to continue working here, because we're not
335 // able to display anything.
336 DEBUG_DISPLAY_FATAL_ERROR(
337 L"Your graphics card doesn't appear to be fully compatible with OpenGL shaders."
338 L" The game does not support pre-shader graphics cards."
339 L" You are advised to try installing newer drivers and/or upgrade your graphics card."
340 L" For more information, please see http://www.wildfiregames.com/forum/index.php?showtopic=16734"
344 device->m_ARB = arb;
346 device->m_Name = GetNameImpl();
347 device->m_Version = GetVersionImpl();
348 device->m_DriverInformation = GetDriverInformationImpl();
349 device->m_Extensions = GetExtensionsImpl();
351 // Set packing parameters for uploading and downloading data.
352 glPixelStorei(GL_PACK_ALIGNMENT, 1);
353 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
355 glEnable(GL_TEXTURE_2D);
356 // glEnable(GL_TEXTURE_2D) is deprecated and might trigger an error. But we
357 // still support pre 2.0 drivers pretending to support 2.0.
358 ogl_SquelchError(GL_INVALID_ENUM);
360 if (arb)
362 #if !CONFIG2_GLES
363 glEnable(GL_VERTEX_PROGRAM_ARB);
364 glEnable(GL_FRAGMENT_PROGRAM_ARB);
365 #endif
368 // Some drivers might invalidate an incorrect surface which leads to artifacts.
369 bool enableFramebufferInvalidating = false;
370 CFG_GET_VAL("renderer.backend.gl.enableframebufferinvalidating", enableFramebufferInvalidating);
371 if (enableFramebufferInvalidating)
373 #if CONFIG2_GLES
374 device->m_UseFramebufferInvalidating = ogl_HaveExtension("GL_EXT_discard_framebuffer");
375 #else
376 device->m_UseFramebufferInvalidating = !arb && ogl_HaveExtension("GL_ARB_invalidate_subdata");
377 #endif
380 Capabilities& capabilities = device->m_Capabilities;
381 capabilities.ARBShaders = !ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", nullptr);
382 if (capabilities.ARBShaders)
383 capabilities.ARBShadersShadow = ogl_HaveExtension("GL_ARB_fragment_program_shadow");
384 capabilities.computeShaders = ogl_HaveVersion(4, 3) || ogl_HaveExtension("GL_ARB_compute_shader");
385 #if CONFIG2_GLES
386 // Some GLES implementations have GL_EXT_texture_compression_dxt1
387 // but that only supports DXT1 so we can't use it.
388 capabilities.S3TC = ogl_HaveExtensions(0, "GL_EXT_texture_compression_s3tc", nullptr) == 0;
389 #else
390 // Note: we don't bother checking for GL_S3_s3tc - it is incompatible
391 // and irrelevant (was never widespread).
392 capabilities.S3TC = ogl_HaveExtensions(0, "GL_ARB_texture_compression", "GL_EXT_texture_compression_s3tc", nullptr) == 0;
393 #endif
394 #if CONFIG2_GLES
395 capabilities.multisampling = false;
396 capabilities.maxSampleCount = 1;
397 #else
398 capabilities.multisampling =
399 ogl_HaveVersion(3, 3) &&
400 ogl_HaveExtension("GL_ARB_multisample") &&
401 ogl_HaveExtension("GL_ARB_texture_multisample");
402 if (capabilities.multisampling)
404 // By default GL_MULTISAMPLE should be enabled, but enable it for buggy drivers.
405 glEnable(GL_MULTISAMPLE);
406 GLint maxSamples = 1;
407 glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
408 capabilities.maxSampleCount = maxSamples;
410 #endif
411 capabilities.anisotropicFiltering = ogl_HaveExtension("GL_EXT_texture_filter_anisotropic");
412 if (capabilities.anisotropicFiltering)
414 GLfloat maxAnisotropy = 1.0f;
415 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
416 capabilities.maxAnisotropy = maxAnisotropy;
418 GLint maxTextureSize = 1024;
419 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
420 capabilities.maxTextureSize = maxTextureSize;
422 #if CONFIG2_GLES
423 const bool isDebugInCore = ogl_HaveVersion(3, 2);
424 #else
425 const bool isDebugInCore = ogl_HaveVersion(4, 3);
426 #endif
427 const bool hasDebug = isDebugInCore || ogl_HaveExtension("GL_KHR_debug");
428 if (hasDebug)
430 #ifdef NDEBUG
431 bool enableDebugMessages = false;
432 CFG_GET_VAL("renderer.backend.debugmessages", enableDebugMessages);
433 capabilities.debugLabels = false;
434 CFG_GET_VAL("renderer.backend.debuglabels", capabilities.debugLabels);
435 capabilities.debugScopedLabels = false;
436 CFG_GET_VAL("renderer.backend.debugscopedlabels", capabilities.debugScopedLabels);
437 #else
438 const bool enableDebugMessages = true;
439 capabilities.debugLabels = true;
440 capabilities.debugScopedLabels = true;
441 #endif
442 if (enableDebugMessages)
444 glEnable(GL_DEBUG_OUTPUT);
445 #if !CONFIG2_GLES
446 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
447 #else
448 #warning GLES without GL_DEBUG_OUTPUT_SYNCHRONOUS might call the callback from different threads which might be unsafe.
449 #endif
450 glDebugMessageCallback(OnDebugMessage, nullptr);
452 // Filter out our own debug group messages
453 const GLuint id = 0x0AD;
454 glDebugMessageControl(
455 GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PUSH_GROUP, GL_DONT_CARE, 1, &id, GL_FALSE);
456 glDebugMessageControl(
457 GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_POP_GROUP, GL_DONT_CARE, 1, &id, GL_FALSE);
461 #if CONFIG2_GLES
462 capabilities.instancing = false;
463 #else
464 capabilities.instancing =
465 !device->m_ARB &&
466 (ogl_HaveVersion(3, 3) ||
467 (ogl_HaveExtension("GL_ARB_draw_instanced") &&
468 ogl_HaveExtension("GL_ARB_instanced_arrays")));
469 #endif
471 return device;
474 CDevice::CDevice() = default;
476 CDevice::~CDevice()
478 if (m_Context)
479 SDL_GL_DeleteContext(m_Context);
482 void CDevice::Report(const ScriptRequest& rq, JS::HandleValue settings)
484 const char* errstr = "(error)";
486 Script::SetProperty(rq, settings, "name", m_ARB ? "glarb" : "gl");
488 #define INTEGER(id) do { \
489 GLint i = -1; \
490 glGetIntegerv(GL_##id, &i); \
491 if (ogl_SquelchError(GL_INVALID_ENUM)) \
492 Script::SetProperty(rq, settings, "GL_" #id, errstr); \
493 else \
494 Script::SetProperty(rq, settings, "GL_" #id, i); \
495 } while (false)
497 #define INTEGER2(id) do { \
498 GLint i[2] = { -1, -1 }; \
499 glGetIntegerv(GL_##id, i); \
500 if (ogl_SquelchError(GL_INVALID_ENUM)) { \
501 Script::SetProperty(rq, settings, "GL_" #id "[0]", errstr); \
502 Script::SetProperty(rq, settings, "GL_" #id "[1]", errstr); \
503 } else { \
504 Script::SetProperty(rq, settings, "GL_" #id "[0]", i[0]); \
505 Script::SetProperty(rq, settings, "GL_" #id "[1]", i[1]); \
507 } while (false)
509 #define FLOAT(id) do { \
510 GLfloat f = std::numeric_limits<GLfloat>::quiet_NaN(); \
511 glGetFloatv(GL_##id, &f); \
512 if (ogl_SquelchError(GL_INVALID_ENUM)) \
513 Script::SetProperty(rq, settings, "GL_" #id, errstr); \
514 else \
515 Script::SetProperty(rq, settings, "GL_" #id, f); \
516 } while (false)
518 #define FLOAT2(id) do { \
519 GLfloat f[2] = { std::numeric_limits<GLfloat>::quiet_NaN(), std::numeric_limits<GLfloat>::quiet_NaN() }; \
520 glGetFloatv(GL_##id, f); \
521 if (ogl_SquelchError(GL_INVALID_ENUM)) { \
522 Script::SetProperty(rq, settings, "GL_" #id "[0]", errstr); \
523 Script::SetProperty(rq, settings, "GL_" #id "[1]", errstr); \
524 } else { \
525 Script::SetProperty(rq, settings, "GL_" #id "[0]", f[0]); \
526 Script::SetProperty(rq, settings, "GL_" #id "[1]", f[1]); \
528 } while (false)
530 #define STRING(id) do { \
531 const char* c = (const char*)glGetString(GL_##id); \
532 if (!c) c = ""; \
533 if (ogl_SquelchError(GL_INVALID_ENUM)) c = errstr; \
534 Script::SetProperty(rq, settings, "GL_" #id, std::string(c)); \
535 } while (false)
537 #define QUERY(target, pname) do { \
538 GLint i = -1; \
539 glGetQueryivARB(GL_##target, GL_##pname, &i); \
540 if (ogl_SquelchError(GL_INVALID_ENUM)) \
541 Script::SetProperty(rq, settings, "GL_" #target ".GL_" #pname, errstr); \
542 else \
543 Script::SetProperty(rq, settings, "GL_" #target ".GL_" #pname, i); \
544 } while (false)
546 #define VERTEXPROGRAM(id) do { \
547 GLint i = -1; \
548 glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_##id, &i); \
549 if (ogl_SquelchError(GL_INVALID_ENUM)) \
550 Script::SetProperty(rq, settings, "GL_VERTEX_PROGRAM_ARB.GL_" #id, errstr); \
551 else \
552 Script::SetProperty(rq, settings, "GL_VERTEX_PROGRAM_ARB.GL_" #id, i); \
553 } while (false)
555 #define FRAGMENTPROGRAM(id) do { \
556 GLint i = -1; \
557 glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_##id, &i); \
558 if (ogl_SquelchError(GL_INVALID_ENUM)) \
559 Script::SetProperty(rq, settings, "GL_FRAGMENT_PROGRAM_ARB.GL_" #id, errstr); \
560 else \
561 Script::SetProperty(rq, settings, "GL_FRAGMENT_PROGRAM_ARB.GL_" #id, i); \
562 } while (false)
564 #define BOOL(id) INTEGER(id)
566 ogl_WarnIfError();
568 // Core OpenGL 1.3:
569 // (We don't bother checking extension strings for anything older than 1.3;
570 // it'll just produce harmless warnings)
571 STRING(VERSION);
572 STRING(VENDOR);
573 STRING(RENDERER);
574 STRING(EXTENSIONS);
576 #if !CONFIG2_GLES
577 INTEGER(MAX_CLIP_PLANES);
578 #endif
579 INTEGER(SUBPIXEL_BITS);
580 #if !CONFIG2_GLES
581 INTEGER(MAX_3D_TEXTURE_SIZE);
582 #endif
583 INTEGER(MAX_TEXTURE_SIZE);
584 INTEGER(MAX_CUBE_MAP_TEXTURE_SIZE);
585 INTEGER2(MAX_VIEWPORT_DIMS);
587 #if !CONFIG2_GLES
588 BOOL(RGBA_MODE);
589 BOOL(INDEX_MODE);
590 BOOL(DOUBLEBUFFER);
591 BOOL(STEREO);
592 #endif
594 FLOAT2(ALIASED_POINT_SIZE_RANGE);
595 FLOAT2(ALIASED_LINE_WIDTH_RANGE);
596 #if !CONFIG2_GLES
597 INTEGER(MAX_ELEMENTS_INDICES);
598 INTEGER(MAX_ELEMENTS_VERTICES);
599 INTEGER(MAX_TEXTURE_UNITS);
600 #endif
601 INTEGER(SAMPLE_BUFFERS);
602 INTEGER(SAMPLES);
603 // TODO: compressed texture formats
604 INTEGER(RED_BITS);
605 INTEGER(GREEN_BITS);
606 INTEGER(BLUE_BITS);
607 INTEGER(ALPHA_BITS);
608 #if !CONFIG2_GLES
609 INTEGER(INDEX_BITS);
610 #endif
611 INTEGER(DEPTH_BITS);
612 INTEGER(STENCIL_BITS);
614 #if !CONFIG2_GLES
616 // Core OpenGL 2.0 (treated as extensions):
618 if (ogl_HaveExtension("GL_EXT_texture_lod_bias"))
620 FLOAT(MAX_TEXTURE_LOD_BIAS_EXT);
623 if (ogl_HaveExtension("GL_ARB_occlusion_query"))
625 QUERY(SAMPLES_PASSED, QUERY_COUNTER_BITS);
628 if (ogl_HaveExtension("GL_ARB_shading_language_100"))
630 STRING(SHADING_LANGUAGE_VERSION_ARB);
633 if (ogl_HaveExtension("GL_ARB_vertex_shader"))
635 INTEGER(MAX_VERTEX_ATTRIBS_ARB);
636 INTEGER(MAX_VERTEX_UNIFORM_COMPONENTS_ARB);
637 INTEGER(MAX_VARYING_FLOATS_ARB);
638 INTEGER(MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB);
639 INTEGER(MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB);
642 if (ogl_HaveExtension("GL_ARB_fragment_shader"))
644 INTEGER(MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB);
647 if (ogl_HaveExtension("GL_ARB_vertex_shader") || ogl_HaveExtension("GL_ARB_fragment_shader") ||
648 ogl_HaveExtension("GL_ARB_vertex_program") || ogl_HaveExtension("GL_ARB_fragment_program"))
650 INTEGER(MAX_TEXTURE_IMAGE_UNITS_ARB);
651 INTEGER(MAX_TEXTURE_COORDS_ARB);
654 if (ogl_HaveExtension("GL_ARB_draw_buffers"))
656 INTEGER(MAX_DRAW_BUFFERS_ARB);
659 // Core OpenGL 3.0:
661 if (ogl_HaveExtension("GL_EXT_gpu_shader4"))
663 INTEGER(MIN_PROGRAM_TEXEL_OFFSET_EXT); // no _EXT version of these in glext.h
664 INTEGER(MAX_PROGRAM_TEXEL_OFFSET_EXT);
667 if (ogl_HaveExtension("GL_EXT_framebuffer_object"))
669 INTEGER(MAX_COLOR_ATTACHMENTS_EXT);
670 INTEGER(MAX_RENDERBUFFER_SIZE_EXT);
673 if (ogl_HaveExtension("GL_EXT_framebuffer_multisample"))
675 INTEGER(MAX_SAMPLES_EXT);
678 if (ogl_HaveExtension("GL_EXT_texture_array"))
680 INTEGER(MAX_ARRAY_TEXTURE_LAYERS_EXT);
683 if (ogl_HaveExtension("GL_EXT_transform_feedback"))
685 INTEGER(MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT);
686 INTEGER(MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT);
687 INTEGER(MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT);
691 // Other interesting extensions:
693 if (ogl_HaveExtension("GL_EXT_timer_query") || ogl_HaveExtension("GL_ARB_timer_query"))
695 QUERY(TIME_ELAPSED, QUERY_COUNTER_BITS);
698 if (ogl_HaveExtension("GL_ARB_timer_query"))
700 QUERY(TIMESTAMP, QUERY_COUNTER_BITS);
703 if (ogl_HaveExtension("GL_EXT_texture_filter_anisotropic"))
705 FLOAT(MAX_TEXTURE_MAX_ANISOTROPY_EXT);
708 if (ogl_HaveExtension("GL_ARB_texture_rectangle"))
710 INTEGER(MAX_RECTANGLE_TEXTURE_SIZE_ARB);
713 if (m_ARB)
715 if (ogl_HaveExtension("GL_ARB_vertex_program") || ogl_HaveExtension("GL_ARB_fragment_program"))
717 INTEGER(MAX_PROGRAM_MATRICES_ARB);
718 INTEGER(MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB);
721 if (ogl_HaveExtension("GL_ARB_vertex_program"))
723 VERTEXPROGRAM(MAX_PROGRAM_ENV_PARAMETERS_ARB);
724 VERTEXPROGRAM(MAX_PROGRAM_LOCAL_PARAMETERS_ARB);
725 VERTEXPROGRAM(MAX_PROGRAM_INSTRUCTIONS_ARB);
726 VERTEXPROGRAM(MAX_PROGRAM_TEMPORARIES_ARB);
727 VERTEXPROGRAM(MAX_PROGRAM_PARAMETERS_ARB);
728 VERTEXPROGRAM(MAX_PROGRAM_ATTRIBS_ARB);
729 VERTEXPROGRAM(MAX_PROGRAM_ADDRESS_REGISTERS_ARB);
730 VERTEXPROGRAM(MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB);
731 VERTEXPROGRAM(MAX_PROGRAM_NATIVE_TEMPORARIES_ARB);
732 VERTEXPROGRAM(MAX_PROGRAM_NATIVE_PARAMETERS_ARB);
733 VERTEXPROGRAM(MAX_PROGRAM_NATIVE_ATTRIBS_ARB);
734 VERTEXPROGRAM(MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB);
736 if (ogl_HaveExtension("GL_ARB_fragment_program"))
738 // The spec seems to say these should be supported, but
739 // Mesa complains about them so let's not bother
741 VERTEXPROGRAM(MAX_PROGRAM_ALU_INSTRUCTIONS_ARB);
742 VERTEXPROGRAM(MAX_PROGRAM_TEX_INSTRUCTIONS_ARB);
743 VERTEXPROGRAM(MAX_PROGRAM_TEX_INDIRECTIONS_ARB);
744 VERTEXPROGRAM(MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB);
745 VERTEXPROGRAM(MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB);
746 VERTEXPROGRAM(MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB);
751 if (ogl_HaveExtension("GL_ARB_fragment_program"))
753 FRAGMENTPROGRAM(MAX_PROGRAM_ENV_PARAMETERS_ARB);
754 FRAGMENTPROGRAM(MAX_PROGRAM_LOCAL_PARAMETERS_ARB);
755 FRAGMENTPROGRAM(MAX_PROGRAM_INSTRUCTIONS_ARB);
756 FRAGMENTPROGRAM(MAX_PROGRAM_ALU_INSTRUCTIONS_ARB);
757 FRAGMENTPROGRAM(MAX_PROGRAM_TEX_INSTRUCTIONS_ARB);
758 FRAGMENTPROGRAM(MAX_PROGRAM_TEX_INDIRECTIONS_ARB);
759 FRAGMENTPROGRAM(MAX_PROGRAM_TEMPORARIES_ARB);
760 FRAGMENTPROGRAM(MAX_PROGRAM_PARAMETERS_ARB);
761 FRAGMENTPROGRAM(MAX_PROGRAM_ATTRIBS_ARB);
762 FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB);
763 FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB);
764 FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB);
765 FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB);
766 FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_TEMPORARIES_ARB);
767 FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_PARAMETERS_ARB);
768 FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_ATTRIBS_ARB);
770 if (ogl_HaveExtension("GL_ARB_vertex_program"))
772 // The spec seems to say these should be supported, but
773 // Intel drivers on Windows complain about them so let's not bother
775 FRAGMENTPROGRAM(MAX_PROGRAM_ADDRESS_REGISTERS_ARB);
776 FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB);
782 if (ogl_HaveExtension("GL_ARB_geometry_shader4"))
784 INTEGER(MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB);
785 INTEGER(MAX_GEOMETRY_OUTPUT_VERTICES_ARB);
786 INTEGER(MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB);
787 INTEGER(MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB);
788 INTEGER(MAX_GEOMETRY_VARYING_COMPONENTS_ARB);
789 INTEGER(MAX_VERTEX_VARYING_COMPONENTS_ARB);
792 #else // CONFIG2_GLES
794 // Core OpenGL ES 2.0:
796 STRING(SHADING_LANGUAGE_VERSION);
797 INTEGER(MAX_VERTEX_ATTRIBS);
798 INTEGER(MAX_VERTEX_UNIFORM_VECTORS);
799 INTEGER(MAX_VARYING_VECTORS);
800 INTEGER(MAX_COMBINED_TEXTURE_IMAGE_UNITS);
801 INTEGER(MAX_VERTEX_TEXTURE_IMAGE_UNITS);
802 INTEGER(MAX_FRAGMENT_UNIFORM_VECTORS);
803 INTEGER(MAX_TEXTURE_IMAGE_UNITS);
804 INTEGER(MAX_RENDERBUFFER_SIZE);
806 #endif // CONFIG2_GLES
809 // TODO: Support OpenGL platforms which don't use GLX as well.
810 #if defined(SDL_VIDEO_DRIVER_X11) && !CONFIG2_GLES
812 #define GLXQCR_INTEGER(id) do { \
813 unsigned int i = UINT_MAX; \
814 if (glXQueryCurrentRendererIntegerMESA(id, &i)) \
815 Script::SetProperty(rq, settings, #id, i); \
816 } while (false)
818 #define GLXQCR_INTEGER2(id) do { \
819 unsigned int i[2] = { UINT_MAX, UINT_MAX }; \
820 if (glXQueryCurrentRendererIntegerMESA(id, i)) { \
821 Script::SetProperty(rq, settings, #id "[0]", i[0]); \
822 Script::SetProperty(rq, settings, #id "[1]", i[1]); \
824 } while (false)
826 #define GLXQCR_INTEGER3(id) do { \
827 unsigned int i[3] = { UINT_MAX, UINT_MAX, UINT_MAX }; \
828 if (glXQueryCurrentRendererIntegerMESA(id, i)) { \
829 Script::SetProperty(rq, settings, #id "[0]", i[0]); \
830 Script::SetProperty(rq, settings, #id "[1]", i[1]); \
831 Script::SetProperty(rq, settings, #id "[2]", i[2]); \
833 } while (false)
835 #define GLXQCR_STRING(id) do { \
836 const char* str = glXQueryCurrentRendererStringMESA(id); \
837 if (str) \
838 Script::SetProperty(rq, settings, #id ".string", str); \
839 } while (false)
842 SDL_SysWMinfo wminfo;
843 SDL_VERSION(&wminfo.version);
844 const int ret = SDL_GetWindowWMInfo(m_Window, &wminfo);
845 if (ret && wminfo.subsystem == SDL_SYSWM_X11)
847 Display* dpy = wminfo.info.x11.display;
848 int scrnum = DefaultScreen(dpy);
850 const char* glxexts = glXQueryExtensionsString(dpy, scrnum);
852 Script::SetProperty(rq, settings, "glx_extensions", glxexts);
854 if (strstr(glxexts, "GLX_MESA_query_renderer") && glXQueryCurrentRendererIntegerMESA && glXQueryCurrentRendererStringMESA)
856 GLXQCR_INTEGER(GLX_RENDERER_VENDOR_ID_MESA);
857 GLXQCR_INTEGER(GLX_RENDERER_DEVICE_ID_MESA);
858 GLXQCR_INTEGER3(GLX_RENDERER_VERSION_MESA);
859 GLXQCR_INTEGER(GLX_RENDERER_ACCELERATED_MESA);
860 GLXQCR_INTEGER(GLX_RENDERER_VIDEO_MEMORY_MESA);
861 GLXQCR_INTEGER(GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA);
862 GLXQCR_INTEGER(GLX_RENDERER_PREFERRED_PROFILE_MESA);
863 GLXQCR_INTEGER2(GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA);
864 GLXQCR_INTEGER2(GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA);
865 GLXQCR_INTEGER2(GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA);
866 GLXQCR_INTEGER2(GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA);
867 GLXQCR_STRING(GLX_RENDERER_VENDOR_ID_MESA);
868 GLXQCR_STRING(GLX_RENDERER_DEVICE_ID_MESA);
871 #endif // SDL_VIDEO_DRIVER_X11
874 std::unique_ptr<IDeviceCommandContext> CDevice::CreateCommandContext()
876 std::unique_ptr<CDeviceCommandContext> commandContet = CDeviceCommandContext::Create(this);
877 m_ActiveCommandContext = commandContet.get();
878 return commandContet;
881 std::unique_ptr<IGraphicsPipelineState> CDevice::CreateGraphicsPipelineState(
882 const SGraphicsPipelineStateDesc& pipelineStateDesc)
884 return CGraphicsPipelineState::Create(this, pipelineStateDesc);
887 std::unique_ptr<IVertexInputLayout> CDevice::CreateVertexInputLayout(
888 const PS::span<const SVertexAttributeFormat> attributes)
890 return std::make_unique<CVertexInputLayout>(this, attributes);
893 std::unique_ptr<ITexture> CDevice::CreateTexture(
894 const char* name, const ITexture::Type type, const uint32_t usage,
895 const Format format, const uint32_t width, const uint32_t height,
896 const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount)
898 return CTexture::Create(this, name, type, usage,
899 format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount);
902 std::unique_ptr<ITexture> CDevice::CreateTexture2D(
903 const char* name, const uint32_t usage,
904 const Format format, const uint32_t width, const uint32_t height,
905 const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount)
907 return CreateTexture(name, CTexture::Type::TEXTURE_2D, usage,
908 format, width, height, defaultSamplerDesc, MIPLevelCount, sampleCount);
911 std::unique_ptr<IFramebuffer> CDevice::CreateFramebuffer(
912 const char* name, SColorAttachment* colorAttachment,
913 SDepthStencilAttachment* depthStencilAttachment)
915 return CFramebuffer::Create(
916 this, name, colorAttachment, depthStencilAttachment);
919 std::unique_ptr<IBuffer> CDevice::CreateBuffer(
920 const char* name, const IBuffer::Type type, const uint32_t size, const bool dynamic)
922 return CBuffer::Create(this, name, type, size, dynamic);
925 std::unique_ptr<IShaderProgram> CDevice::CreateShaderProgram(
926 const CStr& name, const CShaderDefines& defines)
928 return CShaderProgram::Create(this, name, defines);
931 bool CDevice::AcquireNextBackbuffer()
933 ENSURE(!m_BackbufferAcquired);
934 m_BackbufferAcquired = true;
935 return true;
938 size_t CDevice::BackbufferKeyHash::operator()(const BackbufferKey& key) const
940 size_t seed = 0;
941 hash_combine(seed, std::get<0>(key));
942 hash_combine(seed, std::get<1>(key));
943 hash_combine(seed, std::get<2>(key));
944 hash_combine(seed, std::get<3>(key));
945 return seed;
948 IFramebuffer* CDevice::GetCurrentBackbuffer(
949 const AttachmentLoadOp colorAttachmentLoadOp,
950 const AttachmentStoreOp colorAttachmentStoreOp,
951 const AttachmentLoadOp depthStencilAttachmentLoadOp,
952 const AttachmentStoreOp depthStencilAttachmentStoreOp)
954 const BackbufferKey key{
955 colorAttachmentLoadOp, colorAttachmentStoreOp,
956 depthStencilAttachmentLoadOp, depthStencilAttachmentStoreOp};
957 auto it = m_Backbuffers.find(key);
958 if (it == m_Backbuffers.end())
960 it = m_Backbuffers.emplace(key, CFramebuffer::CreateBackbuffer(
961 this, m_SurfaceDrawableWidth, m_SurfaceDrawableHeight,
962 colorAttachmentLoadOp, colorAttachmentStoreOp,
963 depthStencilAttachmentLoadOp, depthStencilAttachmentStoreOp)).first;
965 return it->second.get();
968 void CDevice::Present()
970 ENSURE(m_BackbufferAcquired);
971 m_BackbufferAcquired = false;
973 if (m_Window)
975 PROFILE3("swap buffers");
976 SDL_GL_SwapWindow(m_Window);
977 ogl_WarnIfError();
980 bool checkGLErrorAfterSwap = false;
981 CFG_GET_VAL("gl.checkerrorafterswap", checkGLErrorAfterSwap);
982 #if defined(NDEBUG)
983 if (!checkGLErrorAfterSwap)
984 return;
985 #endif
986 PROFILE3("error check");
987 // We have to check GL errors after SwapBuffer to avoid possible
988 // synchronizations during rendering.
989 if (GLenum err = glGetError())
990 ONCE(LOGERROR("GL error %s (0x%04x) occurred", ogl_GetErrorName(err), err));
993 void CDevice::OnWindowResize(const uint32_t width, const uint32_t height)
995 ENSURE(!m_BackbufferAcquired);
996 m_Backbuffers.clear();
997 m_SurfaceDrawableWidth = width;
998 m_SurfaceDrawableHeight = height;
1001 bool CDevice::IsTextureFormatSupported(const Format format) const
1003 bool supported = false;
1004 switch (format)
1006 case Format::UNDEFINED:
1007 break;
1009 case Format::R8G8B8_UNORM: FALLTHROUGH;
1010 case Format::R8G8B8A8_UNORM: FALLTHROUGH;
1011 case Format::A8_UNORM: FALLTHROUGH;
1012 case Format::L8_UNORM:
1013 supported = true;
1014 break;
1016 case Format::R32_SFLOAT: FALLTHROUGH;
1017 case Format::R32G32_SFLOAT: FALLTHROUGH;
1018 case Format::R32G32B32_SFLOAT: FALLTHROUGH;
1019 case Format::R32G32B32A32_SFLOAT:
1020 break;
1022 case Format::D16_UNORM: FALLTHROUGH;
1023 case Format::D24_UNORM: FALLTHROUGH;
1024 case Format::D32_SFLOAT:
1025 supported = true;
1026 break;
1027 case Format::D24_UNORM_S8_UINT:
1028 #if !CONFIG2_GLES
1029 supported = true;
1030 #endif
1031 break;
1033 case Format::D32_SFLOAT_S8_UINT:
1034 break;
1036 case Format::BC1_RGB_UNORM: FALLTHROUGH;
1037 case Format::BC1_RGBA_UNORM: FALLTHROUGH;
1038 case Format::BC2_UNORM: FALLTHROUGH;
1039 case Format::BC3_UNORM:
1040 supported = m_Capabilities.S3TC;
1041 break;
1043 default:
1044 break;
1046 return supported;
1049 bool CDevice::IsFramebufferFormatSupported(const Format format) const
1051 bool supported = false;
1052 switch (format)
1054 case Format::UNDEFINED:
1055 break;
1056 #if !CONFIG2_GLES
1057 case Format::R8_UNORM:
1058 supported = ogl_HaveVersion(3, 0);
1059 break;
1060 #endif
1061 case Format::R8G8B8A8_UNORM:
1062 supported = true;
1063 break;
1064 default:
1065 break;
1067 return supported;
1070 Format CDevice::GetPreferredDepthStencilFormat(
1071 const uint32_t UNUSED(usage), const bool depth, const bool stencil) const
1073 ENSURE(depth || stencil);
1074 if (stencil)
1075 #if CONFIG2_GLES
1076 return Format::UNDEFINED;
1077 #else
1078 return Format::D24_UNORM_S8_UINT;
1079 #endif
1080 else
1081 return Format::D24_UNORM;
1084 std::unique_ptr<IDevice> CreateDevice(SDL_Window* window, const bool arb)
1086 return GL::CDevice::Create(window, arb);
1089 } // namespace GL
1091 } // namespace Backend
1093 } // namespace Renderer