1#include "backend_opengl.h"
2
3#include <base/dbg.h>
4#include <base/detect.h>
5#include <base/log.h>
6#include <base/mem.h>
7#include <base/str.h>
8
9#include <engine/client/backend_sdl.h>
10#include <engine/graphics.h>
11
12#include <cstdint>
13
14#if defined(BACKEND_AS_OPENGL_ES) || !defined(CONF_BACKEND_OPENGL_ES)
15
16#include <engine/client/backend/glsl_shader_compiler.h>
17#include <engine/client/backend/opengl/opengl_sl.h>
18#include <engine/client/backend/opengl/opengl_sl_program.h>
19#include <engine/client/blocklist_driver.h>
20#include <engine/gfx/image_manipulation.h>
21
22#ifndef BACKEND_AS_OPENGL_ES
23#include <GL/glew.h>
24#else
25#include <GLES3/gl3.h>
26#define GL_TEXTURE_2D_ARRAY_EXT GL_TEXTURE_2D_ARRAY
27// GLES doesn't support GL_QUADS, but the code is also never executed
28#define GL_QUADS GL_TRIANGLES
29#ifndef CONF_BACKEND_OPENGL_ES3
30#include <GLES/gl.h>
31#define glOrtho glOrthof
32#else
33#define BACKEND_GL_MODERN_API 1
34#endif
35#endif
36
37// ------------ CCommandProcessorFragment_OpenGL
38void CCommandProcessorFragment_OpenGL::Cmd_Update_Viewport(const CCommandBuffer::SCommand_Update_Viewport *pCommand)
39{
40 if(pCommand->m_ByResize)
41 {
42 m_CanvasWidth = (uint32_t)pCommand->m_Width;
43 m_CanvasHeight = (uint32_t)pCommand->m_Height;
44 }
45 glViewport(x: pCommand->m_X, y: pCommand->m_Y, width: pCommand->m_Width, height: pCommand->m_Height);
46}
47
48size_t CCommandProcessorFragment_OpenGL::GLFormatToPixelSize(int GLFormat)
49{
50 switch(GLFormat)
51 {
52 case GL_RGBA: return 4;
53 case GL_RGB: return 3;
54 case GL_RED: return 1;
55 case GL_ALPHA: return 1;
56 default: return 4;
57 }
58}
59
60bool CCommandProcessorFragment_OpenGL::IsTexturedState(const CCommandBuffer::SState &State)
61{
62 return State.m_Texture >= 0 && State.m_Texture < (int)m_vTextures.size();
63}
64
65void CCommandProcessorFragment_OpenGL::SetState(const CCommandBuffer::SState &State, bool Use2DArrayTextures)
66{
67#ifndef BACKEND_GL_MODERN_API
68 // blend
69 switch(State.m_BlendMode)
70 {
71 case EBlendMode::NONE:
72 glDisable(GL_BLEND);
73 break;
74 case EBlendMode::ALPHA:
75 glEnable(GL_BLEND);
76 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
77 break;
78 case EBlendMode::ADDITIVE:
79 glEnable(GL_BLEND);
80 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
81 break;
82 default:
83 dbg_assert_failed("Invalid blend mode: %d", (int)State.m_BlendMode);
84 };
85 m_LastBlendMode = State.m_BlendMode;
86
87 // clip
88 if(State.m_ClipEnable)
89 {
90 glScissor(x: State.m_ClipX, y: State.m_ClipY, width: State.m_ClipW, height: State.m_ClipH);
91 glEnable(GL_SCISSOR_TEST);
92 m_LastClipEnable = true;
93 }
94 else if(m_LastClipEnable)
95 {
96 // Don't disable it always
97 glDisable(GL_SCISSOR_TEST);
98 m_LastClipEnable = false;
99 }
100
101 glDisable(GL_TEXTURE_2D);
102 if(!m_HasShaders)
103 {
104 if(m_Has3DTextures)
105 glDisable(GL_TEXTURE_3D);
106 if(m_Has2DArrayTextures)
107 {
108 glDisable(cap: m_2DArrayTarget);
109 }
110 }
111
112 if(m_HasShaders && IsNewApi())
113 {
114 glBindSampler(0, 0);
115 }
116
117 // texture
118 if(IsTexturedState(State))
119 {
120 if(!Use2DArrayTextures)
121 {
122 glEnable(GL_TEXTURE_2D);
123 glBindTexture(GL_TEXTURE_2D, texture: m_vTextures[State.m_Texture].m_Tex);
124
125 if(m_vTextures[State.m_Texture].m_LastWrapMode != State.m_WrapMode)
126 {
127 switch(State.m_WrapMode)
128 {
129 case EWrapMode::REPEAT:
130 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
131 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
132 break;
133 case EWrapMode::CLAMP:
134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
136 break;
137 default:
138 dbg_assert_failed("Invalid wrap mode: %d", (int)State.m_WrapMode);
139 };
140 m_vTextures[State.m_Texture].m_LastWrapMode = State.m_WrapMode;
141 }
142 }
143 else if(m_Has2DArrayTextures)
144 {
145 if(!m_HasShaders)
146 glEnable(cap: m_2DArrayTarget);
147 glBindTexture(target: m_2DArrayTarget, texture: m_vTextures[State.m_Texture].m_Tex2DArray);
148 }
149 else if(m_Has3DTextures)
150 {
151 if(!m_HasShaders)
152 glEnable(GL_TEXTURE_3D);
153 glBindTexture(GL_TEXTURE_3D, texture: m_vTextures[State.m_Texture].m_Tex2DArray);
154 }
155 else
156 {
157 dbg_assert_failed("Should have either 2D, 3D or no texture array support");
158 }
159 }
160
161 // screen mapping
162 glMatrixMode(GL_PROJECTION);
163 glLoadIdentity();
164 glOrtho(left: State.m_ScreenTL.x, right: State.m_ScreenBR.x, bottom: State.m_ScreenBR.y, top: State.m_ScreenTL.y, zNear: -10.0f, zFar: 10.f);
165#endif
166}
167
168static void ParseVersionString(EBackendType BackendType, const char *pStr, int &VersionMajor, int &VersionMinor, int &VersionPatch)
169{
170 if(pStr)
171 {
172 // if backend is GLES, it starts with "OpenGL ES " or OpenGL ES-CM for older contexts, rest is the same
173 if(BackendType == BACKEND_TYPE_OPENGL_ES)
174 {
175 int StrLenGLES = str_length(str: "OpenGL ES ");
176 int StrLenGLESCM = str_length(str: "OpenGL ES-CM ");
177 if(str_comp_num(a: pStr, b: "OpenGL ES ", num: StrLenGLES) == 0)
178 pStr += StrLenGLES;
179 else if(str_comp_num(a: pStr, b: "OpenGL ES-CM ", num: StrLenGLESCM) == 0)
180 pStr += StrLenGLESCM;
181 }
182
183 char aCurNumberStr[32];
184 size_t CurNumberStrLen = 0;
185 size_t TotalNumbersPassed = 0;
186 int aNumbers[3] = {0};
187 bool LastWasNumber = false;
188 while(*pStr && TotalNumbersPassed < 3)
189 {
190 if(str_isnum(c: *pStr))
191 {
192 aCurNumberStr[CurNumberStrLen++] = (char)*pStr;
193 LastWasNumber = true;
194 }
195 else if(LastWasNumber && (*pStr == '.' || *pStr == ' '))
196 {
197 if(CurNumberStrLen > 0)
198 {
199 aCurNumberStr[CurNumberStrLen] = 0;
200 aNumbers[TotalNumbersPassed++] = str_toint(str: aCurNumberStr);
201 CurNumberStrLen = 0;
202 }
203
204 LastWasNumber = false;
205
206 if(*pStr != '.')
207 break;
208 }
209 else
210 {
211 break;
212 }
213
214 ++pStr;
215 }
216
217 VersionMajor = aNumbers[0];
218 VersionMinor = aNumbers[1];
219 VersionPatch = aNumbers[2];
220 }
221}
222
223#ifndef BACKEND_AS_OPENGL_ES
224static LEVEL GetLogSeverity(GLenum Severity)
225{
226 switch(Severity)
227 {
228 case GL_DEBUG_SEVERITY_HIGH: return LEVEL_ERROR;
229 case GL_DEBUG_SEVERITY_MEDIUM: return LEVEL_WARN;
230 case GL_DEBUG_SEVERITY_LOW: return LEVEL_INFO;
231 case GL_DEBUG_SEVERITY_NOTIFICATION: return LEVEL_DEBUG;
232 default: dbg_assert_failed("Severity invalid: %d", (int)Severity);
233 }
234}
235
236static const char *GetErrorName(GLenum Type)
237{
238 switch(Type)
239 {
240 case GL_DEBUG_TYPE_ERROR: return "ERROR";
241 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "DEPRECATED BEHAVIOR";
242 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "UNDEFINED BEHAVIOR";
243 case GL_DEBUG_TYPE_PORTABILITY: return "PORTABILITY";
244 case GL_DEBUG_TYPE_PERFORMANCE: return "PERFORMANCE";
245 case GL_DEBUG_TYPE_OTHER: return "OTHER";
246 case GL_DEBUG_TYPE_MARKER: return "MARKER";
247 case GL_DEBUG_TYPE_PUSH_GROUP: return "PUSH_GROUP";
248 case GL_DEBUG_TYPE_POP_GROUP: return "POP_GROUP";
249 default: return "UNKNOWN";
250 }
251}
252
253static const char *GetSeverityString(GLenum Severity)
254{
255 switch(Severity)
256 {
257 // All OpenGL Errors, shader compilation/linking errors, or highly-dangerous undefined behavior
258 case GL_DEBUG_SEVERITY_HIGH: return "high";
259 // Major performance warnings, shader compilation/linking warnings, or the use of deprecated functionality
260 case GL_DEBUG_SEVERITY_MEDIUM: return "medium";
261 // Redundant state change performance warning, or unimportant undefined behavior
262 case GL_DEBUG_SEVERITY_LOW: return "low";
263 // Anything that isn't an error or performance issue.
264 case GL_DEBUG_SEVERITY_NOTIFICATION: return "notification";
265 default: dbg_assert_failed("Severity invalid: %d", (int)Severity);
266 }
267}
268
269static void GLAPIENTRY
270GfxOpenGLMessageCallback(GLenum Source,
271 GLenum Type,
272 GLuint Id,
273 GLenum Severity,
274 GLsizei Length,
275 const GLchar *pMsg,
276 const void *pUserParam)
277{
278 log_log(level: GetLogSeverity(Severity), sys: "gfx/opengl", fmt: "[%s] (importance: %s) %s", GetErrorName(Type), GetSeverityString(Severity), pMsg);
279}
280#endif
281
282bool CCommandProcessorFragment_OpenGL::GetPresentedImageData(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData)
283{
284 if(m_CanvasWidth == 0 || m_CanvasHeight == 0)
285 {
286 return false;
287 }
288 else
289 {
290 Width = m_CanvasWidth;
291 Height = m_CanvasHeight;
292 Format = CImageInfo::FORMAT_RGBA;
293 vDstData.resize(sz: (size_t)Width * (Height + 1) * 4); // +1 for flipping image
294 glReadBuffer(GL_FRONT);
295 GLint Alignment;
296 glGetIntegerv(GL_PACK_ALIGNMENT, params: &Alignment);
297 glPixelStorei(GL_PACK_ALIGNMENT, param: 1);
298 glReadPixels(x: 0, y: 0, width: m_CanvasWidth, height: m_CanvasHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels: vDstData.data());
299 glPixelStorei(GL_PACK_ALIGNMENT, param: Alignment);
300
301 uint8_t *pTempRow = vDstData.data() + Width * Height * 4;
302 for(uint32_t Y = 0; Y < Height / 2; ++Y)
303 {
304 mem_copy(dest: pTempRow, source: vDstData.data() + Y * Width * 4, size: Width * 4);
305 mem_copy(dest: vDstData.data() + Y * Width * 4, source: vDstData.data() + ((Height - Y) - 1) * Width * 4, size: Width * 4);
306 mem_copy(dest: vDstData.data() + ((Height - Y) - 1) * Width * 4, source: pTempRow, size: Width * 4);
307 }
308
309 return true;
310 }
311}
312
313bool CCommandProcessorFragment_OpenGL::InitOpenGL(const SCommand_Init *pCommand)
314{
315 m_IsOpenGLES = pCommand->m_RequestedBackend == BACKEND_TYPE_OPENGL_ES;
316
317 *pCommand->m_pReadPresentedImageDataFunc = [this](uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData) {
318 return GetPresentedImageData(Width, Height, Format, vDstData);
319 };
320
321 const char *pVendorString = (const char *)glGetString(GL_VENDOR);
322 log_info("gfx/opengl", "Vendor string: %s", pVendorString);
323
324 // check what this context can do
325 const char *pVersionString = (const char *)glGetString(GL_VERSION);
326 log_info("gfx/opengl", "Version string: %s", pVersionString);
327
328 const char *pRendererString = (const char *)glGetString(GL_RENDERER);
329
330 str_copy(dst: pCommand->m_pVendorString, src: pVendorString, dst_size: GPU_INFO_STRING_SIZE);
331 str_copy(dst: pCommand->m_pVersionString, src: pVersionString, dst_size: GPU_INFO_STRING_SIZE);
332 str_copy(dst: pCommand->m_pRendererString, src: pRendererString, dst_size: GPU_INFO_STRING_SIZE);
333
334 // parse version string
335 ParseVersionString(BackendType: pCommand->m_RequestedBackend, pStr: pVersionString, VersionMajor&: pCommand->m_pCapabilities->m_ContextMajor, VersionMinor&: pCommand->m_pCapabilities->m_ContextMinor, VersionPatch&: pCommand->m_pCapabilities->m_ContextPatch);
336
337 *pCommand->m_pInitError = 0;
338
339 int BlocklistMajor = -1, BlocklistMinor = -1, BlocklistPatch = -1;
340 bool RequiresWarning = false;
341 const char *pErrString = ParseBlocklistDriverVersions(pVendorStr: pVendorString, pVersionStr: pVersionString, BlocklistMajor, BlocklistMinor, BlocklistPatch, RequiresWarning);
342 // if the driver is buggy, and the requested GL version is the default, fallback
343 if(pErrString != NULL && pCommand->m_RequestedMajor == 3 && pCommand->m_RequestedMinor == 0 && pCommand->m_RequestedPatch == 0)
344 {
345 // if not already in the error state, set the GL version
346 if(g_Config.m_GfxDriverIsBlocked == 0)
347 {
348 // fallback to known good GL version
349 pCommand->m_pCapabilities->m_ContextMajor = BlocklistMajor;
350 pCommand->m_pCapabilities->m_ContextMinor = BlocklistMinor;
351 pCommand->m_pCapabilities->m_ContextPatch = BlocklistPatch;
352
353 // set backend error string
354 if(RequiresWarning)
355 *pCommand->m_pErrStringPtr = pErrString;
356 *pCommand->m_pInitError = -2;
357
358 g_Config.m_GfxDriverIsBlocked = 1;
359 }
360 }
361 // if the driver was in a blocked error state, but is not anymore, reset all config variables
362 else if(pErrString == NULL && g_Config.m_GfxDriverIsBlocked == 1)
363 {
364 pCommand->m_pCapabilities->m_ContextMajor = 3;
365 pCommand->m_pCapabilities->m_ContextMinor = 0;
366 pCommand->m_pCapabilities->m_ContextPatch = 0;
367
368 // tell the caller to reinitialize the context
369 *pCommand->m_pInitError = -2;
370
371 g_Config.m_GfxDriverIsBlocked = 0;
372 }
373
374 int MajorV = pCommand->m_pCapabilities->m_ContextMajor;
375
376 if(pCommand->m_RequestedBackend == BACKEND_TYPE_OPENGL)
377 {
378#ifndef BACKEND_AS_OPENGL_ES
379 int MinorV = pCommand->m_pCapabilities->m_ContextMinor;
380 if(*pCommand->m_pInitError == 0)
381 {
382 if(MajorV < pCommand->m_RequestedMajor)
383 {
384 *pCommand->m_pInitError = -2;
385 }
386 else if(MajorV == pCommand->m_RequestedMajor)
387 {
388 if(MinorV < pCommand->m_RequestedMinor)
389 {
390 *pCommand->m_pInitError = -2;
391 }
392 else if(MinorV == pCommand->m_RequestedMinor)
393 {
394 int PatchV = pCommand->m_pCapabilities->m_ContextPatch;
395 if(PatchV < pCommand->m_RequestedPatch)
396 {
397 *pCommand->m_pInitError = -2;
398 }
399 }
400 }
401 }
402
403 if(*pCommand->m_pInitError == 0)
404 {
405 MajorV = pCommand->m_RequestedMajor;
406 MinorV = pCommand->m_RequestedMinor;
407
408 pCommand->m_pCapabilities->m_2DArrayTexturesAsExtension = false;
409 pCommand->m_pCapabilities->m_NPOTTextures = true;
410 pCommand->m_pCapabilities->m_TrianglesAsQuads = false;
411
412 if(MajorV >= 4 || (MajorV == 3 && MinorV == 3))
413 {
414 pCommand->m_pCapabilities->m_TileBuffering = true;
415 pCommand->m_pCapabilities->m_QuadBuffering = true;
416 pCommand->m_pCapabilities->m_TextBuffering = true;
417 pCommand->m_pCapabilities->m_QuadContainerBuffering = true;
418 pCommand->m_pCapabilities->m_ShaderSupport = true;
419
420 pCommand->m_pCapabilities->m_MipMapping = true;
421 pCommand->m_pCapabilities->m_3DTextures = true;
422 pCommand->m_pCapabilities->m_2DArrayTextures = true;
423
424 pCommand->m_pCapabilities->m_TrianglesAsQuads = true;
425 }
426 else if(MajorV == 3)
427 {
428 pCommand->m_pCapabilities->m_MipMapping = true;
429 // check for context native 2D array texture size
430 pCommand->m_pCapabilities->m_3DTextures = false;
431 pCommand->m_pCapabilities->m_2DArrayTextures = false;
432 pCommand->m_pCapabilities->m_ShaderSupport = true;
433
434 int TextureLayers = 0;
435 glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, params: &TextureLayers);
436 if(TextureLayers >= 256)
437 {
438 pCommand->m_pCapabilities->m_2DArrayTextures = true;
439 }
440
441 int Texture3DSize = 0;
442 glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, params: &Texture3DSize);
443 if(Texture3DSize >= 256)
444 {
445 pCommand->m_pCapabilities->m_3DTextures = true;
446 }
447
448 if(!pCommand->m_pCapabilities->m_3DTextures && !pCommand->m_pCapabilities->m_2DArrayTextures)
449 {
450 *pCommand->m_pInitError = -2;
451 pCommand->m_pCapabilities->m_ContextMajor = 1;
452 pCommand->m_pCapabilities->m_ContextMinor = 5;
453 pCommand->m_pCapabilities->m_ContextPatch = 0;
454 }
455
456 pCommand->m_pCapabilities->m_TileBuffering = pCommand->m_pCapabilities->m_2DArrayTextures;
457 pCommand->m_pCapabilities->m_QuadBuffering = false;
458 pCommand->m_pCapabilities->m_TextBuffering = false;
459 pCommand->m_pCapabilities->m_QuadContainerBuffering = false;
460 }
461 else if(MajorV == 2)
462 {
463 pCommand->m_pCapabilities->m_MipMapping = true;
464 // check for context extension: 2D array texture and its max size
465 pCommand->m_pCapabilities->m_3DTextures = false;
466 pCommand->m_pCapabilities->m_2DArrayTextures = false;
467
468 pCommand->m_pCapabilities->m_ShaderSupport = false;
469
470 int Texture3DSize = 0;
471 glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, params: &Texture3DSize);
472 if(Texture3DSize >= 256)
473 {
474 pCommand->m_pCapabilities->m_3DTextures = true;
475 }
476
477 pCommand->m_pCapabilities->m_TileBuffering = false;
478 pCommand->m_pCapabilities->m_QuadBuffering = false;
479 pCommand->m_pCapabilities->m_TextBuffering = false;
480 pCommand->m_pCapabilities->m_QuadContainerBuffering = false;
481
482 pCommand->m_pCapabilities->m_NPOTTextures = GLEW_ARB_texture_non_power_of_two || pCommand->m_GlewMajor > 2;
483
484 if(!pCommand->m_pCapabilities->m_NPOTTextures || (!pCommand->m_pCapabilities->m_3DTextures && !pCommand->m_pCapabilities->m_2DArrayTextures))
485 {
486 *pCommand->m_pInitError = -2;
487 pCommand->m_pCapabilities->m_ContextMajor = 1;
488 pCommand->m_pCapabilities->m_ContextMinor = 5;
489 pCommand->m_pCapabilities->m_ContextPatch = 0;
490 }
491 }
492 else if(MajorV < 2)
493 {
494 pCommand->m_pCapabilities->m_TileBuffering = false;
495 pCommand->m_pCapabilities->m_QuadBuffering = false;
496 pCommand->m_pCapabilities->m_TextBuffering = false;
497 pCommand->m_pCapabilities->m_QuadContainerBuffering = false;
498 pCommand->m_pCapabilities->m_ShaderSupport = false;
499
500 pCommand->m_pCapabilities->m_MipMapping = false;
501 pCommand->m_pCapabilities->m_3DTextures = false;
502 pCommand->m_pCapabilities->m_2DArrayTextures = false;
503 pCommand->m_pCapabilities->m_NPOTTextures = false;
504 }
505 }
506#endif
507 }
508 else if(pCommand->m_RequestedBackend == BACKEND_TYPE_OPENGL_ES)
509 {
510 if(MajorV < 3)
511 {
512 pCommand->m_pCapabilities->m_TileBuffering = false;
513 pCommand->m_pCapabilities->m_QuadBuffering = false;
514 pCommand->m_pCapabilities->m_TextBuffering = false;
515 pCommand->m_pCapabilities->m_QuadContainerBuffering = false;
516 pCommand->m_pCapabilities->m_ShaderSupport = false;
517
518 pCommand->m_pCapabilities->m_MipMapping = false;
519 pCommand->m_pCapabilities->m_3DTextures = false;
520 pCommand->m_pCapabilities->m_2DArrayTextures = false;
521 pCommand->m_pCapabilities->m_NPOTTextures = false;
522
523 pCommand->m_pCapabilities->m_TrianglesAsQuads = false;
524 }
525 else
526 {
527 pCommand->m_pCapabilities->m_TileBuffering = true;
528 pCommand->m_pCapabilities->m_QuadBuffering = true;
529 pCommand->m_pCapabilities->m_TextBuffering = true;
530 pCommand->m_pCapabilities->m_QuadContainerBuffering = true;
531 pCommand->m_pCapabilities->m_ShaderSupport = true;
532
533 pCommand->m_pCapabilities->m_MipMapping = true;
534 pCommand->m_pCapabilities->m_3DTextures = true;
535 pCommand->m_pCapabilities->m_2DArrayTextures = true;
536 pCommand->m_pCapabilities->m_NPOTTextures = true;
537
538 pCommand->m_pCapabilities->m_TrianglesAsQuads = true;
539 }
540 }
541
542 if(*pCommand->m_pInitError != -2)
543 {
544 // set some default settings
545 glEnable(GL_BLEND);
546 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
547 glDisable(GL_CULL_FACE);
548 glDisable(GL_DEPTH_TEST);
549
550#ifndef BACKEND_GL_MODERN_API
551 if(!IsNewApi())
552 {
553 glAlphaFunc(GL_GREATER, ref: 0);
554 glEnable(GL_ALPHA_TEST);
555 }
556#endif
557
558 glDepthMask(flag: 0);
559
560#ifndef BACKEND_AS_OPENGL_ES
561 if(g_Config.m_DbgGfx != DEBUG_GFX_MODE_NONE)
562 {
563 if(GLEW_KHR_debug || GLEW_ARB_debug_output)
564 {
565 // During init, enable debug output
566 if(GLEW_KHR_debug)
567 {
568 glEnable(GL_DEBUG_OUTPUT);
569 glDebugMessageCallback((GLDEBUGPROC)GfxOpenGLMessageCallback, 0);
570 }
571 else if(GLEW_ARB_debug_output)
572 {
573 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
574 glDebugMessageCallbackARB((GLDEBUGPROC)GfxOpenGLMessageCallback, 0);
575 }
576 log_info("gfx/opengl", "Enabled OpenGL debug mode");
577 }
578 else
579 {
580 log_warn("gfx/opengl", "Requested OpenGL debug mode, but the driver does not support the required extension");
581 }
582 }
583#endif
584
585 return true;
586 }
587 else
588 return false;
589}
590
591bool CCommandProcessorFragment_OpenGL::Cmd_Init(const SCommand_Init *pCommand)
592{
593 if(!InitOpenGL(pCommand))
594 return false;
595
596 m_pTextureMemoryUsage = pCommand->m_pTextureMemoryUsage;
597 m_pTextureMemoryUsage->store(i: 0, m: std::memory_order_relaxed);
598 m_MaxTexSize = -1;
599
600 m_OpenGLTextureLodBIAS = 0;
601
602 m_Has2DArrayTextures = pCommand->m_pCapabilities->m_2DArrayTextures;
603 if(pCommand->m_pCapabilities->m_2DArrayTexturesAsExtension)
604 {
605 m_Has2DArrayTexturesAsExtension = true;
606 m_2DArrayTarget = GL_TEXTURE_2D_ARRAY_EXT;
607 }
608 else
609 {
610 m_Has2DArrayTexturesAsExtension = false;
611 m_2DArrayTarget = GL_TEXTURE_2D_ARRAY;
612 }
613
614 m_Has3DTextures = pCommand->m_pCapabilities->m_3DTextures;
615 m_HasMipMaps = pCommand->m_pCapabilities->m_MipMapping;
616 m_HasNPOTTextures = pCommand->m_pCapabilities->m_NPOTTextures;
617
618 m_LastBlendMode = EBlendMode::ALPHA;
619 m_LastClipEnable = false;
620
621 return true;
622}
623
624void CCommandProcessorFragment_OpenGL::TextureUpdate(int Slot, int X, int Y, int Width, int Height, int GLFormat, uint8_t *pTexData)
625{
626 glBindTexture(GL_TEXTURE_2D, texture: m_vTextures[Slot].m_Tex);
627
628 if(!m_HasNPOTTextures)
629 {
630 float ResizeW = m_vTextures[Slot].m_ResizeWidth;
631 float ResizeH = m_vTextures[Slot].m_ResizeHeight;
632 if(ResizeW > 0 && ResizeH > 0)
633 {
634 int ResizedW = (int)(Width * ResizeW);
635 int ResizedH = (int)(Height * ResizeH);
636
637 uint8_t *pTmpData = ResizeImage(pImageData: pTexData, Width, Height, NewWidth: ResizedW, NewHeight: ResizedH, BPP: GLFormatToPixelSize(GLFormat));
638 free(ptr: pTexData);
639 pTexData = pTmpData;
640
641 Width = ResizedW;
642 Height = ResizedH;
643 }
644 }
645
646 if(m_vTextures[Slot].m_RescaleCount > 0)
647 {
648 int OldWidth = Width;
649 int OldHeight = Height;
650 for(int i = 0; i < m_vTextures[Slot].m_RescaleCount; ++i)
651 {
652 Width >>= 1;
653 Height >>= 1;
654
655 X /= 2;
656 Y /= 2;
657 }
658
659 uint8_t *pTmpData = ResizeImage(pImageData: pTexData, Width: OldWidth, Height: OldHeight, NewWidth: Width, NewHeight: Height, BPP: GLFormatToPixelSize(GLFormat));
660 free(ptr: pTexData);
661 pTexData = pTmpData;
662 }
663
664 glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: X, yoffset: Y, width: Width, height: Height, format: GLFormat, GL_UNSIGNED_BYTE, pixels: pTexData);
665 free(ptr: pTexData);
666}
667
668void CCommandProcessorFragment_OpenGL::DestroyTexture(int Slot)
669{
670 m_pTextureMemoryUsage->store(i: m_pTextureMemoryUsage->load(m: std::memory_order_relaxed) - m_vTextures[Slot].m_MemSize, m: std::memory_order_relaxed);
671
672 if(m_vTextures[Slot].m_Tex != 0)
673 {
674 glDeleteTextures(n: 1, textures: &m_vTextures[Slot].m_Tex);
675 }
676
677 if(m_vTextures[Slot].m_Tex2DArray != 0)
678 {
679 glDeleteTextures(n: 1, textures: &m_vTextures[Slot].m_Tex2DArray);
680 }
681
682 if(IsNewApi())
683 {
684 if(m_vTextures[Slot].m_Sampler != 0)
685 {
686 glDeleteSamplers(1, &m_vTextures[Slot].m_Sampler);
687 }
688 if(m_vTextures[Slot].m_Sampler2DArray != 0)
689 {
690 glDeleteSamplers(1, &m_vTextures[Slot].m_Sampler2DArray);
691 }
692 }
693
694 m_vTextures[Slot].m_Tex = 0;
695 m_vTextures[Slot].m_Sampler = 0;
696 m_vTextures[Slot].m_Tex2DArray = 0;
697 m_vTextures[Slot].m_Sampler2DArray = 0;
698 m_vTextures[Slot].m_LastWrapMode = EWrapMode::REPEAT;
699}
700
701void CCommandProcessorFragment_OpenGL::Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand)
702{
703 DestroyTexture(Slot: pCommand->m_Slot);
704}
705
706void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int Height, int GLFormat, int GLStoreFormat, int Flags, uint8_t *pTexData)
707{
708#ifndef BACKEND_GL_MODERN_API
709
710 if(m_MaxTexSize == -1)
711 {
712 // fix the alignment to allow even 1byte changes, e.g. for alpha components
713 glPixelStorei(GL_UNPACK_ALIGNMENT, param: 1);
714 glGetIntegerv(GL_MAX_TEXTURE_SIZE, params: &m_MaxTexSize);
715 }
716
717 while(Slot >= (int)m_vTextures.size())
718 m_vTextures.resize(sz: m_vTextures.size() * 2);
719
720 m_vTextures[Slot].m_ResizeWidth = -1.f;
721 m_vTextures[Slot].m_ResizeHeight = -1.f;
722
723 if(!m_HasNPOTTextures)
724 {
725 int PowerOfTwoWidth = HighestBit(OfVar: Width);
726 int PowerOfTwoHeight = HighestBit(OfVar: Height);
727 if(Width != PowerOfTwoWidth || Height != PowerOfTwoHeight)
728 {
729 uint8_t *pTmpData = ResizeImage(pImageData: pTexData, Width, Height, NewWidth: PowerOfTwoWidth, NewHeight: PowerOfTwoHeight, BPP: GLFormatToPixelSize(GLFormat));
730 free(ptr: pTexData);
731 pTexData = pTmpData;
732
733 m_vTextures[Slot].m_ResizeWidth = (float)PowerOfTwoWidth / (float)Width;
734 m_vTextures[Slot].m_ResizeHeight = (float)PowerOfTwoHeight / (float)Height;
735
736 Width = PowerOfTwoWidth;
737 Height = PowerOfTwoHeight;
738 }
739 }
740
741 int RescaleCount = 0;
742 if(GLFormat == GL_RGBA)
743 {
744 int OldWidth = Width;
745 int OldHeight = Height;
746 bool NeedsResize = false;
747
748 if(Width > m_MaxTexSize || Height > m_MaxTexSize)
749 {
750 do
751 {
752 Width >>= 1;
753 Height >>= 1;
754 ++RescaleCount;
755 } while(Width > m_MaxTexSize || Height > m_MaxTexSize);
756 NeedsResize = true;
757 }
758
759 if(NeedsResize)
760 {
761 uint8_t *pTmpData = ResizeImage(pImageData: pTexData, Width: OldWidth, Height: OldHeight, NewWidth: Width, NewHeight: Height, BPP: GLFormatToPixelSize(GLFormat));
762 free(ptr: pTexData);
763 pTexData = pTmpData;
764 }
765 }
766 m_vTextures[Slot].m_Width = Width;
767 m_vTextures[Slot].m_Height = Height;
768 m_vTextures[Slot].m_RescaleCount = RescaleCount;
769
770 const size_t PixelSize = GLFormatToPixelSize(GLFormat);
771
772 if((Flags & TextureFlag::NO_2D_TEXTURE) == 0)
773 {
774 glGenTextures(n: 1, textures: &m_vTextures[Slot].m_Tex);
775 glBindTexture(GL_TEXTURE_2D, texture: m_vTextures[Slot].m_Tex);
776 }
777
778 if(Flags & TextureFlag::NO_MIPMAPS || !m_HasMipMaps)
779 {
780 if((Flags & TextureFlag::NO_2D_TEXTURE) == 0)
781 {
782 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
783 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
784 glTexImage2D(GL_TEXTURE_2D, level: 0, internalformat: GLStoreFormat, width: Width, height: Height, border: 0, format: GLFormat, GL_UNSIGNED_BYTE, pixels: pTexData);
785 }
786 }
787 else
788 {
789 if((Flags & TextureFlag::NO_2D_TEXTURE) == 0)
790 {
791 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
792 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
793 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
794
795#ifndef BACKEND_AS_OPENGL_ES
796 if(m_OpenGLTextureLodBIAS != 0 && !m_IsOpenGLES)
797 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, param: ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f));
798#endif
799
800 glTexImage2D(GL_TEXTURE_2D, level: 0, internalformat: GLStoreFormat, width: Width, height: Height, border: 0, format: GLFormat, GL_UNSIGNED_BYTE, pixels: pTexData);
801 }
802
803 int Flag2DArrayTexture = TextureFlag::TO_2D_ARRAY_TEXTURE;
804 int Flag3DTexture = TextureFlag::TO_3D_TEXTURE;
805 if((Flags & (Flag2DArrayTexture | Flag3DTexture)) != 0)
806 {
807 bool Is3DTexture = (Flags & Flag3DTexture) != 0;
808
809 glGenTextures(n: 1, textures: &m_vTextures[Slot].m_Tex2DArray);
810
811 GLenum Target = GL_TEXTURE_3D;
812
813 if(Is3DTexture)
814 {
815 Target = GL_TEXTURE_3D;
816 }
817 else
818 {
819 Target = m_2DArrayTarget;
820 }
821
822 glBindTexture(target: Target, texture: m_vTextures[Slot].m_Tex2DArray);
823
824 if(IsNewApi())
825 {
826 glGenSamplers(1, &m_vTextures[Slot].m_Sampler2DArray);
827 glBindSampler(0, m_vTextures[Slot].m_Sampler2DArray);
828 }
829
830 glTexParameteri(target: Target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
831 if(Is3DTexture)
832 {
833 glTexParameteri(target: Target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
834 if(IsNewApi())
835 glSamplerParameteri(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
836 }
837 else
838 {
839 glTexParameteri(target: Target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
840 glTexParameteri(target: Target, GL_GENERATE_MIPMAP, GL_TRUE);
841 if(IsNewApi())
842 glSamplerParameteri(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
843 }
844
845 glTexParameteri(target: Target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
846 glTexParameteri(target: Target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
847 glTexParameteri(target: Target, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
848
849#ifndef BACKEND_AS_OPENGL_ES
850 if(m_OpenGLTextureLodBIAS != 0 && !m_IsOpenGLES)
851 glTexParameterf(target: Target, GL_TEXTURE_LOD_BIAS, param: ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f));
852#endif
853
854 if(IsNewApi())
855 {
856 glSamplerParameteri(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
857 glSamplerParameteri(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
858 glSamplerParameteri(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
859
860#ifndef BACKEND_AS_OPENGL_ES
861 if(m_OpenGLTextureLodBIAS != 0 && !m_IsOpenGLES)
862 glSamplerParameterf(m_vTextures[Slot].m_Sampler2DArray, GL_TEXTURE_LOD_BIAS, ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f));
863#endif
864
865 glBindSampler(0, 0);
866 }
867
868 int ConvertWidth = Width;
869 int ConvertHeight = Height;
870
871 if(ConvertWidth == 0 || (ConvertWidth % 16) != 0 || ConvertHeight == 0 || (ConvertHeight % 16) != 0)
872 {
873 int NewWidth = maximum<int>(a: HighestBit(OfVar: ConvertWidth), b: 16);
874 int NewHeight = maximum<int>(a: HighestBit(OfVar: ConvertHeight), b: 16);
875 uint8_t *pNewTexData = ResizeImage(pImageData: pTexData, Width: ConvertWidth, Height: ConvertHeight, NewWidth, NewHeight, BPP: GLFormatToPixelSize(GLFormat));
876 log_debug("gfx/opengl", "3D/2D array texture was resized. Slot=%d Size=(%d, %d) Resized=(%d, %d)", Slot, ConvertWidth, ConvertHeight, NewWidth, NewHeight);
877
878 ConvertWidth = NewWidth;
879 ConvertHeight = NewHeight;
880
881 free(ptr: pTexData);
882 pTexData = pNewTexData;
883 }
884
885 int Image3DWidth, Image3DHeight;
886 uint8_t *pImageData3D = static_cast<uint8_t *>(malloc(size: (size_t)PixelSize * ConvertWidth * ConvertHeight));
887 Texture2DTo3D(pImageBuffer: pTexData, ImageWidth: ConvertWidth, ImageHeight: ConvertHeight, PixelSize, SplitCountWidth: 16, SplitCountHeight: 16, pTarget3DImageData: pImageData3D, Target3DImageWidth&: Image3DWidth, Target3DImageHeight&: Image3DHeight);
888 glTexImage3D(Target, 0, GLStoreFormat, Image3DWidth, Image3DHeight, 256, 0, GLFormat, GL_UNSIGNED_BYTE, pImageData3D);
889 free(ptr: pImageData3D);
890 }
891 }
892
893 // This is the initial value for the wrap modes
894 m_vTextures[Slot].m_LastWrapMode = EWrapMode::REPEAT;
895
896 // calculate memory usage
897 m_vTextures[Slot].m_MemSize = (size_t)Width * Height * PixelSize;
898 while(Width > 2 && Height > 2)
899 {
900 Width >>= 1;
901 Height >>= 1;
902 m_vTextures[Slot].m_MemSize += (size_t)Width * Height * PixelSize;
903 }
904 m_pTextureMemoryUsage->store(i: m_pTextureMemoryUsage->load(m: std::memory_order_relaxed) + m_vTextures[Slot].m_MemSize, m: std::memory_order_relaxed);
905
906 free(ptr: pTexData);
907#endif
908}
909
910void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand)
911{
912 TextureCreate(Slot: pCommand->m_Slot, Width: pCommand->m_Width, Height: pCommand->m_Height, GL_RGBA, GL_RGBA, Flags: pCommand->m_Flags, pTexData: pCommand->m_pData);
913}
914
915void CCommandProcessorFragment_OpenGL::Cmd_TextTexture_Update(const CCommandBuffer::SCommand_TextTexture_Update *pCommand)
916{
917 TextureUpdate(Slot: pCommand->m_Slot, X: pCommand->m_X, Y: pCommand->m_Y, Width: pCommand->m_Width, Height: pCommand->m_Height, GL_ALPHA, pTexData: pCommand->m_pData);
918}
919
920void CCommandProcessorFragment_OpenGL::Cmd_TextTextures_Destroy(const CCommandBuffer::SCommand_TextTextures_Destroy *pCommand)
921{
922 DestroyTexture(Slot: pCommand->m_Slot);
923 DestroyTexture(Slot: pCommand->m_SlotOutline);
924}
925
926void CCommandProcessorFragment_OpenGL::Cmd_TextTextures_Create(const CCommandBuffer::SCommand_TextTextures_Create *pCommand)
927{
928 TextureCreate(Slot: pCommand->m_Slot, Width: pCommand->m_Width, Height: pCommand->m_Height, GL_ALPHA, GL_ALPHA, Flags: TextureFlag::NO_MIPMAPS, pTexData: pCommand->m_pTextData);
929 TextureCreate(Slot: pCommand->m_SlotOutline, Width: pCommand->m_Width, Height: pCommand->m_Height, GL_ALPHA, GL_ALPHA, Flags: TextureFlag::NO_MIPMAPS, pTexData: pCommand->m_pTextOutlineData);
930}
931
932void CCommandProcessorFragment_OpenGL::Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand)
933{
934 // if clip is still active, force disable it for clearing, enable it again afterwards
935 bool ClipWasEnabled = m_LastClipEnable;
936 if(ClipWasEnabled)
937 {
938 glDisable(GL_SCISSOR_TEST);
939 }
940 glClearColor(red: pCommand->m_Color.r, green: pCommand->m_Color.g, blue: pCommand->m_Color.b, alpha: 0.0f);
941 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
942 if(ClipWasEnabled)
943 {
944 glEnable(GL_SCISSOR_TEST);
945 }
946}
947
948void CCommandProcessorFragment_OpenGL::Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand)
949{
950#ifndef BACKEND_GL_MODERN_API
951 SetState(State: pCommand->m_State);
952
953 glVertexPointer(size: 2, GL_FLOAT, stride: sizeof(CCommandBuffer::SVertex), pointer: (char *)pCommand->m_pVertices);
954 glTexCoordPointer(size: 2, GL_FLOAT, stride: sizeof(CCommandBuffer::SVertex), pointer: (char *)pCommand->m_pVertices + sizeof(float) * 2);
955 glColorPointer(size: 4, GL_UNSIGNED_BYTE, stride: sizeof(CCommandBuffer::SVertex), pointer: (char *)pCommand->m_pVertices + sizeof(float) * 4);
956 glEnableClientState(GL_VERTEX_ARRAY);
957 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
958 glEnableClientState(GL_COLOR_ARRAY);
959
960 switch(pCommand->m_PrimType)
961 {
962 case EPrimitiveType::QUADS:
963#ifndef BACKEND_AS_OPENGL_ES
964 glDrawArrays(GL_QUADS, first: 0, count: pCommand->m_PrimCount * 4);
965#endif
966 break;
967 case EPrimitiveType::LINES:
968 glDrawArrays(GL_LINES, first: 0, count: pCommand->m_PrimCount * 2);
969 break;
970 case EPrimitiveType::TRIANGLES:
971 glDrawArrays(GL_TRIANGLES, first: 0, count: pCommand->m_PrimCount * 3);
972 break;
973 default:
974 dbg_assert_failed("Invalid primitive type: %d", (int)pCommand->m_PrimType);
975 };
976#endif
977}
978
979void CCommandProcessorFragment_OpenGL::Cmd_ReadPixel(const CCommandBuffer::SCommand_TrySwapAndReadPixel *pCommand)
980{
981 // get size of viewport
982 GLint aViewport[4] = {0, 0, 0, 0};
983 glGetIntegerv(GL_VIEWPORT, params: aViewport);
984 const int h = aViewport[3];
985
986 // fetch the pixel
987 uint8_t aPixelData[3];
988 GLint Alignment;
989 glGetIntegerv(GL_PACK_ALIGNMENT, params: &Alignment);
990 glPixelStorei(GL_PACK_ALIGNMENT, param: 1);
991 glReadPixels(x: pCommand->m_Position.x, y: h - 1 - pCommand->m_Position.y, width: 1, height: 1, GL_RGB, GL_UNSIGNED_BYTE, pixels: aPixelData);
992 glPixelStorei(GL_PACK_ALIGNMENT, param: Alignment);
993
994 // fill in the information
995 *pCommand->m_pColor = ColorRGBA(aPixelData[0] / 255.0f, aPixelData[1] / 255.0f, aPixelData[2] / 255.0f, 1.0f);
996}
997
998void CCommandProcessorFragment_OpenGL::Cmd_Screenshot(const CCommandBuffer::SCommand_TrySwapAndScreenshot *pCommand)
999{
1000 // fetch image data
1001 GLint aViewport[4] = {0, 0, 0, 0};
1002 glGetIntegerv(GL_VIEWPORT, params: aViewport);
1003
1004 int w = aViewport[2];
1005 int h = aViewport[3];
1006
1007 pCommand->m_pImage->m_Width = w;
1008 pCommand->m_pImage->m_Height = h;
1009 pCommand->m_pImage->m_Format = CImageInfo::FORMAT_RGBA;
1010 pCommand->m_pImage->Allocate();
1011
1012 uint8_t *pPixelData = pCommand->m_pImage->m_pData;
1013
1014 // we create a tmp row to use when we are flipping the texture
1015 std::vector<uint8_t> vTmpRow(w * 4);
1016 uint8_t *pTempRow = vTmpRow.data();
1017
1018 // fetch the pixels
1019 GLint Alignment;
1020 glGetIntegerv(GL_PACK_ALIGNMENT, params: &Alignment);
1021 glPixelStorei(GL_PACK_ALIGNMENT, param: 1);
1022 glReadPixels(x: 0, y: 0, width: w, height: h, GL_RGBA, GL_UNSIGNED_BYTE, pixels: pPixelData);
1023 glPixelStorei(GL_PACK_ALIGNMENT, param: Alignment);
1024
1025 // flip the pixel because opengl works from bottom left corner
1026 for(int y = 0; y < h / 2; y++)
1027 {
1028 mem_copy(dest: pTempRow, source: pPixelData + y * w * 4, size: w * 4);
1029 mem_copy(dest: pPixelData + y * w * 4, source: pPixelData + (h - y - 1) * w * 4, size: w * 4);
1030 mem_copy(dest: pPixelData + (h - y - 1) * w * 4, source: pTempRow, size: w * 4);
1031 for(int x = 0; x < w; x++)
1032 {
1033 pPixelData[y * w * 4 + x * 4 + 3] = 255;
1034 pPixelData[(h - y - 1) * w * 4 + x * 4 + 3] = 255;
1035 }
1036 }
1037}
1038
1039CCommandProcessorFragment_OpenGL::CCommandProcessorFragment_OpenGL()
1040{
1041 m_vTextures.resize(sz: CCommandBuffer::MAX_TEXTURES);
1042 m_HasShaders = false;
1043}
1044
1045ERunCommandReturnTypes CCommandProcessorFragment_OpenGL::RunCommand(const CCommandBuffer::SCommand *pBaseCommand)
1046{
1047 switch(pBaseCommand->m_Cmd)
1048 {
1049 case CCommandProcessorFragment_OpenGL::CMD_INIT:
1050 Cmd_Init(pCommand: static_cast<const SCommand_Init *>(pBaseCommand));
1051 break;
1052 case CCommandProcessorFragment_OpenGL::CMD_SHUTDOWN:
1053 Cmd_Shutdown(pCommand: static_cast<const SCommand_Shutdown *>(pBaseCommand));
1054 break;
1055 case CCommandBuffer::CMD_TEXTURE_CREATE:
1056 Cmd_Texture_Create(pCommand: static_cast<const CCommandBuffer::SCommand_Texture_Create *>(pBaseCommand));
1057 break;
1058 case CCommandBuffer::CMD_TEXTURE_DESTROY:
1059 Cmd_Texture_Destroy(pCommand: static_cast<const CCommandBuffer::SCommand_Texture_Destroy *>(pBaseCommand));
1060 break;
1061 case CCommandBuffer::CMD_TEXT_TEXTURES_CREATE:
1062 Cmd_TextTextures_Create(pCommand: static_cast<const CCommandBuffer::SCommand_TextTextures_Create *>(pBaseCommand));
1063 break;
1064 case CCommandBuffer::CMD_TEXT_TEXTURES_DESTROY:
1065 Cmd_TextTextures_Destroy(pCommand: static_cast<const CCommandBuffer::SCommand_TextTextures_Destroy *>(pBaseCommand));
1066 break;
1067 case CCommandBuffer::CMD_TEXT_TEXTURE_UPDATE:
1068 Cmd_TextTexture_Update(pCommand: static_cast<const CCommandBuffer::SCommand_TextTexture_Update *>(pBaseCommand));
1069 break;
1070 case CCommandBuffer::CMD_CLEAR:
1071 Cmd_Clear(pCommand: static_cast<const CCommandBuffer::SCommand_Clear *>(pBaseCommand));
1072 break;
1073 case CCommandBuffer::CMD_RENDER:
1074 Cmd_Render(pCommand: static_cast<const CCommandBuffer::SCommand_Render *>(pBaseCommand));
1075 break;
1076 case CCommandBuffer::CMD_RENDER_TEX3D:
1077 Cmd_RenderTex3D(pCommand: static_cast<const CCommandBuffer::SCommand_RenderTex3D *>(pBaseCommand));
1078 break;
1079 case CCommandBuffer::CMD_TRY_SWAP_AND_READ_PIXEL:
1080 Cmd_ReadPixel(pCommand: static_cast<const CCommandBuffer::SCommand_TrySwapAndReadPixel *>(pBaseCommand));
1081 break;
1082 case CCommandBuffer::CMD_TRY_SWAP_AND_SCREENSHOT:
1083 Cmd_Screenshot(pCommand: static_cast<const CCommandBuffer::SCommand_TrySwapAndScreenshot *>(pBaseCommand));
1084 break;
1085 case CCommandBuffer::CMD_UPDATE_VIEWPORT:
1086 Cmd_Update_Viewport(pCommand: static_cast<const CCommandBuffer::SCommand_Update_Viewport *>(pBaseCommand));
1087 break;
1088
1089 case CCommandBuffer::CMD_CREATE_BUFFER_OBJECT: Cmd_CreateBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_CreateBufferObject *>(pBaseCommand)); break;
1090 case CCommandBuffer::CMD_UPDATE_BUFFER_OBJECT: Cmd_UpdateBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_UpdateBufferObject *>(pBaseCommand)); break;
1091 case CCommandBuffer::CMD_RECREATE_BUFFER_OBJECT: Cmd_RecreateBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_RecreateBufferObject *>(pBaseCommand)); break;
1092 case CCommandBuffer::CMD_COPY_BUFFER_OBJECT: Cmd_CopyBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_CopyBufferObject *>(pBaseCommand)); break;
1093 case CCommandBuffer::CMD_DELETE_BUFFER_OBJECT: Cmd_DeleteBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_DeleteBufferObject *>(pBaseCommand)); break;
1094
1095 case CCommandBuffer::CMD_CREATE_BUFFER_CONTAINER: Cmd_CreateBufferContainer(pCommand: static_cast<const CCommandBuffer::SCommand_CreateBufferContainer *>(pBaseCommand)); break;
1096 case CCommandBuffer::CMD_UPDATE_BUFFER_CONTAINER: Cmd_UpdateBufferContainer(pCommand: static_cast<const CCommandBuffer::SCommand_UpdateBufferContainer *>(pBaseCommand)); break;
1097 case CCommandBuffer::CMD_DELETE_BUFFER_CONTAINER: Cmd_DeleteBufferContainer(pCommand: static_cast<const CCommandBuffer::SCommand_DeleteBufferContainer *>(pBaseCommand)); break;
1098 case CCommandBuffer::CMD_INDICES_REQUIRED_NUM_NOTIFY: Cmd_IndicesRequiredNumNotify(pCommand: static_cast<const CCommandBuffer::SCommand_IndicesRequiredNumNotify *>(pBaseCommand)); break;
1099
1100 case CCommandBuffer::CMD_RENDER_TILE_LAYER: Cmd_RenderTileLayer(pCommand: static_cast<const CCommandBuffer::SCommand_RenderTileLayer *>(pBaseCommand)); break;
1101 case CCommandBuffer::CMD_RENDER_BORDER_TILE: Cmd_RenderBorderTile(pCommand: static_cast<const CCommandBuffer::SCommand_RenderBorderTile *>(pBaseCommand)); break;
1102 case CCommandBuffer::CMD_RENDER_QUAD_LAYER: Cmd_RenderQuadLayer(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadLayer *>(pBaseCommand), Grouped: false); break;
1103 case CCommandBuffer::CMD_RENDER_QUAD_LAYER_GROUPED: Cmd_RenderQuadLayer(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadLayer *>(pBaseCommand), Grouped: true); break;
1104 case CCommandBuffer::CMD_RENDER_TEXT: Cmd_RenderText(pCommand: static_cast<const CCommandBuffer::SCommand_RenderText *>(pBaseCommand)); break;
1105 case CCommandBuffer::CMD_RENDER_QUAD_CONTAINER: Cmd_RenderQuadContainer(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainer *>(pBaseCommand)); break;
1106 case CCommandBuffer::CMD_RENDER_QUAD_CONTAINER_EX: Cmd_RenderQuadContainerEx(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainerEx *>(pBaseCommand)); break;
1107 case CCommandBuffer::CMD_RENDER_QUAD_CONTAINER_SPRITE_MULTIPLE: Cmd_RenderQuadContainerAsSpriteMultiple(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple *>(pBaseCommand)); break;
1108 default: return ERunCommandReturnTypes::RUN_COMMAND_COMMAND_UNHANDLED;
1109 }
1110
1111 return ERunCommandReturnTypes::RUN_COMMAND_COMMAND_HANDLED;
1112}
1113
1114// ------------ CCommandProcessorFragment_OpenGL2
1115
1116void CCommandProcessorFragment_OpenGL2::UseProgram(CGLSLTWProgram *pProgram)
1117{
1118 pProgram->UseProgram();
1119}
1120
1121void CCommandProcessorFragment_OpenGL2::SetState(const CCommandBuffer::SState &State, CGLSLTWProgram *pProgram, bool Use2DArrayTextures)
1122{
1123 if(m_LastBlendMode == EBlendMode::NONE)
1124 {
1125 m_LastBlendMode = EBlendMode::ALPHA;
1126 glEnable(GL_BLEND);
1127 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1128 }
1129 if(State.m_BlendMode != m_LastBlendMode && State.m_BlendMode != EBlendMode::NONE)
1130 {
1131 // blend
1132 switch(State.m_BlendMode)
1133 {
1134 case EBlendMode::NONE:
1135 // We don't really need this anymore
1136 // glDisable(GL_BLEND);
1137 break;
1138 case EBlendMode::ALPHA:
1139 // glEnable(GL_BLEND);
1140 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1141 break;
1142 case EBlendMode::ADDITIVE:
1143 // glEnable(GL_BLEND);
1144 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1145 break;
1146 default:
1147 dbg_assert_failed("Invalid blend mode: %d", (int)State.m_BlendMode);
1148 };
1149
1150 m_LastBlendMode = State.m_BlendMode;
1151 }
1152
1153 // clip
1154 if(State.m_ClipEnable)
1155 {
1156 glScissor(x: State.m_ClipX, y: State.m_ClipY, width: State.m_ClipW, height: State.m_ClipH);
1157 glEnable(GL_SCISSOR_TEST);
1158 m_LastClipEnable = true;
1159 }
1160 else if(m_LastClipEnable)
1161 {
1162 // Don't disable it always
1163 glDisable(GL_SCISSOR_TEST);
1164 m_LastClipEnable = false;
1165 }
1166
1167 if(!IsNewApi())
1168 {
1169 glDisable(GL_TEXTURE_2D);
1170 if(!m_HasShaders)
1171 {
1172 if(m_Has3DTextures)
1173 glDisable(GL_TEXTURE_3D);
1174 if(m_Has2DArrayTextures)
1175 {
1176 glDisable(cap: m_2DArrayTarget);
1177 }
1178 }
1179 }
1180
1181 // texture
1182 if(IsTexturedState(State))
1183 {
1184 int Slot = 0;
1185 if(!Use2DArrayTextures)
1186 {
1187 if(!IsNewApi() && !m_HasShaders)
1188 glEnable(GL_TEXTURE_2D);
1189 glBindTexture(GL_TEXTURE_2D, texture: m_vTextures[State.m_Texture].m_Tex);
1190 if(IsNewApi())
1191 glBindSampler(Slot, m_vTextures[State.m_Texture].m_Sampler);
1192 }
1193 else
1194 {
1195 if(!m_Has2DArrayTextures)
1196 {
1197 if(!IsNewApi() && !m_HasShaders)
1198 glEnable(GL_TEXTURE_3D);
1199 glBindTexture(GL_TEXTURE_3D, texture: m_vTextures[State.m_Texture].m_Tex2DArray);
1200 if(IsNewApi())
1201 glBindSampler(Slot, m_vTextures[State.m_Texture].m_Sampler2DArray);
1202 }
1203 else
1204 {
1205 if(!IsNewApi() && !m_HasShaders)
1206 glEnable(cap: m_2DArrayTarget);
1207 glBindTexture(target: m_2DArrayTarget, texture: m_vTextures[State.m_Texture].m_Tex2DArray);
1208 if(IsNewApi())
1209 glBindSampler(Slot, m_vTextures[State.m_Texture].m_Sampler2DArray);
1210 }
1211 }
1212
1213 if(pProgram->m_LastTextureSampler != Slot)
1214 {
1215 pProgram->SetUniform(Loc: pProgram->m_LocTextureSampler, Value: Slot);
1216 pProgram->m_LastTextureSampler = Slot;
1217 }
1218
1219 if(m_vTextures[State.m_Texture].m_LastWrapMode != State.m_WrapMode && !Use2DArrayTextures)
1220 {
1221 switch(State.m_WrapMode)
1222 {
1223 case EWrapMode::REPEAT:
1224 if(IsNewApi())
1225 {
1226 glSamplerParameteri(m_vTextures[State.m_Texture].m_Sampler, GL_TEXTURE_WRAP_S, GL_REPEAT);
1227 glSamplerParameteri(m_vTextures[State.m_Texture].m_Sampler, GL_TEXTURE_WRAP_T, GL_REPEAT);
1228 }
1229 break;
1230 case EWrapMode::CLAMP:
1231 if(IsNewApi())
1232 {
1233 glSamplerParameteri(m_vTextures[State.m_Texture].m_Sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1234 glSamplerParameteri(m_vTextures[State.m_Texture].m_Sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1235 }
1236 break;
1237 default:
1238 dbg_assert_failed("Invalid wrap mode: %d", (int)State.m_WrapMode);
1239 };
1240 m_vTextures[State.m_Texture].m_LastWrapMode = State.m_WrapMode;
1241 }
1242 }
1243
1244 if(pProgram->m_LastScreenTL != State.m_ScreenTL || pProgram->m_LastScreenBR != State.m_ScreenBR)
1245 {
1246 pProgram->m_LastScreenTL = State.m_ScreenTL;
1247 pProgram->m_LastScreenBR = State.m_ScreenBR;
1248
1249 // screen mapping
1250 // orthographic projection matrix
1251 // the z coordinate is the same for every vertex, so just ignore the z coordinate and set it in the shaders
1252 float m[2 * 4] = {
1253 2.f / (State.m_ScreenBR.x - State.m_ScreenTL.x),
1254 0,
1255 0,
1256 -((State.m_ScreenBR.x + State.m_ScreenTL.x) / (State.m_ScreenBR.x - State.m_ScreenTL.x)),
1257 0,
1258 (2.f / (State.m_ScreenTL.y - State.m_ScreenBR.y)),
1259 0,
1260 -((State.m_ScreenTL.y + State.m_ScreenBR.y) / (State.m_ScreenTL.y - State.m_ScreenBR.y)),
1261 };
1262
1263 // transpose bcs of column-major order of opengl
1264 glUniformMatrix4x2fv(pProgram->m_LocPos, 1, true, (float *)&m);
1265 }
1266}
1267
1268#ifndef BACKEND_GL_MODERN_API
1269bool CCommandProcessorFragment_OpenGL2::DoAnalyzeStep(size_t CheckCount, size_t VerticesCount, uint8_t aFakeTexture[], size_t SingleImageSize)
1270{
1271 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1272
1273 int Slot = 0;
1274 if(m_HasShaders)
1275 {
1276 CGLSLTWProgram *pProgram = m_pPrimitive3DProgramTextured;
1277 UseProgram(pProgram);
1278
1279 pProgram->SetUniform(Loc: pProgram->m_LocTextureSampler, Value: Slot);
1280
1281 float m[2 * 4] = {
1282 1, 0, 0, 0,
1283 0, 1, 0, 0};
1284
1285 // transpose bcs of column-major order of opengl
1286 glUniformMatrix4x2fv(pProgram->m_LocPos, 1, true, (float *)&m);
1287 }
1288 else
1289 {
1290 glMatrixMode(GL_PROJECTION);
1291 glLoadIdentity();
1292 glOrtho(left: -1, right: 1, bottom: -1, top: 1, zNear: -10.0f, zFar: 10.f);
1293 }
1294
1295 glEnableClientState(GL_VERTEX_ARRAY);
1296 glEnableClientState(GL_COLOR_ARRAY);
1297 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1298
1299 glVertexPointer(size: 2, GL_FLOAT, stride: sizeof(m_aStreamVertices[0]), pointer: m_aStreamVertices);
1300 glColorPointer(size: 4, GL_FLOAT, stride: sizeof(m_aStreamVertices[0]), pointer: (uint8_t *)m_aStreamVertices + (ptrdiff_t)(sizeof(vec2)));
1301 glTexCoordPointer(size: 3, GL_FLOAT, stride: sizeof(m_aStreamVertices[0]), pointer: (uint8_t *)m_aStreamVertices + (ptrdiff_t)(sizeof(vec2) + sizeof(vec4)));
1302
1303 glDrawArrays(GL_QUADS, first: 0, count: VerticesCount);
1304
1305 glDisableClientState(GL_VERTEX_ARRAY);
1306 glDisableClientState(GL_COLOR_ARRAY);
1307 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1308
1309 if(m_HasShaders)
1310 {
1311 glUseProgram(0);
1312 }
1313
1314 glFinish();
1315
1316 GLint aViewport[4] = {0, 0, 0, 0};
1317 glGetIntegerv(GL_VIEWPORT, params: aViewport);
1318
1319 int w = aViewport[2];
1320 int h = aViewport[3];
1321
1322 size_t PixelDataSize = (size_t)w * h * 3;
1323 if(PixelDataSize == 0)
1324 return false;
1325 uint8_t *pPixelData = (uint8_t *)malloc(size: PixelDataSize);
1326
1327 // fetch the pixels
1328 GLint Alignment;
1329 glGetIntegerv(GL_PACK_ALIGNMENT, params: &Alignment);
1330 glPixelStorei(GL_PACK_ALIGNMENT, param: 1);
1331 glReadPixels(x: 0, y: 0, width: w, height: h, GL_RGB, GL_UNSIGNED_BYTE, pixels: pPixelData);
1332 glPixelStorei(GL_PACK_ALIGNMENT, param: Alignment);
1333
1334 // now analyse the image data
1335 bool CheckFailed = false;
1336 int WidthTile = w / 16;
1337 int HeightTile = h / 16;
1338 int StartX = WidthTile / 2;
1339 int StartY = HeightTile / 2;
1340 for(size_t d = 0; d < CheckCount; ++d)
1341 {
1342 int CurX = (int)d % 16;
1343 int CurY = (int)d / 16;
1344
1345 int CheckX = StartX + CurX * WidthTile;
1346 int CheckY = StartY + CurY * HeightTile;
1347
1348 ptrdiff_t OffsetPixelData = (CheckY * (w * 3)) + (CheckX * 3);
1349 ptrdiff_t OffsetFakeTexture = SingleImageSize * d;
1350 OffsetPixelData = std::clamp<ptrdiff_t>(val: OffsetPixelData, lo: 0, hi: (ptrdiff_t)PixelDataSize);
1351 OffsetFakeTexture = std::clamp<ptrdiff_t>(val: OffsetFakeTexture, lo: 0, hi: (ptrdiff_t)(SingleImageSize * CheckCount));
1352 uint8_t *pPixel = pPixelData + OffsetPixelData;
1353 uint8_t *pPixelTex = aFakeTexture + OffsetFakeTexture;
1354 for(size_t i = 0; i < 3; ++i)
1355 {
1356 if((pPixel[i] < pPixelTex[i] - 25) || (pPixel[i] > pPixelTex[i] + 25))
1357 {
1358 CheckFailed = true;
1359 break;
1360 }
1361 }
1362 }
1363
1364 free(ptr: pPixelData);
1365 return !CheckFailed;
1366}
1367
1368bool CCommandProcessorFragment_OpenGL2::IsTileMapAnalysisSucceeded()
1369{
1370 glClearColor(red: 0, green: 0, blue: 0, alpha: 1);
1371
1372 // create fake texture 1024x1024
1373 const size_t ImageWidth = 1024;
1374 const size_t ImageHeight = 1024;
1375 uint8_t *pFakeTexture = (uint8_t *)malloc(size: sizeof(uint8_t) * ImageWidth * ImageHeight * 4);
1376 // fill by colors stepping by 50 => (255 / 50 ~ 5) => 5 times 3(color channels) = 5 ^ 3 = 125 possibilities to check
1377 size_t CheckCount = 5 * 5 * 5;
1378 // always fill 4 pixels of the texture, so the sampling is accurate
1379 int aCurColor[4] = {25, 25, 25, 255};
1380 const size_t SingleImageWidth = 64;
1381 const size_t SingleImageHeight = 64;
1382 size_t SingleImageSize = SingleImageWidth * SingleImageHeight * 4;
1383 for(size_t d = 0; d < CheckCount; ++d)
1384 {
1385 uint8_t *pCurFakeTexture = pFakeTexture + (ptrdiff_t)(SingleImageSize * d);
1386
1387 uint8_t aCurColorUint8[SingleImageWidth * SingleImageHeight * 4];
1388 for(size_t y = 0; y < SingleImageHeight; ++y)
1389 {
1390 for(size_t x = 0; x < SingleImageWidth; ++x)
1391 {
1392 for(size_t i = 0; i < 4; ++i)
1393 {
1394 aCurColorUint8[(y * SingleImageWidth * 4) + (x * 4) + i] = (uint8_t)aCurColor[i];
1395 }
1396 }
1397 }
1398 mem_copy(dest: pCurFakeTexture, source: aCurColorUint8, size: sizeof(aCurColorUint8));
1399
1400 aCurColor[2] += 50;
1401 if(aCurColor[2] > 225)
1402 {
1403 aCurColor[2] -= 250;
1404 aCurColor[1] += 50;
1405 }
1406 if(aCurColor[1] > 225)
1407 {
1408 aCurColor[1] -= 250;
1409 aCurColor[0] += 50;
1410 }
1411 if(aCurColor[0] > 225)
1412 {
1413 break;
1414 }
1415 }
1416
1417 // upload the texture
1418 GLuint FakeTexture;
1419 glGenTextures(n: 1, textures: &FakeTexture);
1420
1421 GLenum Target = GL_TEXTURE_3D;
1422 if(m_Has2DArrayTextures)
1423 {
1424 Target = m_2DArrayTarget;
1425 }
1426
1427 glBindTexture(target: Target, texture: FakeTexture);
1428 glTexParameteri(target: Target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1429 if(!m_Has2DArrayTextures)
1430 {
1431 glTexParameteri(target: Target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1432 }
1433 else
1434 {
1435 glTexParameteri(target: Target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
1436 glTexParameteri(target: Target, GL_GENERATE_MIPMAP, GL_TRUE);
1437 }
1438
1439 glTexParameteri(target: Target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1440 glTexParameteri(target: Target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1441 glTexParameteri(target: Target, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
1442
1443 glTexImage3D(Target, 0, GL_RGBA, ImageWidth / 16, ImageHeight / 16, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, pFakeTexture);
1444
1445 glEnable(GL_BLEND);
1446 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1447 glDisable(GL_SCISSOR_TEST);
1448
1449 if(!m_HasShaders)
1450 {
1451 glDisable(GL_TEXTURE_2D);
1452 if(m_Has3DTextures)
1453 glDisable(GL_TEXTURE_3D);
1454 if(m_Has2DArrayTextures)
1455 {
1456 glDisable(cap: m_2DArrayTarget);
1457 }
1458
1459 if(!m_Has2DArrayTextures)
1460 {
1461 glEnable(GL_TEXTURE_3D);
1462 glBindTexture(GL_TEXTURE_3D, texture: FakeTexture);
1463 }
1464 else
1465 {
1466 glEnable(cap: m_2DArrayTarget);
1467 glBindTexture(target: m_2DArrayTarget, texture: FakeTexture);
1468 }
1469 }
1470
1471 static_assert(sizeof(m_aStreamVertices) / sizeof(m_aStreamVertices[0]) >= 256 * 4, "Keep the number of stream vertices >= 256 * 4.");
1472
1473 size_t VertexCount = 0;
1474 for(size_t i = 0; i < CheckCount; ++i)
1475 {
1476 float XPos = (float)(i % 16);
1477 float YPos = (float)(i / 16);
1478
1479 GL_SVertexTex3D *pVertex = &m_aStreamVertices[VertexCount++];
1480 GL_SVertexTex3D *pVertexBefore = pVertex;
1481 pVertex->m_Pos.x = XPos / 16.f;
1482 pVertex->m_Pos.y = YPos / 16.f;
1483 pVertex->m_Color.r = 1;
1484 pVertex->m_Color.g = 1;
1485 pVertex->m_Color.b = 1;
1486 pVertex->m_Color.a = 1;
1487 pVertex->m_Tex.u = 0;
1488 pVertex->m_Tex.v = 0;
1489
1490 pVertex = &m_aStreamVertices[VertexCount++];
1491 pVertex->m_Pos.x = XPos / 16.f + 1.f / 16.f;
1492 pVertex->m_Pos.y = YPos / 16.f;
1493 pVertex->m_Color.r = 1;
1494 pVertex->m_Color.g = 1;
1495 pVertex->m_Color.b = 1;
1496 pVertex->m_Color.a = 1;
1497 pVertex->m_Tex.u = 1;
1498 pVertex->m_Tex.v = 0;
1499
1500 pVertex = &m_aStreamVertices[VertexCount++];
1501 pVertex->m_Pos.x = XPos / 16.f + 1.f / 16.f;
1502 pVertex->m_Pos.y = YPos / 16.f + 1.f / 16.f;
1503 pVertex->m_Color.r = 1;
1504 pVertex->m_Color.g = 1;
1505 pVertex->m_Color.b = 1;
1506 pVertex->m_Color.a = 1;
1507 pVertex->m_Tex.u = 1;
1508 pVertex->m_Tex.v = 1;
1509
1510 pVertex = &m_aStreamVertices[VertexCount++];
1511 pVertex->m_Pos.x = XPos / 16.f;
1512 pVertex->m_Pos.y = YPos / 16.f + 1.f / 16.f;
1513 pVertex->m_Color.r = 1;
1514 pVertex->m_Color.g = 1;
1515 pVertex->m_Color.b = 1;
1516 pVertex->m_Color.a = 1;
1517 pVertex->m_Tex.u = 0;
1518 pVertex->m_Tex.v = 1;
1519
1520 for(size_t n = 0; n < 4; ++n)
1521 {
1522 pVertexBefore[n].m_Pos.x *= 2;
1523 pVertexBefore[n].m_Pos.x -= 1;
1524 pVertexBefore[n].m_Pos.y *= 2;
1525 pVertexBefore[n].m_Pos.y -= 1;
1526 if(m_Has2DArrayTextures)
1527 {
1528 pVertexBefore[n].m_Tex.w = i;
1529 }
1530 else
1531 {
1532 pVertexBefore[n].m_Tex.w = (i + 0.5f) / 256.f;
1533 }
1534 }
1535 }
1536
1537 // everything build up, now do the analyze steps
1538 bool NoError = DoAnalyzeStep(CheckCount, VerticesCount: VertexCount, aFakeTexture: pFakeTexture, SingleImageSize);
1539
1540 glDeleteTextures(n: 1, textures: &FakeTexture);
1541 free(ptr: pFakeTexture);
1542
1543 return NoError;
1544}
1545
1546bool CCommandProcessorFragment_OpenGL2::Cmd_Init(const SCommand_Init *pCommand)
1547{
1548 if(!CCommandProcessorFragment_OpenGL::Cmd_Init(pCommand))
1549 return false;
1550
1551 m_pTileProgram = nullptr;
1552 m_pTileProgramTextured = nullptr;
1553 m_pPrimitive3DProgram = nullptr;
1554 m_pPrimitive3DProgramTextured = nullptr;
1555
1556 m_OpenGLTextureLodBIAS = g_Config.m_GfxGLTextureLODBIAS;
1557
1558 m_HasShaders = pCommand->m_pCapabilities->m_ShaderSupport;
1559
1560 bool HasAllFunc = true;
1561#ifndef BACKEND_AS_OPENGL_ES
1562 if(m_HasShaders)
1563 {
1564 HasAllFunc &= (glUniformMatrix4x2fv != NULL) && (glGenBuffers != NULL);
1565 HasAllFunc &= (glBindBuffer != NULL) && (glBufferData != NULL);
1566 HasAllFunc &= (glEnableVertexAttribArray != NULL) && (glVertexAttribPointer != NULL) && (glVertexAttribIPointer != NULL);
1567 HasAllFunc &= (glDisableVertexAttribArray != NULL) && (glDeleteBuffers != NULL);
1568 HasAllFunc &= (glUseProgram != NULL) && (glTexImage3D != NULL);
1569 HasAllFunc &= (glBindAttribLocation != NULL) && (glTexImage3D != NULL);
1570 HasAllFunc &= (glBufferSubData != NULL) && (glGetUniformLocation != NULL);
1571 HasAllFunc &= (glUniform1i != NULL) && (glUniform1f != NULL);
1572 HasAllFunc &= (glUniform1ui != NULL) && (glUniform1i != NULL);
1573 HasAllFunc &= (glUniform1fv != NULL) && (glUniform2fv != NULL);
1574 HasAllFunc &= (glUniform4fv != NULL) && (glGetAttachedShaders != NULL);
1575 HasAllFunc &= (glGetProgramInfoLog != NULL) && (glGetProgramiv != NULL);
1576 HasAllFunc &= (glLinkProgram != NULL) && (glDetachShader != NULL);
1577 HasAllFunc &= (glAttachShader != NULL) && (glDeleteProgram != NULL);
1578 HasAllFunc &= (glCreateProgram != NULL) && (glShaderSource != NULL);
1579 HasAllFunc &= (glCompileShader != NULL) && (glGetShaderiv != NULL);
1580 HasAllFunc &= (glGetShaderInfoLog != NULL) && (glDeleteShader != NULL);
1581 HasAllFunc &= (glCreateShader != NULL);
1582 }
1583#endif
1584
1585 bool AnalysisCorrect = true;
1586 if(HasAllFunc)
1587 {
1588 if(m_HasShaders)
1589 {
1590 m_pTileProgram = new CGLSLTileProgram;
1591 m_pTileProgramTextured = new CGLSLTileProgram;
1592 m_pBorderTileProgram = new CGLSLTileProgram;
1593 m_pBorderTileProgramTextured = new CGLSLTileProgram;
1594 m_pPrimitive3DProgram = new CGLSLPrimitiveProgram;
1595 m_pPrimitive3DProgramTextured = new CGLSLPrimitiveProgram;
1596
1597 CGLSLCompiler ShaderCompiler(g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch, m_IsOpenGLES, m_OpenGLTextureLodBIAS / 1000.0f);
1598 ShaderCompiler.SetHasTextureArray(pCommand->m_pCapabilities->m_2DArrayTextures);
1599
1600 if(pCommand->m_pCapabilities->m_2DArrayTextures)
1601 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY);
1602 else
1603 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D);
1604 {
1605 CGLSL PrimitiveVertexShader;
1606 CGLSL PrimitiveFragmentShader;
1607 PrimitiveVertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/pipeline.vert", GL_VERTEX_SHADER);
1608 PrimitiveFragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/pipeline.frag", GL_FRAGMENT_SHADER);
1609
1610 m_pPrimitive3DProgram->CreateProgram();
1611 m_pPrimitive3DProgram->AddShader(pShader: &PrimitiveVertexShader);
1612 m_pPrimitive3DProgram->AddShader(pShader: &PrimitiveFragmentShader);
1613 m_pPrimitive3DProgram->LinkProgram();
1614
1615 UseProgram(pProgram: m_pPrimitive3DProgram);
1616
1617 m_pPrimitive3DProgram->m_LocPos = m_pPrimitive3DProgram->GetUniformLoc(pName: "gPos");
1618 }
1619
1620 if(pCommand->m_pCapabilities->m_2DArrayTextures)
1621 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY);
1622 else
1623 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D);
1624 {
1625 CGLSL PrimitiveVertexShader;
1626 CGLSL PrimitiveFragmentShader;
1627 ShaderCompiler.AddDefine(pDefineName: "TW_TEXTURED", pDefineValue: "");
1628 if(!pCommand->m_pCapabilities->m_2DArrayTextures)
1629 ShaderCompiler.AddDefine(pDefineName: "TW_3D_TEXTURED", pDefineValue: "");
1630 PrimitiveVertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/pipeline.vert", GL_VERTEX_SHADER);
1631 PrimitiveFragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/pipeline.frag", GL_FRAGMENT_SHADER);
1632 ShaderCompiler.ClearDefines();
1633
1634 m_pPrimitive3DProgramTextured->CreateProgram();
1635 m_pPrimitive3DProgramTextured->AddShader(pShader: &PrimitiveVertexShader);
1636 m_pPrimitive3DProgramTextured->AddShader(pShader: &PrimitiveFragmentShader);
1637 m_pPrimitive3DProgramTextured->LinkProgram();
1638
1639 UseProgram(pProgram: m_pPrimitive3DProgramTextured);
1640
1641 m_pPrimitive3DProgramTextured->m_LocPos = m_pPrimitive3DProgramTextured->GetUniformLoc(pName: "gPos");
1642 m_pPrimitive3DProgramTextured->m_LocTextureSampler = m_pPrimitive3DProgramTextured->GetUniformLoc(pName: "gTextureSampler");
1643 }
1644 if(pCommand->m_pCapabilities->m_2DArrayTextures)
1645 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY);
1646 else
1647 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D);
1648 {
1649 CGLSL VertexShader;
1650 CGLSL FragmentShader;
1651 VertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile.vert", GL_VERTEX_SHADER);
1652 FragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile.frag", GL_FRAGMENT_SHADER);
1653
1654 m_pTileProgram->CreateProgram();
1655 m_pTileProgram->AddShader(pShader: &VertexShader);
1656 m_pTileProgram->AddShader(pShader: &FragmentShader);
1657
1658 glBindAttribLocation(m_pTileProgram->GetProgramId(), 0, "inVertex");
1659
1660 m_pTileProgram->LinkProgram();
1661
1662 UseProgram(pProgram: m_pTileProgram);
1663
1664 m_pTileProgram->m_LocPos = m_pTileProgram->GetUniformLoc(pName: "gPos");
1665 m_pTileProgram->m_LocColor = m_pTileProgram->GetUniformLoc(pName: "gVertColor");
1666 }
1667 if(pCommand->m_pCapabilities->m_2DArrayTextures)
1668 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY);
1669 else
1670 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D);
1671 {
1672 CGLSL VertexShader;
1673 CGLSL FragmentShader;
1674 ShaderCompiler.AddDefine(pDefineName: "TW_TILE_TEXTURED", pDefineValue: "");
1675 if(!pCommand->m_pCapabilities->m_2DArrayTextures)
1676 ShaderCompiler.AddDefine(pDefineName: "TW_TILE_3D_TEXTURED", pDefineValue: "");
1677 VertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile.vert", GL_VERTEX_SHADER);
1678 FragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile.frag", GL_FRAGMENT_SHADER);
1679 ShaderCompiler.ClearDefines();
1680
1681 m_pTileProgramTextured->CreateProgram();
1682 m_pTileProgramTextured->AddShader(pShader: &VertexShader);
1683 m_pTileProgramTextured->AddShader(pShader: &FragmentShader);
1684
1685 glBindAttribLocation(m_pTileProgram->GetProgramId(), 0, "inVertex");
1686 glBindAttribLocation(m_pTileProgram->GetProgramId(), 1, "inVertexTexCoord");
1687
1688 m_pTileProgramTextured->LinkProgram();
1689
1690 UseProgram(pProgram: m_pTileProgramTextured);
1691
1692 m_pTileProgramTextured->m_LocPos = m_pTileProgramTextured->GetUniformLoc(pName: "gPos");
1693 m_pTileProgramTextured->m_LocTextureSampler = m_pTileProgramTextured->GetUniformLoc(pName: "gTextureSampler");
1694 m_pTileProgramTextured->m_LocColor = m_pTileProgramTextured->GetUniformLoc(pName: "gVertColor");
1695 }
1696 if(pCommand->m_pCapabilities->m_2DArrayTextures)
1697 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY);
1698 else
1699 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D);
1700 {
1701 CGLSL VertexShader;
1702 CGLSL FragmentShader;
1703 VertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile_border.vert", GL_VERTEX_SHADER);
1704 FragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile_border.frag", GL_FRAGMENT_SHADER);
1705 ShaderCompiler.ClearDefines();
1706
1707 m_pBorderTileProgram->CreateProgram();
1708 m_pBorderTileProgram->AddShader(pShader: &VertexShader);
1709 m_pBorderTileProgram->AddShader(pShader: &FragmentShader);
1710
1711 glBindAttribLocation(m_pBorderTileProgram->GetProgramId(), 0, "inVertex");
1712
1713 m_pBorderTileProgram->LinkProgram();
1714
1715 UseProgram(pProgram: m_pBorderTileProgram);
1716
1717 m_pBorderTileProgram->m_LocPos = m_pBorderTileProgram->GetUniformLoc(pName: "gPos");
1718 m_pBorderTileProgram->m_LocColor = m_pBorderTileProgram->GetUniformLoc(pName: "gVertColor");
1719 m_pBorderTileProgram->m_LocOffset = m_pBorderTileProgram->GetUniformLoc(pName: "gOffset");
1720 m_pBorderTileProgram->m_LocScale = m_pBorderTileProgram->GetUniformLoc(pName: "gScale");
1721 }
1722 if(pCommand->m_pCapabilities->m_2DArrayTextures)
1723 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_2D_ARRAY);
1724 else
1725 ShaderCompiler.SetTextureReplaceType(CGLSLCompiler::GLSL_COMPILER_TEXTURE_REPLACE_TYPE_3D);
1726 {
1727 CGLSL VertexShader;
1728 CGLSL FragmentShader;
1729 ShaderCompiler.AddDefine(pDefineName: "TW_TILE_TEXTURED", pDefineValue: "");
1730 if(!pCommand->m_pCapabilities->m_2DArrayTextures)
1731 ShaderCompiler.AddDefine(pDefineName: "TW_TILE_3D_TEXTURED", pDefineValue: "");
1732 VertexShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile_border.vert", GL_VERTEX_SHADER);
1733 FragmentShader.LoadShader(pCompiler: &ShaderCompiler, pStorage: pCommand->m_pStorage, pFile: "shader/tile_border.frag", GL_FRAGMENT_SHADER);
1734 ShaderCompiler.ClearDefines();
1735
1736 m_pBorderTileProgramTextured->CreateProgram();
1737 m_pBorderTileProgramTextured->AddShader(pShader: &VertexShader);
1738 m_pBorderTileProgramTextured->AddShader(pShader: &FragmentShader);
1739
1740 glBindAttribLocation(m_pBorderTileProgramTextured->GetProgramId(), 0, "inVertex");
1741 glBindAttribLocation(m_pBorderTileProgramTextured->GetProgramId(), 1, "inVertexTexCoord");
1742
1743 m_pBorderTileProgramTextured->LinkProgram();
1744
1745 UseProgram(pProgram: m_pBorderTileProgramTextured);
1746
1747 m_pBorderTileProgramTextured->m_LocPos = m_pBorderTileProgramTextured->GetUniformLoc(pName: "gPos");
1748 m_pBorderTileProgramTextured->m_LocTextureSampler = m_pBorderTileProgramTextured->GetUniformLoc(pName: "gTextureSampler");
1749 m_pBorderTileProgramTextured->m_LocColor = m_pBorderTileProgramTextured->GetUniformLoc(pName: "gVertColor");
1750 m_pBorderTileProgramTextured->m_LocOffset = m_pBorderTileProgramTextured->GetUniformLoc(pName: "gOffset");
1751 m_pBorderTileProgramTextured->m_LocScale = m_pBorderTileProgramTextured->GetUniformLoc(pName: "gScale");
1752 }
1753
1754 glUseProgram(0);
1755 }
1756
1757 if(g_Config.m_Gfx3DTextureAnalysisRan == 0 || str_comp(a: g_Config.m_Gfx3DTextureAnalysisRenderer, b: pCommand->m_pRendererString) != 0 || str_comp(a: g_Config.m_Gfx3DTextureAnalysisVersion, b: pCommand->m_pVersionString) != 0)
1758 {
1759 AnalysisCorrect = IsTileMapAnalysisSucceeded();
1760 if(AnalysisCorrect)
1761 {
1762 g_Config.m_Gfx3DTextureAnalysisRan = 1;
1763 str_copy(dst&: g_Config.m_Gfx3DTextureAnalysisRenderer, src: pCommand->m_pRendererString);
1764 str_copy(dst&: g_Config.m_Gfx3DTextureAnalysisVersion, src: pCommand->m_pVersionString);
1765 }
1766 }
1767 }
1768
1769 if(!AnalysisCorrect || !HasAllFunc)
1770 {
1771 // downgrade to opengl 1.5
1772 *pCommand->m_pInitError = -2;
1773 pCommand->m_pCapabilities->m_ContextMajor = 1;
1774 pCommand->m_pCapabilities->m_ContextMinor = 5;
1775 pCommand->m_pCapabilities->m_ContextPatch = 0;
1776
1777 return false;
1778 }
1779
1780 return true;
1781}
1782
1783void CCommandProcessorFragment_OpenGL2::Cmd_Shutdown(const SCommand_Shutdown *pCommand)
1784{
1785 // TODO: cleanup the OpenGL context too
1786 delete m_pTileProgram;
1787 delete m_pTileProgramTextured;
1788 delete m_pPrimitive3DProgram;
1789 delete m_pPrimitive3DProgramTextured;
1790 for(auto &BufferObject : m_vBufferObjectIndices)
1791 free(ptr: BufferObject.m_pData);
1792}
1793
1794void CCommandProcessorFragment_OpenGL2::Cmd_RenderTex3D(const CCommandBuffer::SCommand_RenderTex3D *pCommand)
1795{
1796 if(m_HasShaders)
1797 {
1798 CGLSLPrimitiveProgram *pProgram = NULL;
1799 if(IsTexturedState(State: pCommand->m_State))
1800 {
1801 pProgram = m_pPrimitive3DProgramTextured;
1802 }
1803 else
1804 pProgram = m_pPrimitive3DProgram;
1805
1806 UseProgram(pProgram);
1807
1808 SetState(State: pCommand->m_State, pProgram, Use2DArrayTextures: true);
1809 }
1810 else
1811 {
1812 CCommandProcessorFragment_OpenGL::SetState(State: pCommand->m_State, Use2DArrayTextures: true);
1813 }
1814
1815 glEnableClientState(GL_VERTEX_ARRAY);
1816 glEnableClientState(GL_COLOR_ARRAY);
1817 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1818
1819 glVertexPointer(size: 2, GL_FLOAT, stride: sizeof(pCommand->m_pVertices[0]), pointer: pCommand->m_pVertices);
1820 glColorPointer(size: 4, GL_UNSIGNED_BYTE, stride: sizeof(pCommand->m_pVertices[0]), pointer: (uint8_t *)pCommand->m_pVertices + (ptrdiff_t)(sizeof(vec2)));
1821 glTexCoordPointer(size: 3, GL_FLOAT, stride: sizeof(pCommand->m_pVertices[0]), pointer: (uint8_t *)pCommand->m_pVertices + (ptrdiff_t)(sizeof(vec2) + sizeof(unsigned char) * 4));
1822
1823 switch(pCommand->m_PrimType)
1824 {
1825 case EPrimitiveType::QUADS:
1826 glDrawArrays(GL_QUADS, first: 0, count: pCommand->m_PrimCount * 4);
1827 break;
1828 case EPrimitiveType::TRIANGLES:
1829 glDrawArrays(GL_TRIANGLES, first: 0, count: pCommand->m_PrimCount * 3);
1830 break;
1831 default:
1832 dbg_assert_failed("Invalid primitive type: %d", (int)pCommand->m_PrimType);
1833 };
1834
1835 glDisableClientState(GL_VERTEX_ARRAY);
1836 glDisableClientState(GL_COLOR_ARRAY);
1837 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1838
1839 if(m_HasShaders)
1840 {
1841 glUseProgram(0);
1842 }
1843}
1844
1845void CCommandProcessorFragment_OpenGL2::Cmd_CreateBufferObject(const CCommandBuffer::SCommand_CreateBufferObject *pCommand)
1846{
1847 void *pUploadData = pCommand->m_pUploadData;
1848 const int Index = pCommand->m_BufferIndex;
1849 // create necessary space
1850 if((size_t)Index >= m_vBufferObjectIndices.size())
1851 {
1852 m_vBufferObjectIndices.resize(sz: Index + 1, c: 0);
1853 }
1854
1855 GLuint VertBufferId = 0;
1856
1857 glGenBuffers(1, &VertBufferId);
1858 glBindBuffer(GL_ARRAY_BUFFER, VertBufferId);
1859 glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(pCommand->m_DataSize), pUploadData, GL_STATIC_DRAW);
1860 glBindBuffer(GL_ARRAY_BUFFER, 0);
1861
1862 SBufferObject &BufferObject = m_vBufferObjectIndices[Index];
1863 BufferObject.m_BufferObjectId = VertBufferId;
1864 BufferObject.m_DataSize = pCommand->m_DataSize;
1865 BufferObject.m_pData = static_cast<uint8_t *>(malloc(size: pCommand->m_DataSize));
1866 if(pUploadData)
1867 mem_copy(dest: BufferObject.m_pData, source: pUploadData, size: pCommand->m_DataSize);
1868
1869 if(pCommand->m_DeletePointer)
1870 free(ptr: pUploadData);
1871}
1872
1873void CCommandProcessorFragment_OpenGL2::Cmd_RecreateBufferObject(const CCommandBuffer::SCommand_RecreateBufferObject *pCommand)
1874{
1875 void *pUploadData = pCommand->m_pUploadData;
1876 int Index = pCommand->m_BufferIndex;
1877 SBufferObject &BufferObject = m_vBufferObjectIndices[Index];
1878
1879 glBindBuffer(GL_ARRAY_BUFFER, BufferObject.m_BufferObjectId);
1880 glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(pCommand->m_DataSize), pUploadData, GL_STATIC_DRAW);
1881 glBindBuffer(GL_ARRAY_BUFFER, 0);
1882
1883 BufferObject.m_DataSize = pCommand->m_DataSize;
1884 free(ptr: BufferObject.m_pData);
1885 BufferObject.m_pData = static_cast<uint8_t *>(malloc(size: pCommand->m_DataSize));
1886 if(pUploadData)
1887 mem_copy(dest: BufferObject.m_pData, source: pUploadData, size: pCommand->m_DataSize);
1888
1889 if(pCommand->m_DeletePointer)
1890 free(ptr: pUploadData);
1891}
1892
1893void CCommandProcessorFragment_OpenGL2::Cmd_UpdateBufferObject(const CCommandBuffer::SCommand_UpdateBufferObject *pCommand)
1894{
1895 void *pUploadData = pCommand->m_pUploadData;
1896 int Index = pCommand->m_BufferIndex;
1897 SBufferObject &BufferObject = m_vBufferObjectIndices[Index];
1898
1899 glBindBuffer(GL_ARRAY_BUFFER, BufferObject.m_BufferObjectId);
1900 glBufferSubData(GL_ARRAY_BUFFER, (GLintptr)(pCommand->m_pOffset), (GLsizeiptr)(pCommand->m_DataSize), pUploadData);
1901 glBindBuffer(GL_ARRAY_BUFFER, 0);
1902
1903 if(pUploadData)
1904 mem_copy(dest: BufferObject.m_pData + (ptrdiff_t)pCommand->m_pOffset, source: pUploadData, size: pCommand->m_DataSize);
1905
1906 if(pCommand->m_DeletePointer)
1907 free(ptr: pUploadData);
1908}
1909
1910void CCommandProcessorFragment_OpenGL2::Cmd_CopyBufferObject(const CCommandBuffer::SCommand_CopyBufferObject *pCommand)
1911{
1912 int WriteIndex = pCommand->m_WriteBufferIndex;
1913 int ReadIndex = pCommand->m_ReadBufferIndex;
1914
1915 SBufferObject &ReadBufferObject = m_vBufferObjectIndices[ReadIndex];
1916 SBufferObject &WriteBufferObject = m_vBufferObjectIndices[WriteIndex];
1917
1918 mem_copy(dest: WriteBufferObject.m_pData + (ptrdiff_t)pCommand->m_WriteOffset, source: ReadBufferObject.m_pData + (ptrdiff_t)pCommand->m_ReadOffset, size: pCommand->m_CopySize);
1919
1920 glBindBuffer(GL_ARRAY_BUFFER, WriteBufferObject.m_BufferObjectId);
1921 glBufferSubData(GL_ARRAY_BUFFER, (GLintptr)(pCommand->m_WriteOffset), (GLsizeiptr)(pCommand->m_CopySize), WriteBufferObject.m_pData + (ptrdiff_t)pCommand->m_WriteOffset);
1922 glBindBuffer(GL_ARRAY_BUFFER, 0);
1923}
1924
1925void CCommandProcessorFragment_OpenGL2::Cmd_DeleteBufferObject(const CCommandBuffer::SCommand_DeleteBufferObject *pCommand)
1926{
1927 int Index = pCommand->m_BufferIndex;
1928 SBufferObject &BufferObject = m_vBufferObjectIndices[Index];
1929
1930 glDeleteBuffers(1, &BufferObject.m_BufferObjectId);
1931
1932 free(ptr: BufferObject.m_pData);
1933 BufferObject.m_pData = NULL;
1934}
1935
1936void CCommandProcessorFragment_OpenGL2::Cmd_CreateBufferContainer(const CCommandBuffer::SCommand_CreateBufferContainer *pCommand)
1937{
1938 const int Index = pCommand->m_BufferContainerIndex;
1939 // create necessary space
1940 if((size_t)Index >= m_vBufferContainers.size())
1941 {
1942 SBufferContainer Container;
1943 Container.m_ContainerInfo.m_Stride = 0;
1944 Container.m_ContainerInfo.m_VertBufferBindingIndex = -1;
1945 m_vBufferContainers.resize(sz: Index + 1, c: Container);
1946 }
1947
1948 SBufferContainer &BufferContainer = m_vBufferContainers[Index];
1949
1950 for(size_t i = 0; i < pCommand->m_AttrCount; ++i)
1951 {
1952 BufferContainer.m_ContainerInfo.m_vAttributes.push_back(x: pCommand->m_pAttributes[i]);
1953 }
1954
1955 BufferContainer.m_ContainerInfo.m_Stride = pCommand->m_Stride;
1956 BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex = pCommand->m_VertBufferBindingIndex;
1957}
1958
1959void CCommandProcessorFragment_OpenGL2::Cmd_UpdateBufferContainer(const CCommandBuffer::SCommand_UpdateBufferContainer *pCommand)
1960{
1961 SBufferContainer &BufferContainer = m_vBufferContainers[pCommand->m_BufferContainerIndex];
1962
1963 BufferContainer.m_ContainerInfo.m_vAttributes.clear();
1964
1965 for(size_t i = 0; i < pCommand->m_AttrCount; ++i)
1966 {
1967 BufferContainer.m_ContainerInfo.m_vAttributes.push_back(x: pCommand->m_pAttributes[i]);
1968 }
1969
1970 BufferContainer.m_ContainerInfo.m_Stride = pCommand->m_Stride;
1971 BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex = pCommand->m_VertBufferBindingIndex;
1972}
1973
1974void CCommandProcessorFragment_OpenGL2::Cmd_DeleteBufferContainer(const CCommandBuffer::SCommand_DeleteBufferContainer *pCommand)
1975{
1976 SBufferContainer &BufferContainer = m_vBufferContainers[pCommand->m_BufferContainerIndex];
1977
1978 if(pCommand->m_DestroyAllBO)
1979 {
1980 int VertBufferId = BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex;
1981 if(VertBufferId != -1)
1982 {
1983 glDeleteBuffers(1, &m_vBufferObjectIndices[VertBufferId].m_BufferObjectId);
1984
1985 free(ptr: m_vBufferObjectIndices[VertBufferId].m_pData);
1986 m_vBufferObjectIndices[VertBufferId].m_pData = NULL;
1987 }
1988 }
1989
1990 BufferContainer.m_ContainerInfo.m_vAttributes.clear();
1991}
1992
1993void CCommandProcessorFragment_OpenGL2::Cmd_IndicesRequiredNumNotify(const CCommandBuffer::SCommand_IndicesRequiredNumNotify *pCommand)
1994{
1995}
1996
1997void CCommandProcessorFragment_OpenGL2::Cmd_RenderBorderTile(const CCommandBuffer::SCommand_RenderBorderTile *pCommand)
1998{
1999 int Index = pCommand->m_BufferContainerIndex;
2000 // if space not there return
2001 if((size_t)Index >= m_vBufferContainers.size())
2002 return;
2003
2004 SBufferContainer &BufferContainer = m_vBufferContainers[Index];
2005
2006 CGLSLTileProgram *pProgram = NULL;
2007 if(IsTexturedState(State: pCommand->m_State))
2008 pProgram = m_pBorderTileProgramTextured;
2009 else
2010 pProgram = m_pBorderTileProgram;
2011 UseProgram(pProgram);
2012
2013 SetState(State: pCommand->m_State, pProgram, Use2DArrayTextures: true);
2014 pProgram->SetUniformVec4(Loc: pProgram->m_LocColor, Count: 1, pValue: (float *)&pCommand->m_Color);
2015
2016 pProgram->SetUniformVec2(Loc: pProgram->m_LocOffset, Count: 1, pValue: (float *)&pCommand->m_Offset);
2017 pProgram->SetUniformVec2(Loc: pProgram->m_LocScale, Count: 1, pValue: (float *)&pCommand->m_Scale);
2018
2019 bool IsTextured = BufferContainer.m_ContainerInfo.m_vAttributes.size() == 2;
2020
2021 SBufferObject &BufferObject = m_vBufferObjectIndices[(size_t)BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex];
2022
2023 glBindBuffer(GL_ARRAY_BUFFER, BufferObject.m_BufferObjectId);
2024
2025 glEnableVertexAttribArray(0);
2026 glVertexAttribPointer(0, 2, GL_FLOAT, false, BufferContainer.m_ContainerInfo.m_Stride, BufferContainer.m_ContainerInfo.m_vAttributes[0].m_pOffset);
2027 if(IsTextured)
2028 {
2029 glEnableVertexAttribArray(1);
2030 glVertexAttribIPointer(1, 4, GL_UNSIGNED_BYTE, BufferContainer.m_ContainerInfo.m_Stride, BufferContainer.m_ContainerInfo.m_vAttributes[1].m_pOffset);
2031 }
2032
2033 size_t RealDrawCount = pCommand->m_DrawNum * 4;
2034 GLint RealOffset = (GLint)((((size_t)(uintptr_t)(pCommand->m_pIndicesOffset)) / (6 * sizeof(unsigned int))) * 4);
2035 glDrawArrays(GL_QUADS, first: RealOffset, count: RealDrawCount);
2036
2037 glDisableVertexAttribArray(0);
2038 if(IsTextured)
2039 glDisableVertexAttribArray(1);
2040 glBindBuffer(GL_ARRAY_BUFFER, 0);
2041 glUseProgram(0);
2042}
2043
2044void CCommandProcessorFragment_OpenGL2::Cmd_RenderTileLayer(const CCommandBuffer::SCommand_RenderTileLayer *pCommand)
2045{
2046 int Index = pCommand->m_BufferContainerIndex;
2047 // if space not there return
2048 if((size_t)Index >= m_vBufferContainers.size())
2049 return;
2050
2051 SBufferContainer &BufferContainer = m_vBufferContainers[Index];
2052
2053 if(pCommand->m_IndicesDrawNum == 0)
2054 {
2055 return; // nothing to draw
2056 }
2057
2058 CGLSLTileProgram *pProgram = NULL;
2059 if(IsTexturedState(State: pCommand->m_State))
2060 {
2061 pProgram = m_pTileProgramTextured;
2062 }
2063 else
2064 pProgram = m_pTileProgram;
2065
2066 UseProgram(pProgram);
2067
2068 SetState(State: pCommand->m_State, pProgram, Use2DArrayTextures: true);
2069 pProgram->SetUniformVec4(Loc: pProgram->m_LocColor, Count: 1, pValue: (float *)&pCommand->m_Color);
2070
2071 bool IsTextured = BufferContainer.m_ContainerInfo.m_vAttributes.size() == 2;
2072
2073 SBufferObject &BufferObject = m_vBufferObjectIndices[(size_t)BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex];
2074
2075 glBindBuffer(GL_ARRAY_BUFFER, BufferObject.m_BufferObjectId);
2076
2077 glEnableVertexAttribArray(0);
2078 glVertexAttribPointer(0, 2, GL_FLOAT, false, BufferContainer.m_ContainerInfo.m_Stride, BufferContainer.m_ContainerInfo.m_vAttributes[0].m_pOffset);
2079 if(IsTextured)
2080 {
2081 glEnableVertexAttribArray(1);
2082 glVertexAttribIPointer(1, 4, GL_UNSIGNED_BYTE, BufferContainer.m_ContainerInfo.m_Stride, BufferContainer.m_ContainerInfo.m_vAttributes[1].m_pOffset);
2083 }
2084
2085 for(int i = 0; i < pCommand->m_IndicesDrawNum; ++i)
2086 {
2087 size_t RealDrawCount = (pCommand->m_pDrawCount[i] / 6) * 4;
2088 GLint RealOffset = (GLint)((((size_t)(uintptr_t)(pCommand->m_pIndicesOffsets[i])) / (6 * sizeof(unsigned int))) * 4);
2089 glDrawArrays(GL_QUADS, first: RealOffset, count: RealDrawCount);
2090 }
2091
2092 glDisableVertexAttribArray(0);
2093 if(IsTextured)
2094 glDisableVertexAttribArray(1);
2095 glBindBuffer(GL_ARRAY_BUFFER, 0);
2096 glUseProgram(0);
2097}
2098
2099#undef BACKEND_GL_MODERN_API
2100
2101#endif
2102
2103#endif
2104