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