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"
22 #include "lib/external_libraries/libsdl.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"
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();
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)
53 #if defined(SDL_VIDEO_DRIVER_WAYLAND)
56 #include <SDL_syswm.h>
58 #endif // !CONFIG2_GLES && (defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND))
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
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
)
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
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
;
111 driverInfo
= CStrW(wgfx_DriverInfo()).ToUTF8();
112 if (driverInfo
.empty())
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())
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());
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";
147 case GL_DEBUG_SOURCE_API
:
148 debugSource
= "the API";
150 case GL_DEBUG_SOURCE_WINDOW_SYSTEM
:
151 debugSource
= "the window system";
153 case GL_DEBUG_SOURCE_SHADER_COMPILER
:
154 debugSource
= "the shader compiler";
156 case GL_DEBUG_SOURCE_THIRD_PARTY
:
157 debugSource
= "a third party";
159 case GL_DEBUG_SOURCE_APPLICATION
:
160 debugSource
= "the application";
162 case GL_DEBUG_SOURCE_OTHER
:
163 debugSource
= "somewhere";
169 case GL_DEBUG_TYPE_ERROR
:
172 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR
:
173 debugType
= "deprecated behaviour";
175 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR
:
176 debugType
= "undefined behaviour";
178 case GL_DEBUG_TYPE_PORTABILITY
:
179 debugType
= "portability";
181 case GL_DEBUG_TYPE_PERFORMANCE
:
182 debugType
= "performance";
184 case GL_DEBUG_TYPE_OTHER
:
187 case GL_DEBUG_TYPE_MARKER
:
188 debugType
= "marker";
190 case GL_DEBUG_TYPE_PUSH_GROUP
:
191 debugType
= "push group";
193 case GL_DEBUG_TYPE_POP_GROUP
:
194 debugType
= "pop group";
200 case GL_DEBUG_SEVERITY_HIGH
:
201 debugSeverity
= "high";
203 case GL_DEBUG_SEVERITY_MEDIUM
:
204 debugSeverity
= "medium";
206 case GL_DEBUG_SEVERITY_LOW
:
207 debugSeverity
= "low";
209 case GL_DEBUG_SEVERITY_NOTIFICATION
:
210 debugSeverity
= "notification";
214 if (severity
== GL_DEBUG_SEVERITY_NOTIFICATION
)
217 "OpenGL | %s: %s source: %s id %u: %s\n", debugSeverity
.c_str(), debugType
.c_str(), debugSource
.c_str(), id
, message
);
222 "OpenGL | %s: %s source: %s id %u: %s\n", debugSeverity
.c_str(), debugType
.c_str(), debugSource
.c_str(), id
, message
);
226 } // anonymous namespace
229 std::unique_ptr
<IDevice
> CDevice::Create(SDL_Window
* window
, const bool arb
)
231 std::unique_ptr
<CDevice
> device(new CDevice());
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());
245 SDL_GL_GetDrawableSize(window
, &device
->m_SurfaceDrawableWidth
, &device
->m_SurfaceDrawableHeight
);
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());
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
));
269 #if defined(SDL_VIDEO_DRIVER_X11)
271 ogl_Init(SDL_GL_GetProcAddress
,
272 GetX11Display(device
->m_Window
),
273 static_cast<int>(wminfo
.subsystem
));
277 ogl_Init(SDL_GL_GetProcAddress
, nullptr,
278 static_cast<int>(wminfo
.subsystem
));
282 ogl_Init(SDL_GL_GetProcAddress
);
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
);
297 ogl_Init(SDL_GL_GetProcAddress
, display
, static_cast<int>(SDL_SYSWM_X11
));
301 #if defined(SDL_VIDEO_DRIVER_WAYLAND)
304 // glad will find default EGLDisplay internally.
305 ogl_Init(SDL_GL_GetProcAddress
, nullptr, static_cast<int>(SDL_SYSWM_WAYLAND
));
311 LOGERROR("Can't initialize GL");
315 ogl_Init(SDL_GL_GetProcAddress
);
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);
324 // If we don't have GL2.0 then we don't have GLSL in core.
325 if (!arb
&& !ogl_HaveVersion(2, 0))
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"
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
);
363 glEnable(GL_VERTEX_PROGRAM_ARB
);
364 glEnable(GL_FRAGMENT_PROGRAM_ARB
);
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
)
374 device
->m_UseFramebufferInvalidating
= ogl_HaveExtension("GL_EXT_discard_framebuffer");
376 device
->m_UseFramebufferInvalidating
= !arb
&& ogl_HaveExtension("GL_ARB_invalidate_subdata");
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");
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;
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;
395 capabilities
.multisampling
= false;
396 capabilities
.maxSampleCount
= 1;
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
;
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
;
423 const bool isDebugInCore
= ogl_HaveVersion(3, 2);
425 const bool isDebugInCore
= ogl_HaveVersion(4, 3);
427 const bool hasDebug
= isDebugInCore
|| ogl_HaveExtension("GL_KHR_debug");
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
);
438 const bool enableDebugMessages
= true;
439 capabilities
.debugLabels
= true;
440 capabilities
.debugScopedLabels
= true;
442 if (enableDebugMessages
)
444 glEnable(GL_DEBUG_OUTPUT
);
446 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS
);
448 #warning GLES without GL_DEBUG_OUTPUT_SYNCHRONOUS might call the callback from different threads which might be unsafe.
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
);
462 capabilities
.instancing
= false;
464 capabilities
.instancing
=
466 (ogl_HaveVersion(3, 3) ||
467 (ogl_HaveExtension("GL_ARB_draw_instanced") &&
468 ogl_HaveExtension("GL_ARB_instanced_arrays")));
474 CDevice::CDevice() = default;
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 { \
490 glGetIntegerv(GL_##id, &i); \
491 if (ogl_SquelchError(GL_INVALID_ENUM)) \
492 Script::SetProperty(rq, settings, "GL_" #id, errstr); \
494 Script::SetProperty(rq, settings, "GL_" #id, i); \
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); \
504 Script::SetProperty(rq, settings, "GL_" #id "[0]", i[0]); \
505 Script::SetProperty(rq, settings, "GL_" #id "[1]", i[1]); \
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); \
515 Script::SetProperty(rq, settings, "GL_" #id, f); \
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); \
525 Script::SetProperty(rq, settings, "GL_" #id "[0]", f[0]); \
526 Script::SetProperty(rq, settings, "GL_" #id "[1]", f[1]); \
530 #define STRING(id) do { \
531 const char* c = (const char*)glGetString(GL_##id); \
533 if (ogl_SquelchError(GL_INVALID_ENUM)) c = errstr; \
534 Script::SetProperty(rq, settings, "GL_" #id, std::string(c)); \
537 #define QUERY(target, pname) do { \
539 glGetQueryivARB(GL_##target, GL_##pname, &i); \
540 if (ogl_SquelchError(GL_INVALID_ENUM)) \
541 Script::SetProperty(rq, settings, "GL_" #target ".GL_" #pname, errstr); \
543 Script::SetProperty(rq, settings, "GL_" #target ".GL_" #pname, i); \
546 #define VERTEXPROGRAM(id) do { \
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); \
552 Script::SetProperty(rq, settings, "GL_VERTEX_PROGRAM_ARB.GL_" #id, i); \
555 #define FRAGMENTPROGRAM(id) do { \
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); \
561 Script::SetProperty(rq, settings, "GL_FRAGMENT_PROGRAM_ARB.GL_" #id, i); \
564 #define BOOL(id) INTEGER(id)
569 // (We don't bother checking extension strings for anything older than 1.3;
570 // it'll just produce harmless warnings)
577 INTEGER(MAX_CLIP_PLANES
);
579 INTEGER(SUBPIXEL_BITS
);
581 INTEGER(MAX_3D_TEXTURE_SIZE
);
583 INTEGER(MAX_TEXTURE_SIZE
);
584 INTEGER(MAX_CUBE_MAP_TEXTURE_SIZE
);
585 INTEGER2(MAX_VIEWPORT_DIMS
);
594 FLOAT2(ALIASED_POINT_SIZE_RANGE
);
595 FLOAT2(ALIASED_LINE_WIDTH_RANGE
);
597 INTEGER(MAX_ELEMENTS_INDICES
);
598 INTEGER(MAX_ELEMENTS_VERTICES
);
599 INTEGER(MAX_TEXTURE_UNITS
);
601 INTEGER(SAMPLE_BUFFERS
);
603 // TODO: compressed texture formats
612 INTEGER(STENCIL_BITS
);
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
);
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
);
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); \
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]); \
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]); \
835 #define GLXQCR_STRING(id) do { \
836 const char* str = glXQueryCurrentRendererStringMESA(id); \
838 Script::SetProperty(rq, settings, #id ".string", str); \
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;
938 size_t CDevice::BackbufferKeyHash::operator()(const BackbufferKey
& key
) const
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
));
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;
975 PROFILE3("swap buffers");
976 SDL_GL_SwapWindow(m_Window
);
980 bool checkGLErrorAfterSwap
= false;
981 CFG_GET_VAL("gl.checkerrorafterswap", checkGLErrorAfterSwap
);
983 if (!checkGLErrorAfterSwap
)
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;
1006 case Format::UNDEFINED
:
1009 case Format::R8G8B8_UNORM
: FALLTHROUGH
;
1010 case Format::R8G8B8A8_UNORM
: FALLTHROUGH
;
1011 case Format::A8_UNORM
: FALLTHROUGH
;
1012 case Format::L8_UNORM
:
1016 case Format::R32_SFLOAT
: FALLTHROUGH
;
1017 case Format::R32G32_SFLOAT
: FALLTHROUGH
;
1018 case Format::R32G32B32_SFLOAT
: FALLTHROUGH
;
1019 case Format::R32G32B32A32_SFLOAT
:
1022 case Format::D16_UNORM
: FALLTHROUGH
;
1023 case Format::D24_UNORM
: FALLTHROUGH
;
1024 case Format::D32_SFLOAT
:
1027 case Format::D24_UNORM_S8_UINT
:
1033 case Format::D32_SFLOAT_S8_UINT
:
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
;
1049 bool CDevice::IsFramebufferFormatSupported(const Format format
) const
1051 bool supported
= false;
1054 case Format::UNDEFINED
:
1057 case Format::R8_UNORM
:
1058 supported
= ogl_HaveVersion(3, 0);
1061 case Format::R8G8B8A8_UNORM
:
1070 Format
CDevice::GetPreferredDepthStencilFormat(
1071 const uint32_t UNUSED(usage
), const bool depth
, const bool stencil
) const
1073 ENSURE(depth
|| stencil
);
1076 return Format::UNDEFINED
;
1078 return Format::D24_UNORM_S8_UINT
;
1081 return Format::D24_UNORM
;
1084 std::unique_ptr
<IDevice
> CreateDevice(SDL_Window
* window
, const bool arb
)
1086 return GL::CDevice::Create(window
, arb
);
1091 } // namespace Backend
1093 } // namespace Renderer