Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
GLWrapper.cpp
Go to the documentation of this file.
1
2
3#include "Headers/GLWrapper.h"
4
8
13
15
16#include "GUI/Headers/GUI.h"
17
20
22
25
30#include <glbinding/Binding.h>
31
32namespace Divide
33{
34
36 std::atomic_bool GL_API::s_glFlushQueued{false};
37 NO_DESTROY GLUtil::glTextureViewCache GL_API::s_textureViewCache{};
41 std::unique_ptr<glHardwareQueryPool> GL_API::s_hardwareQueryPool = nullptr;
42 NO_DESTROY eastl::fixed_vector<GL_API::TexBindEntry, GLStateTracker::MAX_BOUND_TEXTURE_UNITS, false> GL_API::s_TexBindQueue;
43
44 NO_DESTROY std::array<GLUtil::GLMemory::DeviceAllocator, to_base( GLUtil::GLMemory::GLMemoryType::COUNT )> GL_API::s_memoryAllocators = {
45 GLUtil::GLMemory::DeviceAllocator( GLUtil::GLMemory::GLMemoryType::SHADER_BUFFER ),
46 GLUtil::GLMemory::DeviceAllocator( GLUtil::GLMemory::GLMemoryType::UNIFORM_BUFFER ),
47 GLUtil::GLMemory::DeviceAllocator( GLUtil::GLMemory::GLMemoryType::VERTEX_BUFFER ),
48 GLUtil::GLMemory::DeviceAllocator( GLUtil::GLMemory::GLMemoryType::INDEX_BUFFER ),
49 GLUtil::GLMemory::DeviceAllocator( GLUtil::GLMemory::GLMemoryType::OTHER )
50 };
51
53 TO_MEGABYTES( 1 << 10 ),
54 TO_MEGABYTES( 1 << 8 ),
55 TO_MEGABYTES( 1 << 9 ),
56 TO_MEGABYTES( 1 << 7 ),
57 TO_MEGABYTES( 1 << 7 )
58 };
59
60 constexpr bool g_breakOnGLCall = false;
61
62 namespace
63 {
65 {
66 SDL_GLContext _context = nullptr;
67 bool _inUse = false;
68 };
69
71 {
72 bool init( const size_t size, const DisplayWindow& window )
73 {
74 SDL_Window* raw = window.getRawWindow();
75 _contexts.resize( size );
76 GLUtil::ValidateSDL( SDL_GL_MakeCurrent( window.getRawWindow(), window.userData()._glContext ) );
77 for ( SDLContextEntry& contextEntry : _contexts )
78 {
79 contextEntry._context = SDL_GL_CreateContext( raw );
80 }
81 GLUtil::ValidateSDL( SDL_GL_MakeCurrent( window.getRawWindow(), nullptr ) );
82 return true;
83 }
84
85 bool destroy() noexcept
86 {
87 for ( const SDLContextEntry& contextEntry : _contexts )
88 {
89 SDL_GL_DeleteContext( contextEntry._context );
90 }
91 _contexts.clear();
92 return true;
93 }
94
95 bool getAvailableContext( SDL_GLContext& ctx ) noexcept
96 {
97 assert( !_contexts.empty() );
98 for ( SDLContextEntry& contextEntry : _contexts )
99 {
100 if ( !contextEntry._inUse )
101 {
102 ctx = contextEntry._context;
103 contextEntry._inUse = true;
104 return true;
105 }
106 }
107
108 return false;
109 }
110
112 } g_ContextPool;
113 }
114
117 _context( context ),
118 _swapBufferTimer( Time::ADD_TIMER( "Swap Buffer Timer" ) )
119 {
120 }
121
123 ErrorCode GL_API::initRenderingAPI( [[maybe_unused]] I32 argc, [[maybe_unused]] char** argv, Configuration& config )
124 {
125 const DisplayWindow& window = *_context.context().app().windowManager().mainWindow();
127
129
130 GLUtil::ValidateSDL( SDL_GL_MakeCurrent( window.getRawWindow(), window.userData()._glContext ) );
131 glbinding::Binding::initialize( []( const char* proc ) noexcept
132 {
133 return (glbinding::ProcAddress)SDL_GL_GetProcAddress( proc );
134 }, false );
135
136 // OpenGL has a nifty error callback system, available in every build configuration if required
137 if constexpr ( Config::ENABLE_GPU_VALIDATION )
138 {
140 {
141 // GL_DEBUG_OUTPUT_SYNCHRONOUS is essential for debugging gl commands in the IDE
142 gl46core::glEnable( gl46core::GL_DEBUG_OUTPUT );
143 gl46core::glEnable( gl46core::GL_DEBUG_OUTPUT_SYNCHRONOUS );
144 // hard-wire our debug callback function with OpenGL's implementation
145 gl46core::glDebugMessageControl( gl46core::GL_DONT_CARE, gl46core::GL_DEBUG_TYPE_MARKER, gl46core::GL_DONT_CARE, 0, NULL, gl46core::GL_FALSE );
146 gl46core::glDebugMessageControl( gl46core::GL_DONT_CARE, gl46core::GL_DEBUG_TYPE_PUSH_GROUP, gl46core::GL_DONT_CARE, 0, NULL, gl46core::GL_FALSE );
147 gl46core::glDebugMessageControl( gl46core::GL_DONT_CARE, gl46core::GL_DEBUG_TYPE_POP_GROUP, gl46core::GL_DONT_CARE, 0, NULL, gl46core::GL_FALSE );
149 {
150 gl46core::glDebugMessageControl( gl46core::GL_DONT_CARE, gl46core::GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, gl46core::GL_DONT_CARE, 0, NULL, gl46core::GL_FALSE );
151 gl46core::glDebugMessageControl( gl46core::GL_DONT_CARE, gl46core::GL_DEBUG_TYPE_PORTABILITY, gl46core::GL_DONT_CARE, 0, NULL, gl46core::GL_FALSE );
152 gl46core::glDebugMessageControl( gl46core::GL_DONT_CARE, gl46core::GL_DEBUG_TYPE_PERFORMANCE, gl46core::GL_DONT_CARE, 0, NULL, gl46core::GL_FALSE );
153 }
154 gl46core::glDebugMessageCallback( (gl46core::GLDEBUGPROC)GLUtil::DebugCallback, nullptr );
155 }
156 }
157
158
159 if ( SDL_GL_GetCurrentContext() == nullptr )
160 {
162 }
163
164 if constexpr ( g_breakOnGLCall )
165 {
166 glbinding::Binding::setCallbackMaskExcept( glbinding::CallbackMask::Before, { "glGetError" } );
167 glbinding::Binding::setBeforeCallback( [&]( const glbinding::FunctionCall& )
168 {
169 DebugBreak();
170 });
171 }
172
174
175 // Query GPU vendor to enable/disable vendor specific features
177 const char* gpuVendorStr = reinterpret_cast<const char*>(gl46core::glGetString( gl46core::GL_VENDOR ));
178 if ( gpuVendorStr != nullptr )
179 {
180 if ( strstr( gpuVendorStr, "Intel" ) != nullptr )
181 {
182 vendor = GPUVendor::INTEL;
183 }
184 else if ( strstr( gpuVendorStr, "NVIDIA" ) != nullptr )
185 {
186 vendor = GPUVendor::NVIDIA;
187 }
188 else if ( strstr( gpuVendorStr, "ATI" ) != nullptr || strstr( gpuVendorStr, "AMD" ) != nullptr )
189 {
190 vendor = GPUVendor::AMD;
191 }
192 else if ( strstr( gpuVendorStr, "Microsoft" ) != nullptr )
193 {
194 vendor = GPUVendor::MICROSOFT;
195 }
196 else if ( strstr( gpuVendorStr, "Mesa" ) != nullptr )
197 {
198 vendor = GPUVendor::MESA;
199 }
200 else
201 {
202 vendor = GPUVendor::OTHER;
203 }
204 }
205 else
206 {
207 gpuVendorStr = "Unknown GPU Vendor";
208 vendor = GPUVendor::OTHER;
209 }
211 const char* gpuRendererStr = reinterpret_cast<const char*>(gl46core::glGetString( gl46core::GL_RENDERER ));
212 if ( gpuRendererStr != nullptr )
213 {
214 if ( strstr( gpuRendererStr, "Tegra" ) || strstr( gpuRendererStr, "GeForce" ) || strstr( gpuRendererStr, "NV" ) )
215 {
216 renderer = GPURenderer::GEFORCE;
217 }
218 else if ( strstr( gpuRendererStr, "PowerVR" ) || strstr( gpuRendererStr, "Apple" ) )
219 {
220 renderer = GPURenderer::POWERVR;
222 }
223 else if ( strstr( gpuRendererStr, "Mali" ) )
224 {
225 renderer = GPURenderer::MALI;
226 vendor = GPUVendor::ARM;
227 }
228 else if ( strstr( gpuRendererStr, "Adreno" ) )
229 {
230 renderer = GPURenderer::ADRENO;
231 vendor = GPUVendor::QUALCOMM;
232 }
233 else if ( strstr( gpuRendererStr, "AMD" ) || strstr( gpuRendererStr, "ATI" ) )
234 {
235 renderer = GPURenderer::RADEON;
236 }
237 else if ( strstr( gpuRendererStr, "Intel" ) )
238 {
239 renderer = GPURenderer::INTEL;
240 }
241 else if ( strstr( gpuRendererStr, "Vivante" ) )
242 {
243 renderer = GPURenderer::VIVANTE;
244 vendor = GPUVendor::VIVANTE;
245 }
246 else if ( strstr( gpuRendererStr, "VideoCore" ) )
247 {
248 renderer = GPURenderer::VIDEOCORE;
249 vendor = GPUVendor::ALPHAMOSAIC;
250 }
251 else if ( strstr( gpuRendererStr, "WebKit" ) || strstr( gpuRendererStr, "Mozilla" ) || strstr( gpuRendererStr, "ANGLE" ) )
252 {
253 renderer = GPURenderer::WEBGL;
254 vendor = GPUVendor::WEBGL;
255 }
256 else if ( strstr( gpuRendererStr, "GDI Generic" ) )
257 {
258 renderer = GPURenderer::GDI;
259 }
260 else if ( strstr( gpuRendererStr, "Mesa" ) )
261 {
262 renderer = GPURenderer::SOFTWARE;
263 }
264 else
265 {
266 renderer = GPURenderer::UNKNOWN;
267 }
268 }
269 else
270 {
271 gpuRendererStr = "Unknown GPU Renderer";
272 renderer = GPURenderer::UNKNOWN;
273 }
274 // GPU info, including vendor, gpu and driver
275 Console::printfn( LOCALE_STR( "GL_VENDOR_STRING" ), gpuVendorStr, gpuRendererStr, reinterpret_cast<const char*>(gl46core::glGetString( gl46core::GL_VERSION )) );
276
277 DeviceInformation deviceInformation{};
278 deviceInformation._vendor = vendor;
279 deviceInformation._renderer = renderer;
280
281 if ( s_hardwareQueryPool == nullptr )
282 {
283 s_hardwareQueryPool = std::make_unique<glHardwareQueryPool>( _context );
284 }
285
286 // If we got here, let's figure out what capabilities we have available
287 // Maximum addressable texture image units in the fragment shader
288 deviceInformation._maxTextureUnits = CLAMPED( GLUtil::getGLValue( gl46core::GL_MAX_TEXTURE_IMAGE_UNITS ), 16, 255 );
289 DIVIDE_ASSERT( deviceInformation._maxTextureUnits >= GLStateTracker::MAX_BOUND_TEXTURE_UNITS );
290
291 GLUtil::getGLValue( gl46core::GL_MAX_VERTEX_ATTRIB_BINDINGS, deviceInformation._maxVertAttributeBindings );
292
293 GLUtil::getGLValue( gl46core::GL_MAX_TEXTURE_SIZE, deviceInformation._maxTextureSize );
294
295 deviceInformation._versionInfo._major = to_U8( GLUtil::getGLValue( gl46core::GL_MAJOR_VERSION ) );
296 deviceInformation._versionInfo._minor = to_U8( GLUtil::getGLValue( gl46core::GL_MINOR_VERSION ) );
297 Console::printfn( LOCALE_STR( "GL_MAX_VERSION" ), deviceInformation._versionInfo._major, deviceInformation._versionInfo._minor );
298
299 if ( deviceInformation._versionInfo._major < 4 || (deviceInformation._versionInfo._major == 4 && deviceInformation._versionInfo._minor < 6) )
300 {
301 Console::errorfn( LOCALE_STR( "ERROR_OPENGL_VERSION_TO_OLD" ) );
303 }
304
305 // Maximum number of colour attachments per framebuffer
306 GLUtil::getGLValue( gl46core::GL_MAX_COLOR_ATTACHMENTS, deviceInformation._maxRTColourAttachments );
307
308 deviceInformation._shaderCompilerThreads = GLUtil::getGLValue( gl::GL_MAX_SHADER_COMPILER_THREADS_ARB );
309 Console::printfn( LOCALE_STR( "GL_SHADER_THREADS" ), deviceInformation._shaderCompilerThreads );
310 gl::glMaxShaderCompilerThreadsARB( deviceInformation._shaderCompilerThreads );
311
312 gl46core::glEnable( gl46core::GL_MULTISAMPLE );
313 // Line smoothing should almost always be used
314 gl46core::glEnable( gl46core::GL_LINE_SMOOTH );
315
316 // GL_FALSE causes a conflict here. Thanks glbinding ...
317 gl46core::glClampColor( gl46core::GL_CLAMP_READ_COLOR, gl46core::GL_FALSE );
318
319 // Match Vulkan's depth range
320 gl46core::glClipControl( gl46core::GL_LOWER_LEFT, gl46core::GL_ZERO_TO_ONE );
321
322 // Cap max anisotropic level to what the hardware supports
324 U8_ZERO,
325 to_U8( GLUtil::getGLValue( gl46core::GL_MAX_TEXTURE_MAX_ANISOTROPY ) ) );
326
327 deviceInformation._maxAnisotropy = config.rendering.maxAnisotropicFilteringLevel;
328
329 // Number of sample buffers associated with the framebuffer & MSAA sample count
330 const U8 maxGLSamples = to_U8( std::min( 254, GLUtil::getGLValue( gl46core::GL_MAX_SAMPLES ) ) );
331 // If we do not support MSAA on a hardware level for whatever reason, override user set MSAA levels
332 config.rendering.MSAASamples = std::min( config.rendering.MSAASamples, maxGLSamples );
333
334 config.rendering.shadowMapping.csm.MSAASamples = std::min( config.rendering.shadowMapping.csm.MSAASamples, maxGLSamples );
335 config.rendering.shadowMapping.spot.MSAASamples = std::min( config.rendering.shadowMapping.spot.MSAASamples, maxGLSamples );
337
338 // Print all of the OpenGL functionality info to the console and log
339 // How many uniforms can we send to fragment shaders
340 Console::printfn( LOCALE_STR( "GL_MAX_UNIFORM" ), GLUtil::getGLValue( gl46core::GL_MAX_FRAGMENT_UNIFORM_COMPONENTS ) );
341 // How many uniforms can we send to vertex shaders
342 Console::printfn( LOCALE_STR( "GL_MAX_VERT_UNIFORM" ), GLUtil::getGLValue( gl46core::GL_MAX_VERTEX_UNIFORM_COMPONENTS ) );
343 // How many uniforms can we send to vertex + fragment shaders at the same time
344 Console::printfn( LOCALE_STR( "GL_MAX_FRAG_AND_VERT_UNIFORM" ), GLUtil::getGLValue( gl46core::GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS ) );
345 // How many attributes can we send to a vertex shader
346 deviceInformation._maxVertAttributes = GLUtil::getGLValue( gl46core::GL_MAX_VERTEX_ATTRIBS );
347 Console::printfn( LOCALE_STR( "GL_MAX_VERT_ATTRIB" ), deviceInformation._maxVertAttributes );
348
349 // How many workgroups can we have per compute dispatch
350 for ( U8 i = 0u; i < 3; ++i )
351 {
352 GLUtil::getGLValue( gl46core::GL_MAX_COMPUTE_WORK_GROUP_COUNT, deviceInformation._maxWorgroupCount[i], i );
353 GLUtil::getGLValue( gl46core::GL_MAX_COMPUTE_WORK_GROUP_SIZE, deviceInformation._maxWorgroupSize[i], i );
354 }
355
356 deviceInformation._maxWorgroupInvocations = GLUtil::getGLValue( gl46core::GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS );
357 deviceInformation._maxComputeSharedMemoryBytes = GLUtil::getGLValue( gl46core::GL_MAX_COMPUTE_SHARED_MEMORY_SIZE );
358
359 Console::printfn( LOCALE_STR( "MAX_COMPUTE_WORK_GROUP_INFO" ) ,
360 deviceInformation._maxWorgroupCount[0], deviceInformation._maxWorgroupCount[1], deviceInformation._maxWorgroupCount[2],
361 deviceInformation._maxWorgroupSize[0], deviceInformation._maxWorgroupSize[1], deviceInformation._maxWorgroupSize[2],
362 deviceInformation._maxWorgroupInvocations );
363 Console::printfn( LOCALE_STR( "MAX_COMPUTE_SHARED_MEMORY_SIZE" ), deviceInformation._maxComputeSharedMemoryBytes / 1024 );
364
365 // Maximum number of texture units we can address in shaders
366 Console::printfn( LOCALE_STR( "GL_MAX_TEX_UNITS" ),
367 GLUtil::getGLValue( gl46core::GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ),
368 deviceInformation._maxTextureUnits );
369 // Maximum number of varying components supported as outputs in the vertex shader
370 deviceInformation._maxVertOutputComponents = GLUtil::getGLValue( gl46core::GL_MAX_VERTEX_OUTPUT_COMPONENTS );
371 Console::printfn( LOCALE_STR( "MAX_VERTEX_OUTPUT_COMPONENTS" ), deviceInformation._maxVertOutputComponents );
372
373 // Query shading language version support
374 Console::printfn( LOCALE_STR( "GL_GLSL_SUPPORT" ),
375 reinterpret_cast<const char*>( gl46core::glGetString( gl46core::GL_SHADING_LANGUAGE_VERSION ) ) );
376 // In order: Maximum number of uniform buffer binding points,
377 // maximum size in basic machine units of a uniform block and
378 // minimum required alignment for uniform buffer sizes and offset
379 GLUtil::getGLValue( gl46core::GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, deviceInformation._offsetAlignmentBytesUBO );
380 GLUtil::getGLValue( gl46core::GL_MAX_UNIFORM_BLOCK_SIZE, deviceInformation._maxSizeBytesUBO );
381 const bool UBOSizeOver1Mb = deviceInformation._maxSizeBytesUBO / 1024 > 1024;
382 Console::printfn( LOCALE_STR( "GL_VK_UBO_INFO" ),
383 GLUtil::getGLValue( gl46core::GL_MAX_UNIFORM_BUFFER_BINDINGS ),
384 (deviceInformation._maxSizeBytesUBO / 1024) / (UBOSizeOver1Mb ? 1024 : 1),
385 UBOSizeOver1Mb ? "Mb" : "Kb",
386 deviceInformation._offsetAlignmentBytesUBO );
387
388 // In order: Maximum number of shader storage buffer binding points,
389 // maximum size in basic machine units of a shader storage block,
390 // maximum total number of active shader storage blocks that may
391 // be accessed by all active shaders and
392 // minimum required alignment for shader storage buffer sizes and
393 // offset.
394 GLUtil::getGLValue( gl46core::GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, deviceInformation._offsetAlignmentBytesSSBO );
395 GLUtil::getGLValue( gl46core::GL_MAX_SHADER_STORAGE_BLOCK_SIZE, deviceInformation._maxSizeBytesSSBO );
396 deviceInformation._maxSSBOBufferBindings = GLUtil::getGLValue( gl46core::GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS );
397 Console::printfn( LOCALE_STR( "GL_VK_SSBO_INFO" ),
398 deviceInformation._maxSSBOBufferBindings,
399 deviceInformation._maxSizeBytesSSBO / 1024 / 1024,
400 GLUtil::getGLValue( gl46core::GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS ),
401 deviceInformation._offsetAlignmentBytesSSBO );
402
403 // Maximum number of subroutines and maximum number of subroutine uniform
404 // locations usable in a shader
405 Console::printfn( LOCALE_STR( "GL_SUBROUTINE_INFO" ),
406 GLUtil::getGLValue( gl46core::GL_MAX_SUBROUTINES ),
407 GLUtil::getGLValue( gl46core::GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS ) );
408
409 gl46core::GLint range[2];
410 GLUtil::getGLValue( gl46core::GL_SMOOTH_LINE_WIDTH_RANGE, range );
411 Console::printfn( LOCALE_STR( "GL_LINE_WIDTH_INFO" ), range[0], range[1] );
412
413 const I32 clipDistanceCount = std::max( GLUtil::getGLValue( gl46core::GL_MAX_CLIP_DISTANCES ), 0 );
414 const I32 cullDistanceCount = std::max( GLUtil::getGLValue( gl46core::GL_MAX_CULL_DISTANCES ), 0 );
415
416 deviceInformation._maxClipAndCullDistances = GLUtil::getGLValue( gl46core::GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES );
417 deviceInformation._maxClipDistances = to_U32( clipDistanceCount );
418 deviceInformation._maxCullDistances = to_U32( cullDistanceCount );
419
420 GFXDevice::OverrideDeviceInformation( deviceInformation );
421 // Seamless cubemaps are a nice feature to have enabled (core since 3.2)
422 gl46core::glEnable( gl46core::GL_TEXTURE_CUBE_MAP_SEAMLESS );
423 gl46core::glEnable( gl46core::GL_FRAMEBUFFER_SRGB );
424 // Culling is enabled by default, but RenderStateBlocks can toggle it on a per-draw call basis
425 gl46core::glEnable( gl46core::GL_CULL_FACE );
426
427 // Enable all clip planes, I guess
428 for ( U8 i = 0u; i < Config::MAX_CLIP_DISTANCES; ++i )
429 {
430 gl46core::glEnable( static_cast<gl46core::GLenum>(static_cast<U32>(gl46core::GL_CLIP_DISTANCE0) + i) );
431 }
432
433 for ( U8 i = 0u; i < to_base( GLUtil::GLMemory::GLMemoryType::COUNT ); ++i )
434 {
436 }
437
439
440 // Initialize our query pool
442 {
443 { gl46core::GL_TIME_ELAPSED, 9 },
444 { gl46core::GL_TRANSFORM_FEEDBACK_OVERFLOW, 6 },
445 { gl46core::GL_VERTICES_SUBMITTED, 6 },
446 { gl46core::GL_PRIMITIVES_SUBMITTED, 6 },
447 { gl46core::GL_VERTEX_SHADER_INVOCATIONS, 6 },
448 { gl46core::GL_SAMPLES_PASSED, 6 },
449 { gl46core::GL_ANY_SAMPLES_PASSED, 6 },
450 { gl46core::GL_PRIMITIVES_GENERATED, 6 },
451 { gl46core::GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 6 },
452 { gl46core::GL_ANY_SAMPLES_PASSED_CONSERVATIVE, 6 },
453 { gl46core::GL_TESS_CONTROL_SHADER_PATCHES, 6},
454 { gl46core::GL_TESS_EVALUATION_SHADER_INVOCATIONS, 6}
455 }
456 );
457
458 gl46core::glClearColor( DefaultColours::BLACK.r,
462
463 gl46core::glCreateVertexArrays( 1, &_dummyVAO );
464 DIVIDE_ASSERT( _dummyVAO != GL_NULL_HANDLE, LOCALE_STR( "ERROR_VAO_INIT" ) );
465
466 if constexpr ( Config::ENABLE_GPU_VALIDATION )
467 {
468 gl46core::glObjectLabel( gl46core::GL_VERTEX_ARRAY,
469 _dummyVAO,
470 -1,
471 "GENERIC_VAO");
472 }
473
474 gl46core::glBindVertexArray( _dummyVAO );
476
477 constexpr U16 ringLength = 6u;
478
479 _performanceQueries[to_base( GlobalQueryTypes::VERTICES_SUBMITTED )] = std::make_unique<glHardwareQueryRing>( _context, gl46core::GL_VERTICES_SUBMITTED, ringLength );
480 _performanceQueries[to_base( GlobalQueryTypes::PRIMITIVES_GENERATED )] = std::make_unique<glHardwareQueryRing>( _context, gl46core::GL_PRIMITIVES_GENERATED, ringLength );
481 _performanceQueries[to_base( GlobalQueryTypes::TESSELLATION_PATCHES )] = std::make_unique<glHardwareQueryRing>( _context, gl46core::GL_TESS_CONTROL_SHADER_PATCHES, ringLength );
482 _performanceQueries[to_base( GlobalQueryTypes::TESSELLATION_EVAL_INVOCATIONS )] = std::make_unique<glHardwareQueryRing>( _context, gl46core::GL_TESS_EVALUATION_SHADER_INVOCATIONS, ringLength );
483 _performanceQueries[to_base( GlobalQueryTypes::GPU_TIME )] = std::make_unique<glHardwareQueryRing>( _context, gl46core::GL_TIME_ELAPSED, ringLength );
484
487
488 // That's it. Everything should be ready for draw calls
489 Console::printfn( LOCALE_STR( "START_OGL_API_OK" ) );
490 return ErrorCode::NO_ERR;
491 }
492
495 {
497
498 if ( _dummyVAO != GL_NULL_HANDLE )
499 {
500 gl46core::glBindVertexArray(0u);
501 gl46core::glDeleteVertexArrays(1, &_dummyVAO );
503 }
504
505 // Destroy sampler objects
506 {
507 for ( auto& sampler : s_samplerMap )
508 {
509 glSamplerObject::Destruct( sampler._glHandle );
510 }
511 s_samplerMap.clear();
512 }
513
515 if ( s_hardwareQueryPool != nullptr )
516 {
517 s_hardwareQueryPool->destroy();
518 s_hardwareQueryPool.reset();
519 }
521 {
522 allocator.deallocate();
523 }
524 g_ContextPool.destroy();
525
528 }
529
531 {
532 if ( _runQueries )
533 {
535
536 // End the timing query started in beginFrame() in debug builds
537 for ( U8 i = 0; i < to_base( GlobalQueryTypes::COUNT ); ++i )
538 {
539 _performanceQueries[i]->end();
540 }
541 }
542 }
543
546 {
547 GLUtil::ValidateSDL( SDL_GL_MakeCurrent( window.getRawWindow(), window.userData()._glContext ) );
549 return true;
550 }
551
553 {
554 //GLUtil::ValidateSDL( SDL_GL_MakeCurrent( window.getRawWindow(), nullptr );
555 }
556
558 {
559 }
560
561 void GL_API::prepareFlushWindow( [[maybe_unused]] DisplayWindow& window )
562 {
563 GLUtil::ValidateSDL( SDL_GL_MakeCurrent( window.getRawWindow(), window.userData()._glContext ));
564 }
565
567 {
569
570 const bool mainWindow = window.getGUID() == _context.context().mainWindow().getGUID();
571 if ( mainWindow )
572 {
575 }
576 {
577 PROFILE_SCOPE( "GL_API: Swap Buffers", Profiler::Category::Graphics );
578 SDL_GL_SwapWindow( window.getRawWindow() );
579 }
580
581 if ( mainWindow )
582 {
584 }
585 }
586
588 {
590
591 // Start a duration query in debug builds
592 if ( _runQueries )
593 {
594 for ( U8 i = 0u; i < to_base( GlobalQueryTypes::COUNT ); ++i )
595 {
596 _performanceQueries[i]->begin();
597 }
598 }
599
600 while ( !s_stateTracker._endFrameFences.empty() )
601 {
602 auto& sync = s_stateTracker._endFrameFences.front();
603 const gl46core::GLenum waitRet = gl46core::glClientWaitSync( sync.first, gl46core::SyncObjectMask::GL_NONE_BIT, 0u );
604 DIVIDE_ASSERT( waitRet != gl46core::GL_WAIT_FAILED, "GL_API::beginFrame error: Not sure what to do here. Probably raise an exception or something." );
605 if ( waitRet == gl46core::GL_ALREADY_SIGNALED || waitRet == gl46core::GL_CONDITION_SATISFIED )
606 {
608 DestroyFenceSync( sync.first );
610 }
611 else
612 {
613 break;
614 }
615 }
616
618
619 return true;
620 }
621
623 {
625
627
628 for ( U32 i = 0u; i < GL_API::s_LockFrameLifetime - 1; ++i )
629 {
631 }
632
633 PROFILE_SCOPE( "GL_API: Post-Swap cleanup", Profiler::Category::Graphics );
635 s_glFlushQueued.store( false );
636
637 if ( _runQueries )
638 {
639 PROFILE_SCOPE( "GL_API: Time Query", Profiler::Category::Graphics );
640
641 thread_local std::array<I64, to_base( GlobalQueryTypes::COUNT )> results{};
642 for ( U8 i = 0u; i < to_base( GlobalQueryTypes::COUNT ); ++i )
643 {
644 results[i] = _performanceQueries[i]->getResultNoWait();
645 _performanceQueries[i]->incQueue();
646 }
647
648 _context.getPerformanceMetrics()._gpuTimeInMS = Time::NanosecondsToMilliseconds<F32>( results[to_base( GlobalQueryTypes::GPU_TIME )] );
653 }
654
655 const size_t fenceSize = std::size( s_fenceSyncCounter );
656
657 for ( size_t i = 0u; i < std::size( _context.getPerformanceMetrics()._syncObjectsInFlight ); ++i )
658 {
660 }
661
663 _runQueries = _context.queryPerformanceStats();
664
667
668 if ( gl46core::glGetGraphicsResetStatus() != gl46core::GL_NO_ERROR )
669 {
670 DIVIDE_UNEXPECTED_CALL_MSG( "OpenGL Reset Status raised!" );
671 }
672
674 return true;
675 }
676
677 void GL_API::idle( [[maybe_unused]] const bool fast )
678 {
680 }
681
683 {
686
687 if ( cmd._sourceBuffer._id == 0u )
688 {
689 DIVIDE_ASSERT(cmd._cmd.indexCount == 0u);
690
691 if (cmd._cmd.vertexCount == 0u )
692 {
693 GenericDrawCommand drawCmd = cmd;
694 switch ( GL_API::s_stateTracker._activeTopology )
695 {
696 case PrimitiveTopology::POINTS: drawCmd._cmd.vertexCount = 1u; break;
700 case PrimitiveTopology::LINES_ADJANCENCY: drawCmd._cmd.vertexCount = 2u; break;
706 case PrimitiveTopology::PATCH: drawCmd._cmd.vertexCount = 4u; break;
707 default : return false;
708 }
709 GLUtil::SubmitRenderCommand( drawCmd, gl46core::GL_NONE);
710 }
711 else
712 {
713 GLUtil::SubmitRenderCommand(cmd, gl46core::GL_NONE);
714 }
715 }
716 else [[likely]]
717 {
718 // Because this can only happen on the main thread, try and avoid costly lookups for hot-loop drawing
720 thread_local VertexDataInterface* s_lastBuffer = nullptr;
721
722 if ( s_lastID != cmd._sourceBuffer )
723 {
724 s_lastID = cmd._sourceBuffer;
725 s_lastBuffer = VertexDataInterface::s_VDIPool.find( s_lastID );
726 }
727
728 DIVIDE_ASSERT( s_lastBuffer != nullptr );
729 s_lastBuffer->draw( cmd, nullptr );
730 }
731
732 return true;
733 }
734
736 {
738
739 thread_local std::array<TexBindEntry, GLStateTracker::MAX_BOUND_TEXTURE_UNITS> s_textureCache;
740 thread_local std::array<gl46core::GLuint, GLStateTracker::MAX_BOUND_TEXTURE_UNITS> s_textureHandles;
741 thread_local std::array<gl46core::GLuint, GLStateTracker::MAX_BOUND_TEXTURE_UNITS> s_textureSamplers;
742
743 if ( s_TexBindQueue.empty() )
744 {
745 return;
746 }
747
748 if ( s_TexBindQueue.size() == 1u )
749 {
750 const TexBindEntry& texEntry = s_TexBindQueue.front();
752 {
754 }
755 }
756 else
757 {
758 constexpr bool s_useBatchBindTextures = true;
759
760 // Sort by slot number
761 eastl::sort( eastl::begin( s_TexBindQueue ),
762 eastl::end( s_TexBindQueue ),
763 []( const TexBindEntry& lhs, const TexBindEntry& rhs )
764 {
765 return lhs._slot < rhs._slot;
766 } );
767
768 if ( s_useBatchBindTextures )
769 {
770 // Reset our cache
771 for ( auto& it : s_textureCache )
772 {
773 it._handle = GL_NULL_HANDLE;
774 }
775
776 // Grab min and max slot and fill in the cache with all of the available data
777 U8 minSlot = U8_MAX, maxSlot = 0u;
778 for ( const TexBindEntry& texEntry : s_TexBindQueue )
779 {
780 s_textureCache[texEntry._slot] = texEntry;
781 minSlot = std::min( texEntry._slot, minSlot );
782 maxSlot = std::max( texEntry._slot, maxSlot );
783 }
784
785 U8 idx = 0u, bindOffset = minSlot, texCount = maxSlot - minSlot;
786 for ( U8 i = 0u; i < texCount + 1; ++i )
787 {
788 const U8 slot = i + minSlot;
789 const TexBindEntry& it = s_textureCache[slot];
790
791 if ( it._handle != GL_NULL_HANDLE )
792 {
793 s_textureHandles[idx] = it._handle;
794 s_textureSamplers[idx] = it._sampler;
795
796 if ( idx++ == 0u )
797 {
798 // Start a new range so remember the starting offset
799 bindOffset = slot;
800 }
801 }
802 else if ( idx > 0u )
803 {
804 // We found a hole in the texture range. Flush the current range and start a new one
805 if ( s_stateTracker.bindTextures( bindOffset, idx, s_textureHandles.data(), s_textureSamplers.data() ) == GLStateTracker::BindResult::FAILED )
806 {
808 }
809 idx = 0u;
810 }
811 }
812 // Handle the final range (if any)
813 if ( idx > 0u )
814 {
815 if ( s_stateTracker.bindTextures( bindOffset, idx, s_textureHandles.data(), s_textureSamplers.data() ) == GLStateTracker::BindResult::FAILED )
816 {
818 }
819 }
820 }
821 else
822 {
823 for ( const TexBindEntry& texEntry : s_TexBindQueue )
824 {
825 if ( s_stateTracker.bindTexture( texEntry._slot, texEntry._handle, texEntry._sampler ) == GLStateTracker::BindResult::FAILED )
826 {
828 }
829 }
830 }
831 }
832
834 }
835
836 gl46core::GLuint GL_API::getGLTextureView( const ImageView srcView, const size_t srcViewHash, const U8 lifetimeInFrames ) const
837 {
839
840 auto [handle, cacheHit] = s_textureViewCache.allocate( srcViewHash );
841
842 if ( !cacheHit )
843 {
844 const gl46core::GLuint srcHandle = static_cast<const glTexture*>(srcView._srcTexture)->textureHandle();
845
846 if ( srcHandle == GL_NULL_HANDLE )
847 {
848 return srcHandle;
849 }
850
851 const gl46core::GLenum glInternalFormat = GLUtil::InternalFormatAndDataType( srcView._descriptor._baseFormat,
852 srcView._descriptor._dataType,
853 srcView._descriptor._packing )._format;
854
855 const bool isCube = IsCubeTexture( TargetType( srcView ) );
856
857 PROFILE_SCOPE( "GL: cache miss - Image", Profiler::Category::Graphics );
858 gl46core::glTextureView( handle,
860 srcHandle,
861 glInternalFormat,
862 static_cast<gl46core::GLuint>(srcView._subRange._mipLevels._offset),
863 static_cast<gl46core::GLuint>(srcView._subRange._mipLevels._count),
864 srcView._subRange._layerRange._offset * (isCube ? 6 : 1),
865 srcView._subRange._layerRange._count * (isCube ? 6 : 1));
866 }
867
868 s_textureViewCache.deallocate( handle, lifetimeInFrames );
869
870 return handle;
871 }
872
874 {
875 if ( _uniformsNeedLock )
876 {
877 _uniformsNeedLock = false;
880 }
881 }
882
883 void GL_API::preFlushCommandBuffer( [[maybe_unused]] const Handle<GFX::CommandBuffer> commandBuffer )
884 {
887 }
888
890 {
892
893 if ( GFXDevice::IsSubmitCommand( cmd->type() ) )
894 {
896 }
897
899 {
901 }
902
903 switch ( cmd->type() )
904 {
906 {
907 PROFILE_SCOPE( "BEGIN_RENDER_PASS", Profiler::Category::Graphics );
908
910
912
913 if ( crtCmd->_target == SCREEN_TARGET_ID )
914 {
915 PROFILE_SCOPE( "Begin Screen Target", Profiler::Category::Graphics );
916
918 {
920 }
921
924
926 {
927 PROFILE_SCOPE( "Clear Screen Target", Profiler::Category::Graphics );
928
929 gl46core::ClearBufferMask mask = gl46core::ClearBufferMask::GL_COLOR_BUFFER_BIT;
930
932 if ( crtCmd->_clearDescriptor[RT_DEPTH_ATTACHMENT_IDX]._enabled )
933 {
935 mask |= gl46core::ClearBufferMask::GL_DEPTH_BUFFER_BIT;
936 }
937 gl46core::glClear( mask );
938 }
939 PushDebugMessage( crtCmd->_name.c_str(), SCREEN_TARGET_ID );
940 }
941 else
942 {
943 PROFILE_SCOPE( "Begin Render Target", Profiler::Category::Graphics );
944
946
948 if ( bindResult == GLStateTracker::BindResult::FAILED )
949 {
951 }
952
956 PushDebugMessage(crtCmd->_name.c_str(), crtCmd->_target);
957 AddDebugMessage(rt->debugMessage().c_str(), crtCmd->_target );
958 }
959 }break;
961 {
962 PROFILE_SCOPE( "END_RENDER_PASS", Profiler::Category::Graphics );
963
965
966 if ( GL_API::s_stateTracker._activeRenderTarget == nullptr )
967 {
968 assert( GL_API::s_stateTracker._activeRenderTargetID == SCREEN_TARGET_ID );
969 }
970 else
971 {
975 }
978 }break;
980 {
982
985 glFramebuffer* destination = static_cast<glFramebuffer*>(_context.renderTargetPool().getRenderTarget( crtCmd->_destination ));
986 destination->blitFrom( source, crtCmd->_params );
987 } break;
989 {
990 PROFILE_SCOPE( "BEGIN_GPU_QUERY", Profiler::Category::Graphics );
991
993 if ( crtCmd->_queryMask != 0u ) [[likely]]
994 {
995 auto& queryContext = _queryContext.emplace();
996 for ( U8 i = 0u, j = 0u; i < to_base( QueryType::COUNT ); ++i )
997 {
998 const U32 typeFlag = toBit( i + 1u );
999 if ( crtCmd->_queryMask & typeFlag )
1000 {
1001 queryContext[i]._query = &GL_API::GetHardwareQueryPool()->allocate( GLUtil::glQueryTypeTable[i] );
1002 queryContext[i]._type = static_cast<QueryType>(typeFlag);
1003 queryContext[i]._index = j++;
1004 }
1005 }
1006
1007 for ( auto& queryEntry : queryContext )
1008 {
1009 if ( queryEntry._query != nullptr )
1010 {
1011 queryEntry._query->begin();
1012 }
1013 }
1014 }
1015 }break;
1017 {
1018 PROFILE_SCOPE( "END_GPU_QUERY", Profiler::Category::Graphics );
1019
1020 if ( !_queryContext.empty() ) [[likely]]
1021 {
1022 const GFX::EndGPUQueryCommand* crtCmd = cmd->As<GFX::EndGPUQueryCommand>();
1023
1024 DIVIDE_ASSERT( crtCmd->_resultContainer != nullptr );
1025
1026 for ( glHardwareQueryEntry& queryEntry : _queryContext.top() )
1027 {
1028 if ( queryEntry._query != nullptr )
1029 {
1030 queryEntry._query->end();
1031
1032 const I64 qResult = crtCmd->_waitForResults ? queryEntry._query->getResult() : queryEntry._query->getResultNoWait();
1033 (*crtCmd->_resultContainer)[queryEntry._index] = { queryEntry._type, qResult };
1034 }
1035 }
1036
1037 _queryContext.pop();
1038 }
1039 }break;
1041 {
1043
1044 const GFX::CopyTextureCommand* crtCmd = cmd->As<GFX::CopyTextureCommand>();
1045 glTexture::Copy( static_cast<glTexture*>(Get(crtCmd->_source)),
1046 crtCmd->_sourceMSAASamples,
1047 static_cast<glTexture*>(Get(crtCmd->_destination)),
1049 crtCmd->_params );
1050 }break;
1052 {
1053 PROFILE_SCOPE( "CLEAR_TEXTURE", Profiler::Category::Graphics );
1054
1056 if ( crtCmd->_texture != INVALID_HANDLE<Texture> )
1057 {
1058 static_cast<glTexture*>(Get(crtCmd->_texture))->clearData( crtCmd->_clearColour, crtCmd->_layerRange, crtCmd->_mipLevel );
1059 }
1060 }break;
1062 {
1064
1065 const GFX::ReadTextureCommand* crtCmd = cmd->As<GFX::ReadTextureCommand>();
1066 glTexture* tex = static_cast<glTexture*>(Get(crtCmd->_texture));
1067 const ImageReadbackData readData = tex->readData( crtCmd->_mipLevel, crtCmd->_pixelPackAlignment );
1068 crtCmd->_callback( readData );
1069 }break;
1071 {
1072 PROFILE_SCOPE( "BIND_PIPELINE", Profiler::Category::Graphics );
1073
1074 const Pipeline* pipeline = cmd->As<GFX::BindPipelineCommand>()->_pipeline;
1075 assert( pipeline != nullptr );
1076 if ( BindPipeline(_context, *pipeline ) == ShaderResult::Failed )
1077 {
1078 const auto handle = pipeline->descriptor()._shaderProgramHandle;
1079 Console::errorfn( LOCALE_STR( "ERROR_GLSL_INVALID_BIND" ), handle._index, handle._generation );
1080 }
1081 } break;
1083 {
1084 PROFILE_SCOPE( "SEND_PUSH_CONSTANTS", Profiler::Category::Graphics );
1085
1086 const auto dumpLogs = [this]()
1087 {
1088 Console::d_errorfn( LOCALE_STR( "ERROR_GLSL_INVALID_PUSH_CONSTANTS" ) );
1090 {
1091 // Shader failed to compile probably. Dump all shader caches for inspection.
1093 }
1094 };
1095
1096 const Pipeline* activePipeline = s_stateTracker._activePipeline;
1097 if ( activePipeline == nullptr )
1098 {
1099 dumpLogs();
1100 break;
1101 }
1102
1103 if ( s_stateTracker._activeShaderProgram == nullptr )
1104 {
1105 // Should we skip the upload?
1106 dumpLogs();
1107 break;
1108 }
1109
1111 UniformData* uniforms = pushConstantsCmd->_uniformData;
1112 if ( uniforms != nullptr )
1113 {
1115 {
1118 }
1119 }
1121
1122 } break;
1124 {
1125 PROFILE_SCOPE( "BEGIN_DEBUG_SCOPE", Profiler::Category::Graphics );
1126
1127 const auto& crtCmd = cmd->As<GFX::BeginDebugScopeCommand>();
1128 PushDebugMessage( crtCmd->_scopeName.c_str(), crtCmd->_scopeId );
1129 } break;
1131 {
1132 PROFILE_SCOPE( "END_DEBUG_SCOPE", Profiler::Category::Graphics );
1133
1135 } break;
1137 {
1138 PROFILE_SCOPE( "ADD_DEBUG_MESSAGE", Profiler::Category::Graphics );
1139
1140 const auto& crtCmd = cmd->As<GFX::AddDebugMessageCommand>();
1141 AddDebugMessage( crtCmd->_msg.c_str(), crtCmd->_msgId );
1142 }break;
1144 {
1145 PROFILE_SCOPE( "COMPUTE_MIPMAPS", Profiler::Category::Graphics );
1146
1149
1150 ResourcePtr<Texture> tex = Get(crtCmd->_texture);
1151 const U16 texLayers = IsCubeTexture( tex->descriptor()._texType ) ? tex->depth() * 6u : tex->depth();
1152 const U16 layerCount = crtCmd->_layerRange._count == U16_MAX ? texLayers : crtCmd->_layerRange._count;
1153
1154 if ( crtCmd->_layerRange._offset == 0 && layerCount >= texLayers )
1155 {
1156 PROFILE_SCOPE( "GL: In-place computation - Full", Profiler::Category::Graphics );
1157 gl46core::glGenerateTextureMipmap( static_cast<glTexture*>(tex)->textureHandle() );
1158 }
1159 else
1160 {
1161 PROFILE_SCOPE( "GL: View-based computation", Profiler::Category::Graphics );
1162 assert( crtCmd->_mipRange._count != 0u );
1163
1164 ImageView view = tex->getView();
1165 view._subRange._layerRange = crtCmd->_layerRange;
1166 view._subRange._mipLevels = crtCmd->_mipRange;
1167
1168 const TextureType targetType = TargetType( view );
1169 DIVIDE_ASSERT( targetType != TextureType::COUNT );
1170
1171 if ( IsArrayTexture( targetType ) && view._subRange._layerRange._count == 1 )
1172 {
1173 if ( targetType == TextureType::TEXTURE_1D_ARRAY )
1174 {
1176 }
1177 else if ( targetType == TextureType::TEXTURE_2D_ARRAY )
1178 {
1180 }
1181 else if ( targetType ==TextureType::TEXTURE_CUBE_ARRAY )
1182 {
1184 }
1185 else
1186 {
1188 }
1189 }
1190
1193 {
1194 PROFILE_SCOPE( "GL: In-place computation - Image", Profiler::Category::Graphics );
1195 gl46core::glGenerateTextureMipmap( getGLTextureView( view, GetHash( view ), 6u ) );
1196 }
1197 }
1198 }break;
1200 {
1201 PROFILE_SCOPE( "DRAW_COMMANDS", Profiler::Category::Graphics );
1202
1203 if ( s_stateTracker._activePipeline != nullptr )
1204 {
1205 U32 drawCount = 0u;
1206 const auto& drawCommands = cmd->As<GFX::DrawCommand>()->_drawCommands;
1207
1208 for ( const GenericDrawCommand& currentDrawCommand : drawCommands )
1209 {
1210 if ( isEnabledOption( currentDrawCommand, CmdRenderOptions::RENDER_GEOMETRY ))
1211 {
1212 Draw( currentDrawCommand );
1213 ++drawCount;
1214 }
1215
1216 if ( isEnabledOption( currentDrawCommand, CmdRenderOptions::RENDER_WIREFRAME ) )
1217 {
1220 Draw(currentDrawCommand);
1222 ++drawCount;
1223 }
1224 }
1225
1226 _context.registerDrawCalls( drawCount );
1227 }
1228 }break;
1230 {
1231 PROFILE_SCOPE( "DISPATCH_COMPUTE", Profiler::Category::Graphics );
1232
1233 if ( s_stateTracker._activePipeline != nullptr )
1234 {
1236
1238 gl46core::glDispatchCompute( crtCmd->_computeGroupSize.x, crtCmd->_computeGroupSize.y, crtCmd->_computeGroupSize.z );
1239 }
1240 }break;
1242 {
1243 PROFILE_SCOPE( "MEMORY_BARRIER", Profiler::Category::Graphics );
1244
1246
1247 gl46core::MemoryBarrierMask mask = gl46core::GL_NONE_BIT;
1248
1249 SyncObjectHandle handle{};
1250 for ( const BufferLock& lock : crtCmd->_bufferLocks )
1251 {
1252 if ( lock._buffer == nullptr || lock._range._length == 0u)
1253 {
1254 continue;
1255 }
1256
1257 glBufferImpl* buffer = static_cast<glBufferImpl*>(lock._buffer);
1258 const BufferFlags flags = buffer->params()._bufferParams._flags;
1259
1260 switch ( lock._type )
1261 {
1263 {
1264 if ( handle._id == SyncObjectHandle::INVALID_SYNC_ID )
1265 {
1267 }
1268
1269 if ( !buffer->lockRange( lock._range, handle ) )
1270 {
1272 }
1273 } break;
1275 {
1278 {
1279 mask |= gl46core::GL_BUFFER_UPDATE_BARRIER_BIT;
1280 }
1281 else
1282 {
1283 mask |= gl46core::GL_BUFFER_UPDATE_BARRIER_BIT | gl46core::GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT;
1284 }
1285 } break;
1288 {
1289 switch ( flags._usageType )
1290 {
1292 {
1293 mask |= gl46core::GL_UNIFORM_BARRIER_BIT;
1294 } break;
1296 {
1297 mask |= gl46core::GL_SHADER_STORAGE_BARRIER_BIT;
1298 } break;
1300 {
1301 mask |= gl46core::GL_COMMAND_BARRIER_BIT | gl46core::GL_SHADER_STORAGE_BARRIER_BIT;
1302 } break;
1304 {
1305 mask |= gl46core::GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
1306 } break;
1308 {
1309 mask |= gl46core::GL_ELEMENT_ARRAY_BARRIER_BIT;
1310 } break;
1312 {
1313 mask |= gl46core::GL_BUFFER_UPDATE_BARRIER_BIT | gl46core::GL_SHADER_STORAGE_BARRIER_BIT;
1314 } break;
1315 default: DIVIDE_UNEXPECTED_CALL(); break;
1316 }
1317 } break;
1319 {
1320 mask |= gl46core::GL_SHADER_STORAGE_BARRIER_BIT;
1321 } break;
1325 {
1326 NOP();
1327 }break;
1328 default: DIVIDE_UNEXPECTED_CALL(); break;
1329 }
1330 }
1331
1332 bool textureBarrierRequired = false;
1333 for ( const auto& it : crtCmd->_textureLayoutChanges )
1334 {
1335 if ( it._sourceLayout == it._targetLayout )
1336 {
1337 continue;
1338 }
1339
1340 switch ( it._targetLayout )
1341 {
1343 {
1344 mask |= gl46core::GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
1345 } break;
1348 {
1349 if ( it._sourceLayout != ImageUsage::SHADER_WRITE &&
1350 it._sourceLayout != ImageUsage::SHADER_READ_WRITE )
1351 {
1352 textureBarrierRequired = true;
1353 }
1354 else
1355 {
1356 mask |= gl46core::GL_TEXTURE_FETCH_BARRIER_BIT | gl46core::GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
1357 }
1358 } break;
1360 {
1361 mask |= gl46core::GL_TEXTURE_UPDATE_BARRIER_BIT;
1362 } break;
1366 {
1367 mask |= gl46core::GL_FRAMEBUFFER_BARRIER_BIT;
1368 } break;
1369
1370 default: DIVIDE_UNEXPECTED_CALL(); break;
1371 }
1372 }
1373
1374 if ( mask != gl46core::MemoryBarrierMask::GL_NONE_BIT )
1375 {
1376 gl46core::glMemoryBarrier( mask );
1377 }
1378 if ( textureBarrierRequired )
1379 {
1380 gl46core::glTextureBarrier();
1381 }
1382 } break;
1383
1384
1396
1398 default: DIVIDE_UNEXPECTED_CALL(); break;
1399 }
1400 }
1401
1402 void GL_API::postFlushCommandBuffer( [[maybe_unused]] const Handle<GFX::CommandBuffer> commandBuffer )
1403 {
1404 PROFILE_SCOPE_AUTO( Profiler::Category::Graphics );
1405
1406 flushPushConstantsLocks();
1407
1408 bool expected = true;
1409 if ( s_glFlushQueued.compare_exchange_strong( expected, false ) )
1410 {
1411 PROFILE_SCOPE( "GL_FLUSH", Profiler::Category::Graphics );
1412 gl46core::glFlush();
1413 }
1414 GetStateTracker()._activeRenderTargetID = INVALID_RENDER_TARGET_ID;
1415 GetStateTracker()._activeRenderTargetDimensions = _context.context().mainWindow().getDrawableSize();
1416 }
1417
1418 void GL_API::initDescriptorSets()
1419 {
1420 NOP();
1421 }
1422
1423 void GL_API::onThreadCreated( [[maybe_unused]] const std::thread::id& threadID, const bool isMainRenderThread )
1424 {
1425 if ( isMainRenderThread )
1426 {
1427 // We'll try and use the same context from the main thread
1428 return;
1429 }
1430
1431 // Double check so that we don't run into a race condition!
1432 LockGuard<Mutex> lock( GLUtil::s_glSecondaryContextMutex );
1433 assert( SDL_GL_GetCurrentContext() == NULL );
1434
1435 // This also makes the context current
1436 assert( GLUtil::s_glSecondaryContext == nullptr && "GL_API::syncToThread: double init context for current thread!" );
1437 [[maybe_unused]] const bool ctxFound = g_ContextPool.getAvailableContext( GLUtil::s_glSecondaryContext );
1438 assert( ctxFound && "GL_API::syncToThread: context not found for current thread!" );
1439
1440 GLUtil::ValidateSDL( SDL_GL_MakeCurrent( GLUtil::s_glMainRenderWindow->getRawWindow(), GLUtil::s_glSecondaryContext ) );
1441 glbinding::Binding::initialize( []( const char* proc ) noexcept
1442 {
1443 return (glbinding::ProcAddress)SDL_GL_GetProcAddress( proc );
1444 } );
1445
1446 // Enable OpenGL debug callbacks for this context as well
1447 if constexpr( Config::ENABLE_GPU_VALIDATION )
1448 {
1449 gl46core::glEnable( gl46core::GL_DEBUG_OUTPUT );
1450 gl46core::glEnable( gl46core::GL_DEBUG_OUTPUT_SYNCHRONOUS );
1451 gl46core::glDebugMessageControl( gl46core::GL_DONT_CARE, gl46core::GL_DEBUG_TYPE_MARKER, gl46core::GL_DONT_CARE, 0, NULL, gl46core::GL_FALSE );
1452 gl46core::glDebugMessageControl( gl46core::GL_DONT_CARE, gl46core::GL_DEBUG_TYPE_PUSH_GROUP, gl46core::GL_DONT_CARE, 0, NULL, gl46core::GL_FALSE );
1453 gl46core::glDebugMessageControl( gl46core::GL_DONT_CARE, gl46core::GL_DEBUG_TYPE_POP_GROUP, gl46core::GL_DONT_CARE, 0, NULL, gl46core::GL_FALSE );
1454 // Debug callback in a separate thread requires a flag to distinguish it from the main thread's callbacks
1455 gl46core::glDebugMessageCallback( (gl46core::GLDEBUGPROC)GLUtil::DebugCallback, GLUtil::s_glSecondaryContext );
1456 }
1457
1458 gl::glMaxShaderCompilerThreadsARB( 0xFFFFFFFF );
1459 }
1460
1462 void GL_API::clearStates(GLStateTracker& stateTracker) const
1463 {
1464 PROFILE_SCOPE_AUTO( Profiler::Category::Graphics );
1465
1466 if ( !stateTracker.unbindTextures() )
1467 {
1469 }
1470
1471 if ( stateTracker.setActiveBuffer( gl46core::GL_ELEMENT_ARRAY_BUFFER, 0 ) == GLStateTracker::BindResult::FAILED )
1472 {
1474 }
1475 if ( stateTracker.setActiveFB( RenderTarget::Usage::RT_READ_WRITE, 0 ) == GLStateTracker::BindResult::FAILED )
1476 {
1478 }
1479
1480 const U8 blendCount = to_U8( stateTracker._blendEnabled.size() );
1481 for ( U8 i = 0u; i < blendCount; ++i )
1482 {
1483 stateTracker.setBlending( i, {} );
1484 }
1485 stateTracker.setBlendColour( { 0u, 0u, 0u, 0u } );
1486
1487 const vec2<U16> drawableSize = _context.context().mainWindow().getDrawableSize();
1488 stateTracker.setScissor( { 0, 0, drawableSize.width, drawableSize.height } );
1489
1490 stateTracker._activePipeline = nullptr;
1491 stateTracker._activeRenderTarget = nullptr;
1493 stateTracker._activeRenderTargetDimensions = drawableSize;
1494 if ( stateTracker.setActiveProgram( 0u ) == GLStateTracker::BindResult::FAILED )
1495 {
1497 }
1498 if ( stateTracker.setActiveShaderPipeline( 0u ) == GLStateTracker::BindResult::FAILED )
1499 {
1501 }
1502 if ( stateTracker.setStateBlock({}) == GLStateTracker::BindResult::FAILED )
1503 {
1505 }
1506 }
1507
1508 bool GL_API::bindShaderResources( const DescriptorSetEntries& descriptorSetEntries )
1509 {
1510 PROFILE_SCOPE_AUTO(Profiler::Category::Graphics );
1511
1512 for ( const DescriptorSetEntry& entry : descriptorSetEntries )
1513 {
1514 if ( !entry._isDirty )
1515 {
1516 // We don't need to keep track of descriptor set to layout compatibility in OpenGL
1517 continue;
1518 }
1519
1520 for ( U8 i = 0u; i < entry._set->_bindingCount; ++i )
1521 {
1522 const DescriptorSetBinding& srcBinding = entry._set->_bindings[i];
1523 const gl46core::GLubyte glBindingSlot = ShaderProgram::GetGLBindingForDescriptorSlot( entry._usage, srcBinding._slot );
1524
1525 switch ( srcBinding._data._type )
1526 {
1527 case DescriptorSetBindingType::UNIFORM_BUFFER:
1528 case DescriptorSetBindingType::SHADER_STORAGE_BUFFER:
1529 {
1530 const ShaderBufferEntry& bufferEntry = srcBinding._data._buffer;
1531 if ( bufferEntry._buffer == nullptr || bufferEntry._range._length == 0u ) [[unlikely]]
1532 {
1533 continue;
1534 }
1535
1536 glShaderBuffer* glBuffer = static_cast<glShaderBuffer*>(bufferEntry._buffer);
1537
1538 if ( !glBuffer->bindByteRange(
1539 glBindingSlot,
1540 {
1541 bufferEntry._range._startOffset * glBuffer->getPrimitiveSize(),
1542 bufferEntry._range._length * glBuffer->getPrimitiveSize(),
1543 },
1544 bufferEntry._queueReadIndex
1545 ) )
1546 {
1547 NOP();
1548 }
1549 } break;
1550 case DescriptorSetBindingType::COMBINED_IMAGE_SAMPLER:
1551 {
1552 if ( srcBinding._slot == INVALID_TEXTURE_BINDING ) [[unlikely]]
1553 {
1554 continue;
1555 }
1556
1557 const DescriptorCombinedImageSampler& imageSampler = srcBinding._data._sampledImage;
1558 if ( !makeTextureViewResident( glBindingSlot, imageSampler._image, imageSampler._imageHash, imageSampler._sampler, imageSampler._samplerHash ) )
1559 {
1561 }
1562 } break;
1563 case DescriptorSetBindingType::IMAGE:
1564 {
1565 const DescriptorImageView& imageView = srcBinding._data._imageView;
1566 DIVIDE_ASSERT( TargetType( imageView._image ) != TextureType::COUNT );
1568
1569 gl46core::GLenum access = gl46core::GL_NONE;
1570 switch ( imageView._usage )
1571 {
1572 case ImageUsage::SHADER_READ: access = gl46core::GL_READ_ONLY; break;
1573 case ImageUsage::SHADER_WRITE: access = gl46core::GL_WRITE_ONLY; break;
1574 case ImageUsage::SHADER_READ_WRITE: access = gl46core::GL_READ_WRITE; break;
1575
1576 case ImageUsage::UNDEFINED:
1577 case ImageUsage::CPU_READ:
1578 case ImageUsage::RT_COLOUR_ATTACHMENT:
1579 case ImageUsage::RT_DEPTH_ATTACHMENT:
1580 case ImageUsage::RT_DEPTH_STENCIL_ATTACHMENT:
1581 case ImageUsage::COUNT: DIVIDE_UNEXPECTED_CALL(); break;
1582 }
1583
1584 DIVIDE_ASSERT( imageView._image._subRange._mipLevels._count == 1u );
1585
1586 const gl46core::GLenum glInternalFormat = GLUtil::InternalFormatAndDataType( imageView._image._descriptor._baseFormat,
1587 imageView._image._descriptor._dataType,
1588 imageView._image._descriptor._packing )._format;
1589
1590 const gl46core::GLuint handle = static_cast<const glTexture*>(imageView._image._srcTexture)->textureHandle();
1591 if ( handle != GL_NULL_HANDLE &&
1592 GL_API::s_stateTracker.bindTextureImage( glBindingSlot,
1593 handle,
1595 imageView._image._subRange._layerRange._count > 1u,
1597 access,
1598 glInternalFormat ) == GLStateTracker::BindResult::FAILED )
1599 {
1601 }
1602 } break;
1603 case DescriptorSetBindingType::COUNT:
1604 {
1606 } break;
1607 };
1608 }
1609 }
1610
1611 return true;
1612 }
1613
1614 bool GL_API::makeTextureViewResident( const gl46core::GLubyte bindingSlot, const ImageView& imageView, const size_t imageViewHash, const SamplerDescriptor sampler, const size_t samplerHash ) const
1615 {
1616 PROFILE_SCOPE_AUTO( Profiler::Category::Graphics );
1617
1618 if ( imageView._srcTexture == nullptr )
1619 {
1620 //unbind request;
1621 s_TexBindQueue.emplace_back(TexBindEntry
1622 {
1623 ._handle = 0u,
1624 ._sampler = 0u,
1625 ._slot = bindingSlot
1626 });
1627
1628 return true;
1629 }
1630
1632 entry._slot = bindingSlot;
1633
1634 if ( imageView._srcTexture != nullptr && imageView != imageView._srcTexture->getView())
1635 {
1636 entry._handle = getGLTextureView(imageView, imageViewHash, 3u);
1637 }
1638 else
1639 {
1640 entry._handle = static_cast<const glTexture*>(imageView._srcTexture)->textureHandle();
1641 }
1642
1643 if ( entry._handle == GL_NULL_HANDLE )
1644 {
1645 return false;
1646 }
1647
1648 size_t tempSampler = samplerHash;
1649 entry._sampler = GetSamplerHandle( sampler, tempSampler );
1650
1651 for ( TexBindEntry& it : s_TexBindQueue )
1652 {
1653 if ( it._slot == bindingSlot )
1654 {
1655 it = entry;
1656 return true;
1657 }
1658 }
1659
1660 s_TexBindQueue.push_back( MOV( entry ) );
1661 return true;
1662 }
1663
1664 bool GL_API::setViewportInternal( const Rect<I32>& viewport )
1665 {
1666 return s_stateTracker.setViewport( viewport );
1667 }
1668
1669 bool GL_API::setScissorInternal( const Rect<I32>& scissor )
1670 {
1671 return s_stateTracker.setScissor( scissor );
1672 }
1673
1674 ShaderResult GL_API::BindPipeline(GFXDevice& context, const Pipeline& pipeline )
1675 {
1676 PROFILE_SCOPE_AUTO( Profiler::Category::Graphics );
1677
1678 if ( s_stateTracker._activePipeline && *s_stateTracker._activePipeline == pipeline )
1679 {
1680 return ShaderResult::OK;
1681 }
1682
1683 s_stateTracker._activePipeline = &pipeline;
1684
1685 s_stateTracker.setAlphaToCoverage(pipeline.descriptor()._alphaToCoverage);
1686
1687 const PipelineDescriptor& pipelineDescriptor = pipeline.descriptor();
1688 {
1689 PROFILE_SCOPE( "Set Raster State", Profiler::Category::Graphics );
1690 if ( s_stateTracker.setStateBlock( pipelineDescriptor._stateBlock ) == GLStateTracker::BindResult::FAILED )
1691 {
1693 }
1694 }
1695 {
1696 PROFILE_SCOPE( "Set Blending", Profiler::Category::Graphics );
1697 U16 i = 0u;
1698 s_stateTracker.setBlendColour( pipelineDescriptor._blendStates._blendColour );
1699 for ( const BlendingSettings& blendState : pipelineDescriptor._blendStates._settings )
1700 {
1701 s_stateTracker.setBlending( i++, blendState );
1702 }
1703 }
1704
1705 ShaderResult ret = ShaderResult::Failed;
1706 ResourcePtr<ShaderProgram> program = Get( pipelineDescriptor._shaderProgramHandle );
1707 glShaderProgram* glProgram = static_cast<glShaderProgram*>(program);
1708 if ( glProgram != nullptr )
1709 {
1710 {
1711 PROFILE_SCOPE( "Set Vertex Format", Profiler::Category::Graphics );
1712 s_stateTracker.setPrimitiveTopology( pipelineDescriptor._primitiveTopology );
1713 s_stateTracker.setVertexFormat( pipelineDescriptor._vertexFormat, pipeline.vertexFormatHash() );
1714 }
1715 {
1716 PROFILE_SCOPE( "Set Shader Program", Profiler::Category::Graphics );
1717 // We need a valid shader as no fixed function pipeline is available
1718 // Try to bind the shader program. If it failed to load, or isn't loaded yet, cancel the draw request for this frame
1719 ret = Attorney::GLAPIShaderProgram::bind( *glProgram );
1720 }
1721
1722 if ( ret != ShaderResult::OK )
1723 {
1724 if ( s_stateTracker.setActiveProgram( 0u ) == GLStateTracker::BindResult::FAILED )
1725 {
1727 }
1728 if ( s_stateTracker.setActiveShaderPipeline( 0u ) == GLStateTracker::BindResult::FAILED )
1729 {
1731 }
1732 s_stateTracker._activePipeline = nullptr;
1733 }
1734 else
1735 {
1736 s_stateTracker._activeShaderProgram = glProgram;
1737 }
1738 context.descriptorSet( DescriptorSetUsage::PER_DRAW ).dirty(true);
1739 }
1740 else
1741 {
1742 const auto handle = pipelineDescriptor._shaderProgramHandle;
1743 Console::errorfn( LOCALE_STR( "ERROR_GLSL_INVALID_HANDLE" ), handle._index, handle._generation );
1744 }
1745
1746 return ret;
1747 }
1748
1749 GLStateTracker& GL_API::GetStateTracker() noexcept
1750 {
1751 return s_stateTracker;
1752 }
1753
1754 GLUtil::GLMemory::GLMemoryType GL_API::GetMemoryTypeForUsage( const gl46core::GLenum usage ) noexcept
1755 {
1756 assert( usage != gl46core::GL_NONE );
1757 if ( usage == gl46core::GL_SHADER_STORAGE_BUFFER )
1758 {
1759 return GLUtil::GLMemory::GLMemoryType::SHADER_BUFFER;
1760 }
1761 if ( usage == gl46core::GL_UNIFORM_BUFFER )
1762 {
1763 return GLUtil::GLMemory::GLMemoryType::UNIFORM_BUFFER;
1764 }
1765 if (usage == gl46core::GL_ELEMENT_ARRAY_BUFFER)
1766 {
1767 return GLUtil::GLMemory::GLMemoryType::INDEX_BUFFER;
1768 }
1769
1770 if (usage == gl46core::GL_ARRAY_BUFFER)
1771 {
1772 return GLUtil::GLMemory::GLMemoryType::VERTEX_BUFFER;
1773 }
1774
1775 return GLUtil::GLMemory::GLMemoryType::OTHER;
1776 }
1777
1778 GLUtil::GLMemory::DeviceAllocator& GL_API::GetMemoryAllocator( const GLUtil::GLMemory::GLMemoryType memoryType ) noexcept
1779 {
1780 return s_memoryAllocators[to_base( memoryType )];
1781 }
1782
1783 void GL_API::QueueFlush() noexcept
1784 {
1785 if ( Runtime::isMainThread() )
1786 {
1787 gl46core::glFlush();
1788 }
1789 else
1790 {
1791 s_glFlushQueued.store( true );
1792 }
1793 }
1794
1795 void GL_API::AddDebugMessage( const char* message, const U32 id )
1796 {
1797 PROFILE_SCOPE_AUTO( Profiler::Category::Graphics );
1798 if constexpr ( Config::ENABLE_GPU_VALIDATION )
1799 {
1800 gl46core::glPushDebugGroup( gl46core::GL_DEBUG_SOURCE_APPLICATION, id, -1, message );
1801 gl46core::glPopDebugGroup();
1802 }
1803 s_stateTracker._lastInsertedDebugMessage = {message, id};
1804 }
1805
1806 void GL_API::PushDebugMessage( const char* message, const U32 id )
1807 {
1808 PROFILE_SCOPE_AUTO( Profiler::Category::Graphics );
1809
1810 if constexpr( Config::ENABLE_GPU_VALIDATION )
1811 {
1812 gl46core::glPushDebugGroup( gl46core::GL_DEBUG_SOURCE_APPLICATION, id, -1, message );
1813 }
1814 assert( s_stateTracker._debugScopeDepth < Config::MAX_DEBUG_SCOPE_DEPTH );
1815 s_stateTracker._debugScope[s_stateTracker._debugScopeDepth++] = { message, id };
1816 }
1817
1818 void GL_API::PopDebugMessage()
1819 {
1820 PROFILE_SCOPE_AUTO( Profiler::Category::Graphics );
1821
1822 if constexpr( Config::ENABLE_GPU_VALIDATION )
1823 {
1824 gl46core::glPopDebugGroup();
1825 }
1826 s_stateTracker._debugScope[s_stateTracker._debugScopeDepth--] = {};
1827 }
1828
1829 bool GL_API::DeleteShaderPrograms( const gl46core::GLuint count, gl46core::GLuint* programs )
1830 {
1831 if ( count > 0 && programs != nullptr )
1832 {
1833 for ( gl46core::GLuint i = 0; i < count; ++i )
1834 {
1835 if ( s_stateTracker._activeShaderProgramHandle == programs[i] )
1836 {
1837 if ( s_stateTracker.setActiveProgram( 0u ) == GLStateTracker::BindResult::FAILED )
1838 {
1840 }
1841 }
1842 gl46core::glDeleteProgram( programs[i] );
1843 }
1844
1845 memset( programs, 0, count * sizeof( gl46core::GLuint ) );
1846 return true;
1847 }
1848 return false;
1849 }
1850
1851 bool GL_API::DeleteSamplers( const gl46core::GLuint count, gl46core::GLuint* samplers )
1852 {
1853 if ( count > 0 && samplers != nullptr )
1854 {
1855
1856 for ( gl46core::GLuint i = 0; i < count; ++i )
1857 {
1858 const gl46core::GLuint crtSampler = samplers[i];
1859 if ( crtSampler != 0 )
1860 {
1861 for ( gl46core::GLuint& boundSampler : s_stateTracker._samplerBoundMap )
1862 {
1863 if ( boundSampler == crtSampler )
1864 {
1865 boundSampler = 0;
1866 }
1867 }
1868 }
1869 }
1870 gl46core::glDeleteSamplers( count, samplers );
1871 memset( samplers, 0, count * sizeof( gl46core::GLuint ) );
1872 return true;
1873 }
1874
1875 return false;
1876 }
1877
1878
1879 bool GL_API::DeleteBuffers( const gl46core::GLuint count, gl46core::GLuint* buffers )
1880 {
1881 if ( count > 0 && buffers != nullptr )
1882 {
1883 for ( gl46core::GLuint i = 0; i < count; ++i )
1884 {
1885 const gl46core::GLuint crtBuffer = buffers[i];
1886
1887 for ( gl46core::GLuint& boundBuffer : s_stateTracker._activeBufferID )
1888 {
1889 if ( boundBuffer == crtBuffer )
1890 {
1891 boundBuffer = GL_NULL_HANDLE;
1892 }
1893 }
1894 if ( s_stateTracker._activeVAOIB == crtBuffer )
1895 {
1896 s_stateTracker._activeVAOIB = GL_NULL_HANDLE;
1897 }
1898 }
1899
1900 gl46core::glDeleteBuffers( count, buffers );
1901 memset( buffers, 0, count * sizeof( gl46core::GLuint ) );
1902 return true;
1903 }
1904
1905 return false;
1906 }
1907
1908 bool GL_API::DeleteFramebuffers( const gl46core::GLuint count, gl46core::GLuint* framebuffers )
1909 {
1910 if ( count > 0 && framebuffers != nullptr )
1911 {
1912 for ( gl46core::GLuint i = 0; i < count; ++i )
1913 {
1914 const gl46core::GLuint crtFB = framebuffers[i];
1915 for ( gl46core::GLuint& activeFB : s_stateTracker._activeFBID )
1916 {
1917 if ( activeFB == crtFB )
1918 {
1919 activeFB = GL_NULL_HANDLE;
1920 }
1921 }
1922 }
1923 gl46core::glDeleteFramebuffers( count, framebuffers );
1924 memset( framebuffers, 0, count * sizeof( gl46core::GLuint ) );
1925 return true;
1926 }
1927 return false;
1928 }
1929
1931 gl46core::GLuint GL_API::GetSamplerHandle( const SamplerDescriptor sampler, size_t& samplerHashInOut )
1932 {
1933 thread_local size_t cached_hash = 0u;
1934 thread_local gl46core::GLuint cached_handle = GL_NULL_HANDLE;
1935
1936 PROFILE_SCOPE_AUTO( Profiler::Category::Graphics );
1937
1938 if ( samplerHashInOut == SamplerDescriptor::INVALID_SAMPLER_HASH )
1939 {
1940 samplerHashInOut = GetHash(sampler);
1941 }
1942 DIVIDE_ASSERT( samplerHashInOut != 0u );
1943
1944 if ( cached_hash == samplerHashInOut )
1945 {
1946 return cached_handle;
1947 }
1948
1949 cached_hash = samplerHashInOut;
1950
1951 {
1952 SharedLock<SharedMutex> r_lock( s_samplerMapLock );
1953 // If we fail to find the sampler object for the given hash, we print an error and return the default OpenGL handle
1954 for ( const auto& sampler : s_samplerMap )
1955 {
1956 if ( sampler._hash == cached_hash )
1957 {
1958 // Return the OpenGL handle for the sampler object matching the specified hash value
1959 cached_handle = sampler._glHandle;
1960 return cached_handle;
1961 }
1962 }
1963 }
1964
1965 LockGuard<SharedMutex> w_lock( s_samplerMapLock );
1966 // Check again
1967 for ( const auto& sampler : s_samplerMap )
1968 {
1969 if ( sampler._hash == cached_hash )
1970 {
1971 // Return the OpenGL handle for the sampler object matching the specified hash value
1972 cached_handle = sampler._glHandle;
1973 return cached_handle;
1974 }
1975 }
1976
1977 // Create and store the newly created sample object. GL_API is responsible for deleting these!
1978 cached_handle = glSamplerObject::Construct( sampler );
1979 s_samplerMap.emplace_back(cached_hash, cached_handle );
1980 return cached_handle;
1981 }
1982
1983 glHardwareQueryPool* GL_API::GetHardwareQueryPool() noexcept
1984 {
1985 return s_hardwareQueryPool.get();
1986 }
1987
1988 gl46core::GLsync GL_API::CreateFenceSync()
1989 {
1990 PROFILE_SCOPE_AUTO( Profiler::Category::Graphics );
1991
1992 DIVIDE_ASSERT( s_fenceSyncCounter[s_LockFrameLifetime - 1u] < U32_MAX );
1993
1994 ++s_fenceSyncCounter[s_LockFrameLifetime - 1u];
1995 return gl46core::glFenceSync( gl46core::GL_SYNC_GPU_COMMANDS_COMPLETE, gl46core::UnusedMask::GL_UNUSED_BIT );
1996 }
1997
1998 void GL_API::DestroyFenceSync( gl46core::GLsync& sync )
1999 {
2000 PROFILE_SCOPE_AUTO( Profiler::Category::Graphics );
2001
2002 DIVIDE_ASSERT( s_fenceSyncCounter[s_LockFrameLifetime - 1u] > 0u );
2003
2004 --s_fenceSyncCounter[s_LockFrameLifetime - 1u];
2005 gl46core::glDeleteSync( sync );
2006 sync = nullptr;
2007 }
2008
2009 RenderTarget_uptr GL_API::newRT( const RenderTargetDescriptor& descriptor ) const
2010 {
2011 return std::make_unique<glFramebuffer>( _context, descriptor );
2012 }
2013
2014 GenericVertexData_ptr GL_API::newGVD( U32 ringBufferLength, const std::string_view name ) const
2015 {
2016 return std::make_shared<glGenericVertexData>( _context, ringBufferLength, name );
2017 }
2018
2019 ShaderBuffer_uptr GL_API::newSB( const ShaderBufferDescriptor& descriptor ) const
2020 {
2021 return std::make_unique<glShaderBuffer>( _context, descriptor );
2022 }
2023
2024};
SDL_Window SDL_Window
Definition: DisplayWindow.h:42
#define LOCALE_STR(X)
Definition: Localization.h:91
#define TO_MEGABYTES(X)
Definition: MathHelper.h:47
#define MOV(...)
#define DIVIDE_ASSERT(...)
#define NO_DESTROY
#define DIVIDE_UNEXPECTED_CALL()
#define NOP()
#define DIVIDE_UNEXPECTED_CALL_MSG(X)
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
#define PROFILE_SCOPE(NAME, CATEGORY)
Definition: Profiler.h:86
char * argv[]
Definition: main.cpp:8
WindowManager & windowManager() noexcept
Definition: Application.inl:77
static void MaxMSAASamples(const U8 maxSampleCount) noexcept
Definition: Application.h:238
static void end(glFramebuffer &rt, const RTTransitionMask &mask)
static void begin(glFramebuffer &rt, const RTDrawDescriptor &drawPolicy, const RTClearDescriptor &clearPolicy)
static void uploadPushConstants(glShaderProgram &program, const PushConstantsStruct &pushConstants)
vec2< U16 > getDrawableSize() const noexcept
SDL_Window * getRawWindow() const noexcept
Rough around the edges Adapter pattern abstracting the actual rendering API and access to the GPU.
Definition: GFXDevice.h:215
static bool IsSubmitCommand(GFX::CommandType type) noexcept
Definition: GFXDevice.inl:168
static const DeviceInformation & GetDeviceInformation() noexcept
Definition: GFXDevice.inl:158
GFXRTPool & renderTargetPool() noexcept
Definition: GFXDevice.inl:133
void registerDrawCalls(U32 count) noexcept
Definition: GFXDevice.inl:153
static U64 FrameCount() noexcept
Definition: GFXDevice.h:340
static void OverrideDeviceInformation(const DeviceInformation &info) noexcept
Definition: GFXDevice.inl:163
GFXDescriptorSet & descriptorSet(DescriptorSetUsage usage) noexcept
Definition: GFXDevice.inl:204
PerformanceMetrics & getPerformanceMetrics() noexcept
Definition: GFXDevice.inl:194
RenderTarget * getRenderTarget(const RenderTargetID target) const
Definition: GFXRTPool.cpp:50
const vector< RenderTarget_uptr > & getRenderTargets() const noexcept
Definition: GFXRTPool.h:50
static eastl::fixed_vector< TexBindEntry, 32, false > s_TexBindQueue
Definition: GLWrapper.h:184
GFXDevice & _context
Definition: GLWrapper.h:197
static std::atomic_bool s_glFlushQueued
Definition: GLWrapper.h:214
static void PopDebugMessage()
Definition: GLWrapper.cpp:1818
GL_API(GFXDevice &context)
Definition: GLWrapper.cpp:115
static glHardwareQueryPool * GetHardwareQueryPool() noexcept
Definition: GLWrapper.cpp:1983
static void DestroyFenceSync(gl46core::GLsync &sync)
Definition: GLWrapper.cpp:1998
gl46core::GLuint getGLTextureView(ImageView srcView, size_t srcViewHash, U8 lifetimeInFrames) const
Definition: GLWrapper.cpp:836
void onRenderThreadLoopStart() override
Definition: GLWrapper.cpp:552
void clearStates(GLStateTracker &stateTracker) const
Reset as much of the GL default state as possible within the limitations given.
Definition: GLWrapper.cpp:1462
static gl46core::GLsync CreateFenceSync()
Definition: GLWrapper.cpp:1988
static std::array< size_t, to_base(GLUtil::GLMemory::GLMemoryType::COUNT)> s_memoryAllocatorSizes
Definition: GLWrapper.h:221
static constexpr U32 s_LockFrameLifetime
Definition: GLWrapper.h:78
static void PushDebugMessage(const char *message, U32 id=U32_MAX)
Definition: GLWrapper.cpp:1806
gl46core::GLuint _dummyVAO
Definition: GLWrapper.h:210
Time::ProfileTimer & _swapBufferTimer
Definition: GLWrapper.h:198
bool _uniformsNeedLock
Definition: GLWrapper.h:207
static void AddDebugMessage(const char *message, U32 id=U32_MAX)
Definition: GLWrapper.cpp:1795
eastl::fixed_vector< CachedSamplerEntry, InitialSamplerMapSize, true > SamplerObjectMap
Definition: GLWrapper.h:194
GFX::MemoryBarrierCommand _uniformsMemCommand
Definition: GLWrapper.h:208
void closeRenderingAPI() override
Clear everything that was setup in initRenderingAPI()
Definition: GLWrapper.cpp:494
static SharedMutex s_samplerMapLock
Definition: GLWrapper.h:216
static GLStateTracker & GetStateTracker() noexcept
Definition: GLWrapper.cpp:1749
bool drawToWindow(DisplayWindow &window) override
Prepare the GPU for rendering a frame.
Definition: GLWrapper.cpp:545
static GLStateTracker s_stateTracker
Definition: GLWrapper.h:218
void onRenderThreadLoopEnd() override
Definition: GLWrapper.cpp:557
ErrorCode initRenderingAPI(I32 argc, char **argv, Configuration &config) override
Try and create a valid OpenGL context taking in account the specified command line arguments.
Definition: GLWrapper.cpp:123
static ShaderResult BindPipeline(GFXDevice &context, const Pipeline &pipeline)
Definition: GLWrapper.cpp:1674
void flushPushConstantsLocks()
Definition: GLWrapper.cpp:873
eastl::stack< HardwareQueryContext > _queryContext
Definition: GLWrapper.h:204
bool frameEnded() override
Definition: GLWrapper.cpp:622
std::array< glHardwareQueryRing_uptr, to_base(GlobalQueryTypes::COUNT)> _performanceQueries
Hardware query objects used for performance measurements.
Definition: GLWrapper.h:202
void flushCommand(GFX::CommandBase *cmd) override
Definition: GLWrapper.cpp:889
void flushWindow(DisplayWindow &window) override
Definition: GLWrapper.cpp:566
void idle(bool fast) override
Definition: GLWrapper.cpp:677
static SamplerObjectMap s_samplerMap
Definition: GLWrapper.h:217
static U32 s_fenceSyncCounter[s_LockFrameLifetime]
Definition: GLWrapper.h:227
bool _runQueries
Definition: GLWrapper.h:205
void preFlushCommandBuffer(Handle< GFX::CommandBuffer > commandBuffer) override
Definition: GLWrapper.cpp:883
bool frameStarted() override
Definition: GLWrapper.cpp:587
static std::array< GLUtil::GLMemory::DeviceAllocator, to_base(GLUtil::GLMemory::GLMemoryType::COUNT)> s_memoryAllocators
Definition: GLWrapper.h:220
void endPerformanceQueries()
Definition: GLWrapper.cpp:530
static GLUtil::glTextureViewCache s_textureViewCache
Definition: GLWrapper.h:223
static std::unique_ptr< glHardwareQueryPool > s_hardwareQueryPool
Definition: GLWrapper.h:225
void prepareFlushWindow(DisplayWindow &window) override
Definition: GLWrapper.cpp:561
void flushTextureBindQueue()
Definition: GLWrapper.cpp:735
static bool Draw(const GenericDrawCommand &cmd)
Definition: GLWrapper.cpp:682
std::pair< gl46core::GLuint, bool > allocate(size_t hash, bool retry=false)
void deallocate(gl46core::GLuint handle, U32 frameDelay=1)
FORCE_INLINE I64 getGUID() const noexcept
Definition: GUIDWrapper.h:51
static size_t TotalThreadCount(TaskPoolType type) noexcept
Definition: Kernel.cpp:51
static void CleanExpiredSyncObjects(RenderAPI api, U64 frameNumber)
Definition: LockManager.cpp:29
static void Clear()
Definition: LockManager.cpp:64
static SyncObjectHandle CreateSyncObject(RenderAPI api, U8 flag=DEFAULT_SYNC_FLAG_INTERNAL)
T * find(PoolHandle handle) const
Definition: ObjectPool.inl:46
PlatformContext & context() noexcept
DisplayWindow & mainWindow() noexcept
Application & app() noexcept
Renderer Programming Interface.
U16 getWidth() const noexcept
U16 getHeight() const noexcept
static void DestroyStaticData()
bool uploadUniformData(const UniformData &data, DescriptorSet &set, GFX::MemoryBarrierCommand &memCmdInOut)
ImageView getView() const noexcept
Definition: Texture.cpp:597
virtual void draw(const GenericDrawCommand &command, VDIUserData *data)=0
static constexpr Handle INVALID_VDI_HANDLE
void blitFrom(RenderTarget *source, const RTBlitParams &params)
glHardwareQueryRing & allocate(gl46core::GLenum queryType)
static void Destruct(gl46core::GLuint &handle)
Base class for shader uniform blocks.
bool bindByteRange(U8 bindIndex, BufferRange range, I32 readIndex=-1)
OpenGL implementation of the ShaderProgram entity.
static void Idle(PlatformContext &platformContext)
static void Copy(const glTexture *source, U8 sourceSamples, const glTexture *destination, U8 destinationSamples, const CopyTexParams &params)
Definition: glTexture.cpp:366
void * SDL_GLContext
Definition: glResources.h:39
constexpr bool ENABLE_GPU_VALIDATION
Error callbacks, validations, buffer checks, etc. are controlled by this flag. Heavy performance impa...
Definition: config.h:192
constexpr U8 MAX_CLIP_DISTANCES
Definition: config.h:127
void OnFrameEnd(const U64 frameCount)
std::array< gl46core::GLenum, to_base(QueryType::COUNT)> glQueryTypeTable
Definition: glResources.cpp:97
const DisplayWindow * s_glMainRenderWindow
Definition: glResources.cpp:82
void getGLValue(gl46core::GLenum param, T &value, gl46core::GLint index=-1)
Wrapper for glGetIntegerv.
Definition: glResources.inl:40
void SubmitRenderCommand(const GenericDrawCommand &drawCommand, const gl46core::GLenum internalFormat)
Note: If internal format is not GL_NONE, an indexed draw is issued!
bool ValidateSDL(const I32 errCode, bool assert)
void OnStartup()
Populate enumeration tables with appropriate API values.
Definition: glResources.cpp:99
gl46core::GLenum internalTextureType(const TextureType type, const U8 msaaSamples)
void DebugCallback(const gl46core::GLenum source, const gl46core::GLenum type, const gl46core::GLuint id, const gl46core::GLenum severity, const gl46core::GLsizei length, const gl46core::GLchar *message, const void *userParam)
Print OpenGL specific messages.
FormatAndDataType InternalFormatAndDataType(const GFXImageFormat baseFormat, const GFXDataFormat dataType, const GFXImagePacking packing) noexcept
constexpr Optick::Category::Type Graphics
Definition: Profiler.h:60
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
std::array< DescriptorSetEntry, to_base(DescriptorSetUsage::COUNT)> DescriptorSetEntries
std::lock_guard< mutex > LockGuard
Definition: SharedMutex.h:55
constexpr U32 to_U32(const T value)
static constexpr U32 RT_DEPTH_ATTACHMENT_IDX
Definition: RTAttachment.h:71
void Draw()
TextureType TargetType(const ImageView &imageView) noexcept
bool isEnabledOption(const GenericDrawCommand &cmd, CmdRenderOptions option) noexcept
constexpr U64 to_U64(const T value)
constexpr RenderTargetID SCREEN_TARGET_ID
int32_t I32
T * ResourcePtr
Definition: Resource.h:112
uint8_t U8
bool DebugBreak(const bool condition) noexcept
bool IsCubeTexture(TextureType texType) noexcept
bool IsArrayTexture(TextureType texType) noexcept
std::shared_mutex SharedMutex
Definition: SharedMutex.h:43
size_t GetHash(const PropertyDescriptor< T > &descriptor) noexcept
Definition: Resource.inl:40
constexpr gl46core::GLuint GL_NULL_HANDLE
Invalid object value. Used to compare handles and determine if they were properly created.
Definition: glResources.h:105
eastl::vector< Type > vector
Definition: Vector.h:42
std::shared_lock< mutex > SharedLock
Definition: SharedMutex.h:49
constexpr U8 INVALID_TEXTURE_BINDING
constexpr U16 U16_MAX
uint16_t U16
constexpr RenderTargetID INVALID_RENDER_TARGET_ID
constexpr U8 U8_MAX
void efficient_clear(eastl::fixed_vector< T, nodeCount, bEnableOverflow, OverflowAllocator > &fixed_vector)
Definition: Vector.h:52
constexpr U8 to_U8(const T value)
::value constexpr T CLAMPED(T n, T min, T max) noexcept
Definition: MathHelper.inl:126
constexpr U32 U32_MAX
constexpr bool g_breakOnGLCall
Definition: GLWrapper.cpp:60
::value constexpr void CLAMP(T &n, T min, T max) noexcept
Clamps value n between min and max.
Definition: MathHelper.inl:114
int64_t I64
FORCE_INLINE T * Get(const Handle< T > handle)
uint32_t U32
Project const SceneEntry & entry
Definition: DefaultScene.h:41
constexpr U8 U8_ZERO
constexpr T toBit(const T X)
Converts an arbitrary positive integer value to a bitwise value used for masks.
constexpr auto to_base(const Type value) -> Type
BufferUpdateUsage _updateUsage
Definition: BufferParams.h:40
BufferUsageType _usageType
Definition: BufferParams.h:41
BufferUpdateFrequency _updateFrequency
Definition: BufferParams.h:39
BufferRange _range
Definition: BufferLocks.h:57
BufferSyncUsage _type
Definition: BufferLocks.h:58
LockableBuffer * _buffer
Definition: BufferLocks.h:60
struct Divide::Configuration::Debug::Renderer renderer
Divide::Configuration::Rendering::ShadowMapping::CSMSettings csm
struct Divide::Configuration::Rendering::ShadowMapping shadowMapping
struct Divide::Configuration::Rendering rendering
struct Divide::Configuration::Debug debug
static NO_INLINE void errorfn(const char *format, T &&... args)
static NO_INLINE void printfn(const char *format, T &&... args)
static NO_INLINE void d_errorfn(const char *format, T &&... args)
DescriptorSetBindingType _type
DescriptorCombinedImageSampler _sampledImage
DescriptorSetBindingData _data
RTClearDescriptor _clearDescriptor
Definition: Commands.inl:97
UColour4 _clearColour
r = depth, g = stencil if target is a depth(+stencil) attachment
Definition: Commands.inl:138
FORCE_INLINE T * As()
Definition: Commands.h:60
Handle< Texture > _destination
Definition: Commands.inl:122
RTTransitionMask _transitionMask
Definition: Commands.inl:102
TextureLayoutChanges _textureLayoutChanges
Definition: Commands.inl:191
PixelAlignment _pixelPackAlignment
Definition: Commands.inl:130
Handle< Texture > _texture
Definition: Commands.inl:129
DELEGATE_STD< void, const ImageReadbackData & > _callback
Definition: Commands.inl:132
Definition: GLWrapper.h:178
gl46core::GLuint _sampler
Definition: GLWrapper.h:180
gl46core::GLubyte _slot
Definition: GLWrapper.h:181
gl46core::GLuint _handle
Definition: GLWrapper.h:179
Definition: GLWrapper.h:171
RenderTargetID _activeRenderTargetID
eastl::queue< std::pair< gl46core::GLsync, U64 > > _endFrameFences
static constexpr U8 MAX_BOUND_TEXTURE_UNITS
BindResult setActiveBuffer(gl46core::GLenum target, gl46core::GLuint bufferHandle)
Single place to change buffer objects for every target available.
vector< gl46core::GLboolean > _blendEnabled
void setBlendColour(const UColour4 &blendColour)
Pipeline const * _activePipeline
void setBlending(const BlendingSettings &blendingProperties)
BindResult setStateBlock(const RenderStateBlock &stateBlock)
glShaderProgram * _activeShaderProgram
PrimitiveTopology _activeTopology
bool setScissor(const Rect< I32 > &newScissorRect)
void setPrimitiveTopology(PrimitiveTopology topology)
vec2< U16 > _activeRenderTargetDimensions
BindResult bindTexture(gl46core::GLubyte unit, gl46core::GLuint handle, gl46core::GLuint samplerHandle=0u)
Bind a texture specified by a GL handle and GL type to the specified unit using the sampler object de...
BindResult setActiveShaderPipeline(gl46core::GLuint pipelineHandle)
Change the currently active shader pipeline. Returns false if the pipeline was already bound.
BindResult bindTextures(gl46core::GLubyte unitOffset, gl46core::GLuint textureCount, const gl46core::GLuint *textureHandles, const gl46core::GLuint *samplerHandles)
Bind multiple textures specified by an array of handles and an offset unit.
BindResult setActiveProgram(gl46core::GLuint programHandle)
Change the currently active shader program. Returns false if the program was already bound.
bool setClearColour(const FColour4 &colour)
BindResult setActiveFB(RenderTarget::Usage usage, gl46core::GLuint ID)
glFramebuffer * _activeRenderTarget
bool setClearDepth(F32 value)
IndirectIndexedDrawCommand _cmd
ImageViewDescriptor _descriptor
TextureType _targetType
ImageSubRange _subRange
const Texture * _srcTexture
bool lockRange(BufferRange range, SyncObjectHandle &sync) const
Definition: BufferLocks.cpp:26
U64 _verticesSubmitted
Returns the time in milliseconds that it took to render one frame.
U64 _tessellationInvocations
Number of times the tessellation control shader has been invoked.
U32 _syncObjectsInFlight[3]
Number of active sync objects.
U64 _queuedGPUFrames
How many frames are still queued up for execution on the GPU.
U64 _tessellationPatches
Number of patches processed by the tessellation control shader.
PrimitiveTopology _primitiveTopology
Definition: Pipeline.h:48
AttributeMap _vertexFormat
Definition: Pipeline.h:49
Handle< ShaderProgram > _shaderProgramHandle
Definition: Pipeline.h:47
RTBlendStates _blendStates
Definition: Pipeline.h:45
RenderStateBlock _stateBlock
Definition: Pipeline.h:46
std::array< BlendingSettings, to_base(RTColourAttachmentSlot::COUNT)> _settings
BufferRange _range
I32 _queueReadIndex
ShaderBuffer * _buffer
static constexpr size_t INVALID_SYNC_ID
Definition: LockManager.h:62
bool getAvailableContext(SDL_GLContext &ctx) noexcept
Definition: GLWrapper.cpp:95
bool init(const size_t size, const DisplayWindow &window)
Definition: GLWrapper.cpp:72
Definition: GLWrapper.cpp:65