RmlUi_Renderer_GL3.cpp 66 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027
  1. /*
  2. * This source file is part of RmlUi, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://github.com/mikke89/RmlUi
  5. *
  6. * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
  7. * Copyright (c) 2019-2023 The RmlUi Team, and contributors
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. *
  27. */
  28. #include "RmlUi_Renderer_GL3.h"
  29. #include <RmlUi/Core/Core.h>
  30. #include <RmlUi/Core/DecorationTypes.h>
  31. #include <RmlUi/Core/FileInterface.h>
  32. #include <RmlUi/Core/GeometryUtilities.h>
  33. #include <RmlUi/Core/Log.h>
  34. #include <RmlUi/Core/Platform.h>
  35. #include <string.h>
  36. #if defined(RMLUI_PLATFORM_WIN32) && !defined(__MINGW32__)
  37. // function call missing argument list
  38. #pragma warning(disable : 4551)
  39. // unreferenced local function has been removed
  40. #pragma warning(disable : 4505)
  41. #endif
  42. #if defined RMLUI_PLATFORM_EMSCRIPTEN
  43. #define RMLUI_SHADER_HEADER_VERSION "#version 300 es\nprecision highp float;\n"
  44. #include <GLES3/gl3.h>
  45. #elif defined RMLUI_GL3_CUSTOM_LOADER
  46. #define RMLUI_SHADER_HEADER_VERSION "#version 330\n"
  47. #include RMLUI_GL3_CUSTOM_LOADER
  48. #else
  49. #define RMLUI_SHADER_HEADER_VERSION "#version 330\n"
  50. #define GLAD_GL_IMPLEMENTATION
  51. #include "RmlUi_Include_GL3.h"
  52. #endif
  53. // Determines the anti-aliasing quality when creating layers. Enables better-looking visuals, especially when transforms are applied.
  54. static constexpr int NUM_MSAA_SAMPLES = 2;
  55. #define RMLUI_PREMULTIPLIED_ALPHA 1
  56. #define MAX_NUM_STOPS 16
  57. #define BLUR_SIZE 7
  58. #define BLUR_NUM_WEIGHTS ((BLUR_SIZE + 1) / 2)
  59. #define RMLUI_STRINGIFY_IMPL(x) #x
  60. #define RMLUI_STRINGIFY(x) RMLUI_STRINGIFY_IMPL(x)
  61. #define RMLUI_SHADER_HEADER \
  62. RMLUI_SHADER_HEADER_VERSION \
  63. "#define RMLUI_PREMULTIPLIED_ALPHA " RMLUI_STRINGIFY(RMLUI_PREMULTIPLIED_ALPHA) "\n#define MAX_NUM_STOPS " RMLUI_STRINGIFY(MAX_NUM_STOPS) "\n"
  64. static const char* shader_vert_main = RMLUI_SHADER_HEADER R"(
  65. uniform vec2 _translate;
  66. uniform mat4 _transform;
  67. in vec2 inPosition;
  68. in vec4 inColor0;
  69. in vec2 inTexCoord0;
  70. out vec2 fragTexCoord;
  71. out vec4 fragColor;
  72. void main() {
  73. fragTexCoord = inTexCoord0;
  74. fragColor = inColor0;
  75. #if RMLUI_PREMULTIPLIED_ALPHA
  76. // Pre-multiply vertex colors with their alpha.
  77. fragColor.rgb = fragColor.rgb * fragColor.a;
  78. #endif
  79. vec2 translatedPos = inPosition + _translate;
  80. vec4 outPos = _transform * vec4(translatedPos, 0.0, 1.0);
  81. gl_Position = outPos;
  82. }
  83. )";
  84. static const char* shader_frag_texture = RMLUI_SHADER_HEADER R"(
  85. uniform sampler2D _tex;
  86. in vec2 fragTexCoord;
  87. in vec4 fragColor;
  88. out vec4 finalColor;
  89. void main() {
  90. vec4 texColor = texture(_tex, fragTexCoord);
  91. finalColor = fragColor * texColor;
  92. }
  93. )";
  94. static const char* shader_frag_color = RMLUI_SHADER_HEADER R"(
  95. in vec2 fragTexCoord;
  96. in vec4 fragColor;
  97. out vec4 finalColor;
  98. void main() {
  99. finalColor = fragColor;
  100. }
  101. )";
  102. enum class ShaderGradientFunction { Linear, RepeatingLinear }; // Must match shader definitions below.
  103. static const char* shader_frag_gradient = RMLUI_SHADER_HEADER R"(
  104. #define LINEAR 0
  105. #define REPEATING_LINEAR 1
  106. #define PI 3.14159265
  107. uniform int _func; // one of the above definitions
  108. uniform vec2 _p; // starting point
  109. uniform vec2 _v; // vector to ending point
  110. uniform vec4 _stop_colors[MAX_NUM_STOPS];
  111. uniform float _stop_positions[MAX_NUM_STOPS]; // normalized, 0 -> starting point, 1 -> ending point
  112. uniform int _num_stops;
  113. in vec2 fragTexCoord;
  114. in vec4 fragColor;
  115. out vec4 finalColor;
  116. vec4 mix_stop_colors(float t) {
  117. vec4 color = _stop_colors[0];
  118. for (int i = 1; i < _num_stops; i++)
  119. color = mix(color, _stop_colors[i], smoothstep(_stop_positions[i-1], _stop_positions[i], t));
  120. return color;
  121. }
  122. void main() {
  123. float t = 0;
  124. float dist_square = dot(_v, _v);
  125. vec2 V = fragTexCoord - _p;
  126. t = dot(_v, V) / dist_square;
  127. if (_func == REPEATING_LINEAR)
  128. {
  129. float t0 = _stop_positions[0];
  130. float t1 = _stop_positions[_num_stops - 1];
  131. t = t0 + mod(t - t0, t1 - t0);
  132. }
  133. finalColor = fragColor * mix_stop_colors(t);
  134. }
  135. )";
  136. static const char* shader_vert_passthrough = RMLUI_SHADER_HEADER R"(
  137. in vec2 inPosition;
  138. in vec2 inTexCoord0;
  139. out vec2 fragTexCoord;
  140. void main() {
  141. fragTexCoord = inTexCoord0;
  142. gl_Position = vec4(inPosition, 0.0, 1.0);
  143. }
  144. )";
  145. static const char* shader_frag_passthrough = RMLUI_SHADER_HEADER R"(
  146. uniform sampler2D _tex;
  147. in vec2 fragTexCoord;
  148. out vec4 finalColor;
  149. void main() {
  150. finalColor = texture(_tex, fragTexCoord);
  151. }
  152. )";
  153. static const char* shader_frag_color_matrix = RMLUI_SHADER_HEADER R"(
  154. uniform sampler2D _tex;
  155. uniform mat4 _color_matrix;
  156. in vec2 fragTexCoord;
  157. out vec4 finalColor;
  158. void main() {
  159. vec4 texColor = texture(_tex, fragTexCoord);
  160. finalColor = _color_matrix * texColor;
  161. }
  162. )";
  163. #define RMLUI_SHADER_BLUR_HEADER \
  164. RMLUI_SHADER_HEADER "\n#define BLUR_SIZE " RMLUI_STRINGIFY(BLUR_SIZE) "\n#define BLUR_NUM_WEIGHTS " RMLUI_STRINGIFY(BLUR_NUM_WEIGHTS)
  165. static const char* shader_vert_blur = RMLUI_SHADER_BLUR_HEADER R"(
  166. uniform vec2 _texelOffset;
  167. in vec3 inPosition;
  168. in vec2 inTexCoord0;
  169. out vec2 fragTexCoord[BLUR_SIZE];
  170. void main() {
  171. for(int i = 0; i < BLUR_SIZE; i++)
  172. fragTexCoord[i] = inTexCoord0 - float(i - BLUR_NUM_WEIGHTS + 1) * _texelOffset;
  173. gl_Position = vec4(inPosition, 1.0);
  174. }
  175. )";
  176. static const char* shader_frag_blur = RMLUI_SHADER_BLUR_HEADER R"(
  177. uniform sampler2D _tex;
  178. uniform float _weights[BLUR_NUM_WEIGHTS];
  179. uniform vec2 _texCoordMin;
  180. uniform vec2 _texCoordMax;
  181. in vec2 fragTexCoord[BLUR_SIZE];
  182. out vec4 finalColor;
  183. void main() {
  184. vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
  185. for(int i = 0; i < BLUR_SIZE; i++)
  186. {
  187. vec2 in_region = step(_texCoordMin, fragTexCoord[i]) * step(fragTexCoord[i], _texCoordMax);
  188. color += texture(_tex, fragTexCoord[i]) * in_region.x * in_region.y * _weights[abs(i - BLUR_NUM_WEIGHTS + 1)];
  189. }
  190. finalColor = color;
  191. }
  192. )";
  193. static const char* shader_frag_drop_shadow = RMLUI_SHADER_HEADER R"(
  194. uniform sampler2D _tex;
  195. uniform vec2 _texCoordMin;
  196. uniform vec2 _texCoordMax;
  197. uniform vec4 _color;
  198. in vec2 fragTexCoord;
  199. out vec4 finalColor;
  200. void main() {
  201. vec2 in_region = step(_texCoordMin, fragTexCoord) * step(fragTexCoord, _texCoordMax);
  202. finalColor = texture(_tex, fragTexCoord).a * in_region.x * in_region.y * _color;
  203. }
  204. )";
  205. enum class ProgramId {
  206. None,
  207. Color,
  208. Texture,
  209. Gradient,
  210. Passthrough,
  211. ColorMatrix,
  212. Blur,
  213. DropShadow,
  214. Count,
  215. };
  216. enum class VertShaderId {
  217. Main,
  218. Passthrough,
  219. Blur,
  220. Count,
  221. };
  222. enum class FragShaderId {
  223. Color,
  224. Texture,
  225. Gradient,
  226. Passthrough,
  227. ColorMatrix,
  228. Blur,
  229. DropShadow,
  230. Count,
  231. };
  232. enum class UniformId {
  233. Translate,
  234. Transform,
  235. Tex,
  236. Color,
  237. ColorMatrix,
  238. TexelOffset,
  239. TexCoordMin,
  240. TexCoordMax,
  241. Weights,
  242. Func,
  243. P,
  244. V,
  245. StopColors,
  246. StopPositions,
  247. NumStops,
  248. Count,
  249. };
  250. namespace Gfx {
  251. static const char* const program_uniform_names[(size_t)UniformId::Count] = {"_translate", "_transform", "_tex", "_color", "_color_matrix",
  252. "_texelOffset", "_texCoordMin", "_texCoordMax", "_weights[0]", "_func", "_p", "_v", "_stop_colors[0]", "_stop_positions[0]", "_num_stops"};
  253. enum class VertexAttribute { Position, Color0, TexCoord0, Count };
  254. static const char* const vertex_attribute_names[(size_t)VertexAttribute::Count] = {"inPosition", "inColor0", "inTexCoord0"};
  255. struct VertShaderDefinition {
  256. VertShaderId id;
  257. const char* name_str;
  258. const char* code_str;
  259. };
  260. struct FragShaderDefinition {
  261. FragShaderId id;
  262. const char* name_str;
  263. const char* code_str;
  264. };
  265. struct ProgramDefinition {
  266. ProgramId id;
  267. const char* name_str;
  268. VertShaderId vert_shader;
  269. FragShaderId frag_shader;
  270. };
  271. // clang-format off
  272. static const VertShaderDefinition vert_shader_definitions[] = {
  273. {VertShaderId::Main, "main", shader_vert_main},
  274. {VertShaderId::Passthrough, "passthrough", shader_vert_passthrough},
  275. {VertShaderId::Blur, "blur", shader_vert_blur},
  276. };
  277. static const FragShaderDefinition frag_shader_definitions[] = {
  278. {FragShaderId::Color, "color", shader_frag_color},
  279. {FragShaderId::Texture, "texture", shader_frag_texture},
  280. {FragShaderId::Gradient, "gradient", shader_frag_gradient},
  281. {FragShaderId::Passthrough, "passthrough", shader_frag_passthrough},
  282. {FragShaderId::ColorMatrix, "color_matrix", shader_frag_color_matrix},
  283. {FragShaderId::Blur, "blur", shader_frag_blur},
  284. {FragShaderId::DropShadow, "drop_shadow", shader_frag_drop_shadow},
  285. };
  286. static const ProgramDefinition program_definitions[] = {
  287. {ProgramId::Color, "color", VertShaderId::Main, FragShaderId::Color},
  288. {ProgramId::Texture, "texture", VertShaderId::Main, FragShaderId::Texture},
  289. {ProgramId::Gradient, "gradient", VertShaderId::Main, FragShaderId::Gradient},
  290. {ProgramId::Passthrough, "passthrough", VertShaderId::Passthrough, FragShaderId::Passthrough},
  291. {ProgramId::ColorMatrix, "color_matrix", VertShaderId::Passthrough, FragShaderId::ColorMatrix},
  292. {ProgramId::Blur, "blur", VertShaderId::Blur, FragShaderId::Blur},
  293. {ProgramId::DropShadow, "drop_shadow", VertShaderId::Passthrough, FragShaderId::DropShadow},
  294. };
  295. // clang-format on
  296. template <typename T, typename Enum>
  297. class EnumArray {
  298. public:
  299. const T& operator[](Enum id) const
  300. {
  301. RMLUI_ASSERT((size_t)id < (size_t)Enum::Count);
  302. return ids[size_t(id)];
  303. }
  304. T& operator[](Enum id)
  305. {
  306. RMLUI_ASSERT((size_t)id < (size_t)Enum::Count);
  307. return ids[size_t(id)];
  308. }
  309. auto begin() const { return ids.begin(); }
  310. auto end() const { return ids.end(); }
  311. private:
  312. Rml::Array<T, (size_t)Enum::Count> ids = {};
  313. };
  314. using Programs = EnumArray<GLuint, ProgramId>;
  315. using VertShaders = EnumArray<GLuint, VertShaderId>;
  316. using FragShaders = EnumArray<GLuint, FragShaderId>;
  317. class Uniforms {
  318. public:
  319. GLint Get(ProgramId id, UniformId uniform) const
  320. {
  321. auto it = map.find(ToKey(id, uniform));
  322. if (it != map.end())
  323. return it->second;
  324. return -1;
  325. }
  326. void Insert(ProgramId id, UniformId uniform, GLint location) { map[ToKey(id, uniform)] = location; }
  327. private:
  328. using Key = std::uint64_t;
  329. Key ToKey(ProgramId id, UniformId uniform) const { return (static_cast<Key>(id) << 32) | static_cast<Key>(uniform); }
  330. Rml::UnorderedMap<Key, GLint> map;
  331. };
  332. struct ProgramData {
  333. Programs programs;
  334. VertShaders vert_shaders;
  335. FragShaders frag_shaders;
  336. Uniforms uniforms;
  337. };
  338. struct CompiledGeometryData {
  339. GLuint vao;
  340. GLuint vbo;
  341. GLuint ibo;
  342. GLsizei draw_count;
  343. };
  344. struct FramebufferData {
  345. int width, height;
  346. GLuint framebuffer;
  347. GLuint color_tex_buffer;
  348. GLuint color_render_buffer;
  349. GLuint depth_stencil_buffer;
  350. bool owns_depth_stencil_buffer;
  351. };
  352. enum class FramebufferAttachment { None, Depth, DepthStencil };
  353. static void CheckGLError(const char* operation_name)
  354. {
  355. #ifdef RMLUI_DEBUG
  356. GLenum error_code = glGetError();
  357. if (error_code != GL_NO_ERROR)
  358. {
  359. static const Rml::Pair<GLenum, const char*> error_names[] = {{GL_INVALID_ENUM, "GL_INVALID_ENUM"}, {GL_INVALID_VALUE, "GL_INVALID_VALUE"},
  360. {GL_INVALID_OPERATION, "GL_INVALID_OPERATION"}, {GL_OUT_OF_MEMORY, "GL_OUT_OF_MEMORY"}};
  361. const char* error_str = "''";
  362. for (auto& err : error_names)
  363. {
  364. if (err.first == error_code)
  365. {
  366. error_str = err.second;
  367. break;
  368. }
  369. }
  370. Rml::Log::Message(Rml::Log::LT_ERROR, "OpenGL error during %s. Error code 0x%x (%s).", operation_name, error_code, error_str);
  371. }
  372. #endif
  373. (void)operation_name;
  374. }
  375. // Create the shader, 'shader_type' is either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
  376. static bool CreateShader(GLuint& out_shader_id, GLenum shader_type, const char* code_string)
  377. {
  378. RMLUI_ASSERT(shader_type == GL_VERTEX_SHADER || shader_type == GL_FRAGMENT_SHADER);
  379. GLuint id = glCreateShader(shader_type);
  380. glShaderSource(id, 1, (const GLchar**)&code_string, NULL);
  381. glCompileShader(id);
  382. GLint status = 0;
  383. glGetShaderiv(id, GL_COMPILE_STATUS, &status);
  384. if (status == GL_FALSE)
  385. {
  386. GLint info_log_length = 0;
  387. glGetShaderiv(id, GL_INFO_LOG_LENGTH, &info_log_length);
  388. char* info_log_string = new char[info_log_length + 1];
  389. glGetShaderInfoLog(id, info_log_length, NULL, info_log_string);
  390. Rml::Log::Message(Rml::Log::LT_ERROR, "Compile failure in OpenGL shader: %s", info_log_string);
  391. delete[] info_log_string;
  392. glDeleteShader(id);
  393. return false;
  394. }
  395. CheckGLError("CreateShader");
  396. out_shader_id = id;
  397. return true;
  398. }
  399. static bool CreateProgram(GLuint& out_program, Uniforms& inout_uniform_map, ProgramId program_id, GLuint vertex_shader, GLuint fragment_shader)
  400. {
  401. GLuint id = glCreateProgram();
  402. RMLUI_ASSERT(id);
  403. for (GLuint i = 0; i < (GLuint)VertexAttribute::Count; i++)
  404. glBindAttribLocation(id, i, vertex_attribute_names[i]);
  405. CheckGLError("BindAttribLocations");
  406. glAttachShader(id, vertex_shader);
  407. glAttachShader(id, fragment_shader);
  408. glLinkProgram(id);
  409. glDetachShader(id, vertex_shader);
  410. glDetachShader(id, fragment_shader);
  411. GLint status = 0;
  412. glGetProgramiv(id, GL_LINK_STATUS, &status);
  413. if (status == GL_FALSE)
  414. {
  415. GLint info_log_length = 0;
  416. glGetProgramiv(id, GL_INFO_LOG_LENGTH, &info_log_length);
  417. char* info_log_string = new char[info_log_length + 1];
  418. glGetProgramInfoLog(id, info_log_length, NULL, info_log_string);
  419. Rml::Log::Message(Rml::Log::LT_ERROR, "OpenGL program linking failure: %s", info_log_string);
  420. delete[] info_log_string;
  421. glDeleteProgram(id);
  422. return false;
  423. }
  424. out_program = id;
  425. // Make a lookup table for the uniform locations.
  426. GLint num_active_uniforms = 0;
  427. glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &num_active_uniforms);
  428. constexpr size_t name_size = 64;
  429. GLchar name_buf[name_size] = "";
  430. for (int unif = 0; unif < num_active_uniforms; ++unif)
  431. {
  432. GLint array_size = 0;
  433. GLenum type = 0;
  434. GLsizei actual_length = 0;
  435. glGetActiveUniform(id, unif, name_size, &actual_length, &array_size, &type, name_buf);
  436. GLint location = glGetUniformLocation(id, name_buf);
  437. // See if we have the name in our pre-defined name list.
  438. UniformId program_uniform = UniformId::Count;
  439. for (int i = 0; i < (int)UniformId::Count; i++)
  440. {
  441. const char* uniform_name = program_uniform_names[i];
  442. if (strcmp(name_buf, uniform_name) == 0)
  443. {
  444. program_uniform = (UniformId)i;
  445. break;
  446. }
  447. }
  448. if ((size_t)program_uniform < (size_t)UniformId::Count)
  449. {
  450. inout_uniform_map.Insert(program_id, program_uniform, location);
  451. }
  452. else
  453. {
  454. Rml::Log::Message(Rml::Log::LT_ERROR, "OpenGL program uses unknown uniform '%s'.", name_buf);
  455. return false;
  456. }
  457. }
  458. CheckGLError("CreateProgram");
  459. return true;
  460. }
  461. static bool CreateFramebuffer(FramebufferData& out_fb, int width, int height, int samples, FramebufferAttachment attachment,
  462. GLuint shared_depth_stencil_buffer)
  463. {
  464. #ifdef RMLUI_PLATFORM_EMSCRIPTEN
  465. constexpr GLint wrap_mode = GL_CLAMP_TO_EDGE;
  466. #else
  467. constexpr GLint wrap_mode = GL_CLAMP_TO_BORDER; // GL_REPEAT GL_MIRRORED_REPEAT GL_CLAMP_TO_EDGE
  468. #endif
  469. constexpr GLenum color_format = GL_RGBA8; // GL_RGBA8 GL_SRGB8_ALPHA8 GL_RGBA16F
  470. constexpr GLint min_mag_filter = GL_LINEAR; // GL_NEAREST
  471. const Rml::Colourf border_color(0.f, 0.f);
  472. GLuint framebuffer = 0;
  473. glGenFramebuffers(1, &framebuffer);
  474. glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  475. GLuint color_tex_buffer = 0;
  476. GLuint color_render_buffer = 0;
  477. if (samples > 0)
  478. {
  479. glGenRenderbuffers(1, &color_render_buffer);
  480. glBindRenderbuffer(GL_RENDERBUFFER, color_render_buffer);
  481. glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, color_format, width, height);
  482. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_render_buffer);
  483. }
  484. else
  485. {
  486. glGenTextures(1, &color_tex_buffer);
  487. glBindTexture(GL_TEXTURE_2D, color_tex_buffer);
  488. glTexImage2D(GL_TEXTURE_2D, 0, color_format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  489. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_mag_filter);
  490. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, min_mag_filter);
  491. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode);
  492. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode);
  493. #ifndef RMLUI_PLATFORM_EMSCRIPTEN
  494. glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &border_color[0]);
  495. #endif
  496. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex_buffer, 0);
  497. }
  498. // Create depth/stencil buffer storage attachment.
  499. GLuint depth_stencil_buffer = 0;
  500. if (attachment != FramebufferAttachment::None)
  501. {
  502. if (shared_depth_stencil_buffer)
  503. {
  504. // Share depth/stencil buffer
  505. depth_stencil_buffer = shared_depth_stencil_buffer;
  506. }
  507. else
  508. {
  509. // Create new depth/stencil buffer
  510. glGenRenderbuffers(1, &depth_stencil_buffer);
  511. glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buffer);
  512. const GLenum internal_format = (attachment == FramebufferAttachment::DepthStencil ? GL_DEPTH24_STENCIL8 : GL_DEPTH_COMPONENT24);
  513. glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internal_format, width, height);
  514. }
  515. const GLenum attachment_type = (attachment == FramebufferAttachment::DepthStencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT);
  516. glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment_type, GL_RENDERBUFFER, depth_stencil_buffer);
  517. }
  518. const GLuint framebuffer_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  519. if (framebuffer_status != GL_FRAMEBUFFER_COMPLETE)
  520. {
  521. Rml::Log::Message(Rml::Log::LT_ERROR, "OpenGL framebuffer could not be generated. Error code %x.", framebuffer_status);
  522. return false;
  523. }
  524. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  525. glBindTexture(GL_TEXTURE_2D, 0);
  526. glBindRenderbuffer(GL_RENDERBUFFER, 0);
  527. CheckGLError("CreateFramebuffer");
  528. out_fb = {};
  529. out_fb.width = width;
  530. out_fb.height = height;
  531. out_fb.framebuffer = framebuffer;
  532. out_fb.color_tex_buffer = color_tex_buffer;
  533. out_fb.color_render_buffer = color_render_buffer;
  534. out_fb.depth_stencil_buffer = depth_stencil_buffer;
  535. out_fb.owns_depth_stencil_buffer = !shared_depth_stencil_buffer;
  536. return true;
  537. }
  538. static void DestroyFramebuffer(FramebufferData& fb)
  539. {
  540. if (fb.framebuffer)
  541. glDeleteFramebuffers(1, &fb.framebuffer);
  542. if (fb.color_tex_buffer)
  543. glDeleteTextures(1, &fb.color_tex_buffer);
  544. if (fb.color_render_buffer)
  545. glDeleteRenderbuffers(1, &fb.color_render_buffer);
  546. if (fb.owns_depth_stencil_buffer && fb.depth_stencil_buffer)
  547. glDeleteRenderbuffers(1, &fb.depth_stencil_buffer);
  548. fb = {};
  549. }
  550. static void BindTexture(const FramebufferData& fb)
  551. {
  552. if (!fb.color_tex_buffer)
  553. {
  554. RMLUI_ERRORMSG("Only framebuffers with color textures can be bound as textures. This framebuffer probably uses multisampling which needs a "
  555. "blit step first.");
  556. }
  557. glBindTexture(GL_TEXTURE_2D, fb.color_tex_buffer);
  558. }
  559. static bool CreateShaders(ProgramData& data)
  560. {
  561. RMLUI_ASSERT(std::all_of(data.vert_shaders.begin(), data.vert_shaders.end(), [](auto&& value) { return value == 0; }));
  562. RMLUI_ASSERT(std::all_of(data.frag_shaders.begin(), data.frag_shaders.end(), [](auto&& value) { return value == 0; }));
  563. RMLUI_ASSERT(std::all_of(data.programs.begin(), data.programs.end(), [](auto&& value) { return value == 0; }));
  564. auto ReportError = [](const char* type, const char* name) {
  565. Rml::Log::Message(Rml::Log::LT_ERROR, "Could not create OpenGL %s: '%s'.", type, name);
  566. return false;
  567. };
  568. for (const VertShaderDefinition& def : vert_shader_definitions)
  569. {
  570. if (!CreateShader(data.vert_shaders[def.id], GL_VERTEX_SHADER, def.code_str))
  571. return ReportError("vertex shader", def.name_str);
  572. }
  573. for (const FragShaderDefinition& def : frag_shader_definitions)
  574. {
  575. if (!CreateShader(data.frag_shaders[def.id], GL_FRAGMENT_SHADER, def.code_str))
  576. return ReportError("fragment shader", def.name_str);
  577. }
  578. for (const ProgramDefinition& def : program_definitions)
  579. {
  580. if (!CreateProgram(data.programs[def.id], data.uniforms, def.id, data.vert_shaders[def.vert_shader], data.frag_shaders[def.frag_shader]))
  581. return ReportError("program", def.name_str);
  582. }
  583. glUseProgram(0);
  584. return true;
  585. }
  586. static void DestroyShaders(const ProgramData& data)
  587. {
  588. for (GLuint id : data.programs)
  589. glDeleteProgram(id);
  590. for (GLuint id : data.vert_shaders)
  591. glDeleteShader(id);
  592. for (GLuint id : data.frag_shaders)
  593. glDeleteShader(id);
  594. }
  595. } // namespace Gfx
  596. RenderInterface_GL3::RenderInterface_GL3()
  597. {
  598. auto mut_program_data = Rml::MakeUnique<Gfx::ProgramData>();
  599. if (Gfx::CreateShaders(*mut_program_data))
  600. {
  601. program_data = std::move(mut_program_data);
  602. Rml::Vertex vertices[4];
  603. int indices[6];
  604. Rml::GeometryUtilities::GenerateQuad(vertices, indices, Rml::Vector2f(-1), Rml::Vector2f(2), {});
  605. fullscreen_quad_geometry = RenderInterface_GL3::CompileGeometry(vertices, 4, indices, 6);
  606. }
  607. }
  608. RenderInterface_GL3::~RenderInterface_GL3()
  609. {
  610. if (fullscreen_quad_geometry)
  611. {
  612. RenderInterface_GL3::ReleaseCompiledGeometry(fullscreen_quad_geometry);
  613. fullscreen_quad_geometry = {};
  614. }
  615. if (program_data)
  616. {
  617. Gfx::DestroyShaders(*program_data);
  618. program_data.reset();
  619. }
  620. }
  621. void RenderInterface_GL3::SetViewport(int width, int height)
  622. {
  623. viewport_width = Rml::Math::Max(width, 1);
  624. viewport_height = Rml::Math::Max(height, 1);
  625. projection = Rml::Matrix4f::ProjectOrtho(0, (float)viewport_width, (float)viewport_height, 0, -10000, 10000);
  626. }
  627. void RenderInterface_GL3::BeginFrame()
  628. {
  629. RMLUI_ASSERT(viewport_width >= 1 && viewport_height >= 1);
  630. // Backup GL state.
  631. glstate_backup.enable_cull_face = glIsEnabled(GL_CULL_FACE);
  632. glstate_backup.enable_blend = glIsEnabled(GL_BLEND);
  633. glstate_backup.enable_stencil_test = glIsEnabled(GL_STENCIL_TEST);
  634. glstate_backup.enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
  635. glGetIntegerv(GL_VIEWPORT, glstate_backup.viewport);
  636. glGetIntegerv(GL_SCISSOR_BOX, glstate_backup.scissor);
  637. glGetIntegerv(GL_ACTIVE_TEXTURE, &glstate_backup.active_texture);
  638. glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &glstate_backup.stencil_clear_value);
  639. glGetFloatv(GL_COLOR_CLEAR_VALUE, glstate_backup.color_clear_value);
  640. glGetIntegerv(GL_BLEND_EQUATION_RGB, &glstate_backup.blend_equation_rgb);
  641. glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &glstate_backup.blend_equation_alpha);
  642. glGetIntegerv(GL_BLEND_SRC_RGB, &glstate_backup.blend_src_rgb);
  643. glGetIntegerv(GL_BLEND_DST_RGB, &glstate_backup.blend_dst_rgb);
  644. glGetIntegerv(GL_BLEND_SRC_ALPHA, &glstate_backup.blend_src_alpha);
  645. glGetIntegerv(GL_BLEND_DST_ALPHA, &glstate_backup.blend_dst_alpha);
  646. glGetIntegerv(GL_STENCIL_FUNC, &glstate_backup.stencil_front.func);
  647. glGetIntegerv(GL_STENCIL_REF, &glstate_backup.stencil_front.ref);
  648. glGetIntegerv(GL_STENCIL_VALUE_MASK, &glstate_backup.stencil_front.value_mask);
  649. glGetIntegerv(GL_STENCIL_WRITEMASK, &glstate_backup.stencil_front.writemask);
  650. glGetIntegerv(GL_STENCIL_FAIL, &glstate_backup.stencil_front.fail);
  651. glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &glstate_backup.stencil_front.pass_depth_fail);
  652. glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &glstate_backup.stencil_front.pass_depth_pass);
  653. glGetIntegerv(GL_STENCIL_BACK_FUNC, &glstate_backup.stencil_back.func);
  654. glGetIntegerv(GL_STENCIL_BACK_REF, &glstate_backup.stencil_back.ref);
  655. glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &glstate_backup.stencil_back.value_mask);
  656. glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &glstate_backup.stencil_back.writemask);
  657. glGetIntegerv(GL_STENCIL_BACK_FAIL, &glstate_backup.stencil_back.fail);
  658. glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &glstate_backup.stencil_back.pass_depth_fail);
  659. glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &glstate_backup.stencil_back.pass_depth_pass);
  660. // Setup expected GL state.
  661. glViewport(0, 0, viewport_width, viewport_height);
  662. glClearStencil(0);
  663. glClearColor(0, 0, 0, 0);
  664. glActiveTexture(GL_TEXTURE0);
  665. glDisable(GL_SCISSOR_TEST);
  666. glDisable(GL_CULL_FACE);
  667. glEnable(GL_BLEND);
  668. glBlendEquation(GL_FUNC_ADD);
  669. #if RMLUI_PREMULTIPLIED_ALPHA
  670. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  671. #else
  672. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  673. #endif
  674. #ifndef RMLUI_PLATFORM_EMSCRIPTEN
  675. // We do blending in nonlinear sRGB space because that is the common practice and gives results that we are used to.
  676. glDisable(GL_FRAMEBUFFER_SRGB);
  677. #endif
  678. glEnable(GL_STENCIL_TEST);
  679. glStencilFunc(GL_ALWAYS, 1, GLuint(-1));
  680. glStencilMask(GLuint(-1));
  681. glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
  682. SetTransform(nullptr);
  683. render_layers.BeginFrame(viewport_width, viewport_height);
  684. glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetTopLayer().framebuffer);
  685. glClear(GL_COLOR_BUFFER_BIT);
  686. UseProgram(ProgramId::None);
  687. program_transform_dirty.set();
  688. scissor_state = Rml::Rectanglei::MakeInvalid();
  689. Gfx::CheckGLError("BeginFrame");
  690. }
  691. void RenderInterface_GL3::EndFrame()
  692. {
  693. const Gfx::FramebufferData& fb_active = render_layers.GetTopLayer();
  694. const Gfx::FramebufferData& fb_postprocess = render_layers.GetPostprocessPrimary();
  695. // Resolve MSAA to postprocess framebuffer.
  696. glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_active.framebuffer);
  697. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_postprocess.framebuffer);
  698. glBlitFramebuffer(0, 0, fb_active.width, fb_active.height, 0, 0, fb_postprocess.width, fb_postprocess.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
  699. // Draw to backbuffer
  700. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  701. // Assuming we have an opaque background, we can just write to it with the premultiplied alpha blend mode and we'll get the correct result.
  702. // Instead, if we had a transparent destination that didn't use pre-multiplied alpha, we would need to perform a manual un-premultiplication step.
  703. glActiveTexture(GL_TEXTURE0);
  704. Gfx::BindTexture(fb_postprocess);
  705. UseProgram(ProgramId::Passthrough);
  706. DrawFullscreenQuad();
  707. render_layers.EndFrame();
  708. // Restore GL state.
  709. if (glstate_backup.enable_cull_face)
  710. glEnable(GL_CULL_FACE);
  711. else
  712. glDisable(GL_CULL_FACE);
  713. if (glstate_backup.enable_blend)
  714. glEnable(GL_BLEND);
  715. else
  716. glDisable(GL_BLEND);
  717. if (glstate_backup.enable_stencil_test)
  718. glEnable(GL_STENCIL_TEST);
  719. else
  720. glDisable(GL_STENCIL_TEST);
  721. if (glstate_backup.enable_scissor_test)
  722. glEnable(GL_SCISSOR_TEST);
  723. else
  724. glDisable(GL_SCISSOR_TEST);
  725. glViewport(glstate_backup.viewport[0], glstate_backup.viewport[1], glstate_backup.viewport[2], glstate_backup.viewport[3]);
  726. glScissor(glstate_backup.scissor[0], glstate_backup.scissor[1], glstate_backup.scissor[2], glstate_backup.scissor[3]);
  727. glActiveTexture(glstate_backup.active_texture);
  728. glClearStencil(glstate_backup.stencil_clear_value);
  729. glClearColor(glstate_backup.color_clear_value[0], glstate_backup.color_clear_value[1], glstate_backup.color_clear_value[2],
  730. glstate_backup.color_clear_value[3]);
  731. glBlendEquationSeparate(glstate_backup.blend_equation_rgb, glstate_backup.blend_equation_alpha);
  732. glBlendFuncSeparate(glstate_backup.blend_src_rgb, glstate_backup.blend_dst_rgb, glstate_backup.blend_src_alpha, glstate_backup.blend_dst_alpha);
  733. glStencilFuncSeparate(GL_FRONT, glstate_backup.stencil_front.func, glstate_backup.stencil_front.ref, glstate_backup.stencil_front.value_mask);
  734. glStencilMaskSeparate(GL_FRONT, glstate_backup.stencil_front.writemask);
  735. glStencilOpSeparate(GL_FRONT, glstate_backup.stencil_front.fail, glstate_backup.stencil_front.pass_depth_fail,
  736. glstate_backup.stencil_front.pass_depth_pass);
  737. glStencilFuncSeparate(GL_BACK, glstate_backup.stencil_back.func, glstate_backup.stencil_back.ref, glstate_backup.stencil_back.value_mask);
  738. glStencilMaskSeparate(GL_BACK, glstate_backup.stencil_back.writemask);
  739. glStencilOpSeparate(GL_BACK, glstate_backup.stencil_back.fail, glstate_backup.stencil_back.pass_depth_fail,
  740. glstate_backup.stencil_back.pass_depth_pass);
  741. Gfx::CheckGLError("EndFrame");
  742. }
  743. void RenderInterface_GL3::Clear()
  744. {
  745. glClearColor(0, 0, 0, 1);
  746. glClear(GL_COLOR_BUFFER_BIT);
  747. }
  748. void RenderInterface_GL3::RenderGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices, const Rml::TextureHandle texture,
  749. const Rml::Vector2f& translation)
  750. {
  751. Rml::CompiledGeometryHandle geometry = CompileGeometry(vertices, num_vertices, indices, num_indices);
  752. if (geometry)
  753. {
  754. RenderCompiledGeometry(geometry, translation, texture);
  755. ReleaseCompiledGeometry(geometry);
  756. }
  757. }
  758. Rml::CompiledGeometryHandle RenderInterface_GL3::CompileGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices)
  759. {
  760. constexpr GLenum draw_usage = GL_STATIC_DRAW;
  761. GLuint vao = 0;
  762. GLuint vbo = 0;
  763. GLuint ibo = 0;
  764. glGenVertexArrays(1, &vao);
  765. glGenBuffers(1, &vbo);
  766. glGenBuffers(1, &ibo);
  767. glBindVertexArray(vao);
  768. glBindBuffer(GL_ARRAY_BUFFER, vbo);
  769. glBufferData(GL_ARRAY_BUFFER, sizeof(Rml::Vertex) * num_vertices, (const void*)vertices, draw_usage);
  770. glEnableVertexAttribArray((GLuint)Gfx::VertexAttribute::Position);
  771. glVertexAttribPointer((GLuint)Gfx::VertexAttribute::Position, 2, GL_FLOAT, GL_FALSE, sizeof(Rml::Vertex),
  772. (const GLvoid*)(offsetof(Rml::Vertex, position)));
  773. glEnableVertexAttribArray((GLuint)Gfx::VertexAttribute::Color0);
  774. glVertexAttribPointer((GLuint)Gfx::VertexAttribute::Color0, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Rml::Vertex),
  775. (const GLvoid*)(offsetof(Rml::Vertex, colour)));
  776. glEnableVertexAttribArray((GLuint)Gfx::VertexAttribute::TexCoord0);
  777. glVertexAttribPointer((GLuint)Gfx::VertexAttribute::TexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Rml::Vertex),
  778. (const GLvoid*)(offsetof(Rml::Vertex, tex_coord)));
  779. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
  780. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * num_indices, (const void*)indices, draw_usage);
  781. glBindVertexArray(0);
  782. glBindBuffer(GL_ARRAY_BUFFER, 0);
  783. Gfx::CheckGLError("CompileGeometry");
  784. Gfx::CompiledGeometryData* geometry = new Gfx::CompiledGeometryData;
  785. geometry->vao = vao;
  786. geometry->vbo = vbo;
  787. geometry->ibo = ibo;
  788. geometry->draw_count = num_indices;
  789. return (Rml::CompiledGeometryHandle)geometry;
  790. }
  791. void RenderInterface_GL3::RenderCompiledGeometry(Rml::CompiledGeometryHandle handle, const Rml::Vector2f& translation, Rml::TextureHandle texture)
  792. {
  793. Gfx::CompiledGeometryData* geometry = (Gfx::CompiledGeometryData*)handle;
  794. if (texture == TexturePostprocess)
  795. {
  796. // Do nothing.
  797. }
  798. else if (texture)
  799. {
  800. UseProgram(ProgramId::Texture);
  801. SubmitTransformUniform(translation);
  802. if (texture != TextureEnableWithoutBinding)
  803. glBindTexture(GL_TEXTURE_2D, (GLuint)texture);
  804. }
  805. else
  806. {
  807. UseProgram(ProgramId::Color);
  808. glBindTexture(GL_TEXTURE_2D, 0);
  809. SubmitTransformUniform(translation);
  810. }
  811. glBindVertexArray(geometry->vao);
  812. glDrawElements(GL_TRIANGLES, geometry->draw_count, GL_UNSIGNED_INT, (const GLvoid*)0);
  813. glBindVertexArray(0);
  814. glBindTexture(GL_TEXTURE_2D, 0);
  815. Gfx::CheckGLError("RenderCompiledGeometry");
  816. }
  817. void RenderInterface_GL3::ReleaseCompiledGeometry(Rml::CompiledGeometryHandle handle)
  818. {
  819. Gfx::CompiledGeometryData* geometry = (Gfx::CompiledGeometryData*)handle;
  820. glDeleteVertexArrays(1, &geometry->vao);
  821. glDeleteBuffers(1, &geometry->vbo);
  822. glDeleteBuffers(1, &geometry->ibo);
  823. delete geometry;
  824. }
  825. /// Flip vertical axis of the rectangle, and move its origin to the vertically opposite side of the viewport.
  826. /// @note Changes coordinate system from RmlUi to OpenGL, or equivalently in reverse.
  827. /// @note The Rectangle::Top and Rectangle::Bottom members will have reverse meaning in the returned rectangle.
  828. static Rml::Rectanglei VerticallyFlipped(Rml::Rectanglei rect, int viewport_height)
  829. {
  830. RMLUI_ASSERT(rect.Valid());
  831. Rml::Rectanglei flipped_rect = rect;
  832. flipped_rect.p0.y = viewport_height - rect.p1.y;
  833. flipped_rect.p1.y = viewport_height - rect.p0.y;
  834. return flipped_rect;
  835. }
  836. void RenderInterface_GL3::SetScissor(Rml::Rectanglei region, bool vertically_flip)
  837. {
  838. if (region.Valid() != scissor_state.Valid())
  839. {
  840. if (region.Valid())
  841. glEnable(GL_SCISSOR_TEST);
  842. else
  843. glDisable(GL_SCISSOR_TEST);
  844. }
  845. if (region.Valid() && vertically_flip)
  846. region = VerticallyFlipped(region, viewport_height);
  847. if (region.Valid() && region != scissor_state)
  848. glScissor(region.Left(), viewport_height - region.Bottom(), region.Width(), region.Height());
  849. Gfx::CheckGLError("SetScissorRegion");
  850. scissor_state = region;
  851. }
  852. void RenderInterface_GL3::EnableScissorRegion(bool enable)
  853. {
  854. // Assume enable is immediately followed by a SetScissorRegion() call, and ignore it here.
  855. if (!enable)
  856. SetScissor(Rml::Rectanglei::MakeInvalid(), false);
  857. }
  858. void RenderInterface_GL3::SetScissorRegion(int x, int y, int width, int height)
  859. {
  860. SetScissor(Rml::Rectanglei::FromPositionSize({x, y}, {width, height}));
  861. }
  862. void RenderInterface_GL3::EnableClipMask(bool enable)
  863. {
  864. if (enable)
  865. glEnable(GL_STENCIL_TEST);
  866. else
  867. glDisable(GL_STENCIL_TEST);
  868. }
  869. void RenderInterface_GL3::RenderToClipMask(Rml::ClipMaskOperation operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation)
  870. {
  871. RMLUI_ASSERT(glIsEnabled(GL_STENCIL_TEST));
  872. using Rml::ClipMaskOperation;
  873. const bool clear_stencil = (operation == ClipMaskOperation::Set || operation == ClipMaskOperation::SetInverse);
  874. if (clear_stencil)
  875. {
  876. // @performance Increment the reference value instead of clearing each time.
  877. glClear(GL_STENCIL_BUFFER_BIT);
  878. }
  879. GLint stencil_test_value = 0;
  880. glGetIntegerv(GL_STENCIL_REF, &stencil_test_value);
  881. glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
  882. glStencilFunc(GL_ALWAYS, GLint(1), GLuint(-1));
  883. switch (operation)
  884. {
  885. case ClipMaskOperation::Set:
  886. {
  887. glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
  888. stencil_test_value = 1;
  889. }
  890. break;
  891. case ClipMaskOperation::SetInverse:
  892. {
  893. glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
  894. stencil_test_value = 0;
  895. }
  896. break;
  897. case ClipMaskOperation::Intersect:
  898. {
  899. glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
  900. stencil_test_value += 1;
  901. }
  902. break;
  903. }
  904. RenderCompiledGeometry(geometry, translation, {});
  905. // Restore state
  906. // @performance Cache state so we don't toggle it unnecessarily.
  907. glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  908. glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
  909. glStencilFunc(GL_EQUAL, stencil_test_value, GLuint(-1));
  910. }
  911. // Set to byte packing, or the compiler will expand our struct, which means it won't read correctly from file
  912. #pragma pack(1)
  913. struct TGAHeader {
  914. char idLength;
  915. char colourMapType;
  916. char dataType;
  917. short int colourMapOrigin;
  918. short int colourMapLength;
  919. char colourMapDepth;
  920. short int xOrigin;
  921. short int yOrigin;
  922. short int width;
  923. short int height;
  924. char bitsPerPixel;
  925. char imageDescriptor;
  926. };
  927. // Restore packing
  928. #pragma pack()
  929. bool RenderInterface_GL3::LoadTexture(Rml::TextureHandle& texture_handle, Rml::Vector2i& texture_dimensions, const Rml::String& source)
  930. {
  931. Rml::FileInterface* file_interface = Rml::GetFileInterface();
  932. Rml::FileHandle file_handle = file_interface->Open(source);
  933. if (!file_handle)
  934. {
  935. return false;
  936. }
  937. file_interface->Seek(file_handle, 0, SEEK_END);
  938. size_t buffer_size = file_interface->Tell(file_handle);
  939. file_interface->Seek(file_handle, 0, SEEK_SET);
  940. if (buffer_size <= sizeof(TGAHeader))
  941. {
  942. Rml::Log::Message(Rml::Log::LT_ERROR, "Texture file size is smaller than TGAHeader, file is not a valid TGA image.");
  943. file_interface->Close(file_handle);
  944. return false;
  945. }
  946. using Rml::byte;
  947. byte* buffer = new byte[buffer_size];
  948. file_interface->Read(buffer, buffer_size, file_handle);
  949. file_interface->Close(file_handle);
  950. TGAHeader header;
  951. memcpy(&header, buffer, sizeof(TGAHeader));
  952. int color_mode = header.bitsPerPixel / 8;
  953. int image_size = header.width * header.height * 4; // We always make 32bit textures
  954. if (header.dataType != 2)
  955. {
  956. Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24/32bit uncompressed TGAs are supported.");
  957. delete[] buffer;
  958. return false;
  959. }
  960. // Ensure we have at least 3 colors
  961. if (color_mode < 3)
  962. {
  963. Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24 and 32bit textures are supported.");
  964. delete[] buffer;
  965. return false;
  966. }
  967. const byte* image_src = buffer + sizeof(TGAHeader);
  968. byte* image_dest = new byte[image_size];
  969. // Targa is BGR, swap to RGB and flip Y axis
  970. for (long y = 0; y < header.height; y++)
  971. {
  972. long read_index = y * header.width * color_mode;
  973. long write_index = ((header.imageDescriptor & 32) != 0) ? read_index : (header.height - y - 1) * header.width * 4;
  974. for (long x = 0; x < header.width; x++)
  975. {
  976. image_dest[write_index] = image_src[read_index + 2];
  977. image_dest[write_index + 1] = image_src[read_index + 1];
  978. image_dest[write_index + 2] = image_src[read_index];
  979. if (color_mode == 4)
  980. image_dest[write_index + 3] = image_src[read_index + 3];
  981. else
  982. image_dest[write_index + 3] = 255;
  983. write_index += 4;
  984. read_index += color_mode;
  985. }
  986. }
  987. texture_dimensions.x = header.width;
  988. texture_dimensions.y = header.height;
  989. bool success = GenerateTexture(texture_handle, image_dest, texture_dimensions);
  990. delete[] image_dest;
  991. delete[] buffer;
  992. return success;
  993. }
  994. bool RenderInterface_GL3::GenerateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, const Rml::Vector2i& source_dimensions)
  995. {
  996. GLuint texture_id = 0;
  997. glGenTextures(1, &texture_id);
  998. if (texture_id == 0)
  999. {
  1000. Rml::Log::Message(Rml::Log::LT_ERROR, "Failed to generate texture.");
  1001. return false;
  1002. }
  1003. #if RMLUI_PREMULTIPLIED_ALPHA
  1004. using Rml::byte;
  1005. Rml::UniquePtr<byte[]> source_premultiplied;
  1006. if (source)
  1007. {
  1008. const size_t num_bytes = source_dimensions.x * source_dimensions.y * 4;
  1009. source_premultiplied = Rml::UniquePtr<byte[]>(new byte[num_bytes]);
  1010. for (size_t i = 0; i < num_bytes; i += 4)
  1011. {
  1012. const byte alpha = source[i + 3];
  1013. for (size_t j = 0; j < 3; j++)
  1014. source_premultiplied[i + j] = byte((int(source[i + j]) * int(alpha)) / 255);
  1015. source_premultiplied[i + 3] = alpha;
  1016. }
  1017. source = source_premultiplied.get();
  1018. }
  1019. #endif
  1020. glBindTexture(GL_TEXTURE_2D, texture_id);
  1021. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, source_dimensions.x, source_dimensions.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, source);
  1022. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  1023. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  1024. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  1025. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  1026. texture_handle = (Rml::TextureHandle)texture_id;
  1027. glBindTexture(GL_TEXTURE_2D, 0);
  1028. return true;
  1029. }
  1030. void RenderInterface_GL3::DrawFullscreenQuad()
  1031. {
  1032. RenderCompiledGeometry(fullscreen_quad_geometry, {}, RenderInterface_GL3::TexturePostprocess);
  1033. }
  1034. void RenderInterface_GL3::DrawFullscreenQuad(Rml::Vector2f uv_offset, Rml::Vector2f uv_scaling)
  1035. {
  1036. Rml::Vertex vertices[4];
  1037. int indices[6];
  1038. Rml::GeometryUtilities::GenerateQuad(vertices, indices, Rml::Vector2f(-1), Rml::Vector2f(2), {});
  1039. if (uv_offset != Rml::Vector2f() || uv_scaling != Rml::Vector2f(1.f))
  1040. {
  1041. for (Rml::Vertex& vertex : vertices)
  1042. vertex.tex_coord = (vertex.tex_coord * uv_scaling) + uv_offset;
  1043. }
  1044. RenderGeometry(vertices, 4, indices, 6, RenderInterface_GL3::TexturePostprocess, {});
  1045. }
  1046. static Rml::Colourf ToPremultipliedAlpha(Rml::Colourb c0)
  1047. {
  1048. Rml::Colourf result;
  1049. result.alpha = (1.f / 255.f) * float(c0.alpha);
  1050. result.red = (1.f / 255.f) * float(c0.red) * result.alpha;
  1051. result.green = (1.f / 255.f) * float(c0.green) * result.alpha;
  1052. result.blue = (1.f / 255.f) * float(c0.blue) * result.alpha;
  1053. return result;
  1054. }
  1055. static void SigmaToParameters(const float desired_sigma, int& out_pass_level, float& out_sigma)
  1056. {
  1057. constexpr int max_num_passes = 10;
  1058. static_assert(max_num_passes < 31, "");
  1059. constexpr float max_single_pass_sigma = 3.0f;
  1060. out_pass_level = Rml::Math::Clamp(Rml::Math::Log2(int(desired_sigma * (2.f / max_single_pass_sigma))), 0, max_num_passes);
  1061. out_sigma = Rml::Math::Clamp(desired_sigma / float(1 << out_pass_level), 0.0f, max_single_pass_sigma);
  1062. }
  1063. static void SetTexCoordLimits(GLint tex_coord_min_location, GLint tex_coord_max_location, Rml::Rectanglei rectangle_flipped,
  1064. Rml::Vector2i framebuffer_size)
  1065. {
  1066. // Offset by half-texel values so that texture lookups are clamped to fragment centers, thereby avoiding color
  1067. // bleeding from neighboring texels due to bilinear interpolation.
  1068. const Rml::Vector2f min = (Rml::Vector2f(rectangle_flipped.p0) + Rml::Vector2f(0.5f)) / Rml::Vector2f(framebuffer_size);
  1069. const Rml::Vector2f max = (Rml::Vector2f(rectangle_flipped.p1) - Rml::Vector2f(0.5f)) / Rml::Vector2f(framebuffer_size);
  1070. glUniform2f(tex_coord_min_location, min.x, min.y);
  1071. glUniform2f(tex_coord_max_location, max.x, max.y);
  1072. }
  1073. static void SetBlurWeights(GLint weights_location, float sigma)
  1074. {
  1075. constexpr int num_weights = BLUR_NUM_WEIGHTS;
  1076. float weights[num_weights];
  1077. float normalization = 0.0f;
  1078. for (int i = 0; i < num_weights; i++)
  1079. {
  1080. if (Rml::Math::Absolute(sigma) < 0.1f)
  1081. weights[i] = float(i == 0);
  1082. else
  1083. weights[i] = Rml::Math::Exp(-float(i * i) / (2.0f * sigma * sigma)) / (Rml::Math::SquareRoot(2.f * Rml::Math::RMLUI_PI) * sigma);
  1084. normalization += (i == 0 ? 1.f : 2.0f) * weights[i];
  1085. }
  1086. for (int i = 0; i < num_weights; i++)
  1087. weights[i] /= normalization;
  1088. glUniform1fv(weights_location, (GLsizei)num_weights, &weights[0]);
  1089. }
  1090. void RenderInterface_GL3::RenderBlur(float sigma, const Gfx::FramebufferData& source_destination, const Gfx::FramebufferData& temp,
  1091. const Rml::Rectanglei window_flipped)
  1092. {
  1093. RMLUI_ASSERT(&source_destination != &temp && source_destination.width == temp.width && source_destination.height == temp.height);
  1094. RMLUI_ASSERT(window_flipped.Valid());
  1095. int pass_level = 0;
  1096. SigmaToParameters(sigma, pass_level, sigma);
  1097. const Rml::Rectanglei original_scissor = scissor_state;
  1098. // Begin by downscaling so that the blur pass can be done at a reduced resolution for large sigma.
  1099. Rml::Rectanglei scissor = window_flipped;
  1100. UseProgram(ProgramId::Passthrough);
  1101. SetScissor(scissor, true);
  1102. // Downscale by iterative half-scaling with bilinear filtering, to reduce aliasing.
  1103. glViewport(0, 0, source_destination.width / 2, source_destination.height / 2);
  1104. // Scale UVs if we have even dimensions, such that texture fetches align perfectly between texels, thereby producing a 50% blend of
  1105. // neighboring texels.
  1106. const Rml::Vector2f uv_scaling = {(source_destination.width % 2 == 1) ? (1.f - 1.f / float(source_destination.width)) : 1.f,
  1107. (source_destination.height % 2 == 1) ? (1.f - 1.f / float(source_destination.height)) : 1.f};
  1108. for (int i = 0; i < pass_level; i++)
  1109. {
  1110. scissor.p0 = (scissor.p0 + Rml::Vector2i(1)) / 2;
  1111. scissor.p1 = Rml::Math::Max(scissor.p1 / 2, scissor.p0);
  1112. const bool from_source = (i % 2 == 0);
  1113. Gfx::BindTexture(from_source ? source_destination : temp);
  1114. glBindFramebuffer(GL_FRAMEBUFFER, (from_source ? temp : source_destination).framebuffer);
  1115. SetScissor(scissor, true);
  1116. DrawFullscreenQuad({}, uv_scaling);
  1117. }
  1118. glViewport(0, 0, source_destination.width, source_destination.height);
  1119. // Ensure texture data end up in the temp buffer. Depending on the last downscaling, we might need to move it from the source_destination buffer.
  1120. const bool transfer_to_temp_buffer = (pass_level % 2 == 0);
  1121. if (transfer_to_temp_buffer)
  1122. {
  1123. Gfx::BindTexture(source_destination);
  1124. glBindFramebuffer(GL_FRAMEBUFFER, temp.framebuffer);
  1125. DrawFullscreenQuad();
  1126. }
  1127. // Set up uniforms.
  1128. UseProgram(ProgramId::Blur);
  1129. SetBlurWeights(GetUniformLocation(UniformId::Weights), sigma);
  1130. SetTexCoordLimits(GetUniformLocation(UniformId::TexCoordMin), GetUniformLocation(UniformId::TexCoordMax), scissor,
  1131. {source_destination.width, source_destination.height});
  1132. const GLint texel_offset_location = GetUniformLocation(UniformId::TexelOffset);
  1133. auto SetTexelOffset = [texel_offset_location](Rml::Vector2f blur_direction, int texture_dimension) {
  1134. const Rml::Vector2f texel_offset = blur_direction * (1.0f / float(texture_dimension));
  1135. glUniform2f(texel_offset_location, texel_offset.x, texel_offset.y);
  1136. };
  1137. // Blur render pass - vertical.
  1138. Gfx::BindTexture(temp);
  1139. glBindFramebuffer(GL_FRAMEBUFFER, source_destination.framebuffer);
  1140. SetTexelOffset({0.f, 1.f}, temp.height);
  1141. DrawFullscreenQuad();
  1142. // Blur render pass - horizontal.
  1143. Gfx::BindTexture(source_destination);
  1144. glBindFramebuffer(GL_FRAMEBUFFER, temp.framebuffer);
  1145. SetTexelOffset({1.f, 0.f}, source_destination.width);
  1146. DrawFullscreenQuad();
  1147. // Blit the blurred image to the scissor region with upscaling.
  1148. SetScissor(window_flipped, true);
  1149. glBindFramebuffer(GL_READ_FRAMEBUFFER, temp.framebuffer);
  1150. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, source_destination.framebuffer);
  1151. const Rml::Vector2i src_min = scissor.p0;
  1152. const Rml::Vector2i src_max = scissor.p1;
  1153. const Rml::Vector2i dst_min = window_flipped.p0;
  1154. const Rml::Vector2i dst_max = window_flipped.p1;
  1155. glBlitFramebuffer(src_min.x, src_min.y, src_max.x, src_max.y, dst_min.x, dst_min.y, dst_max.x, dst_max.y, GL_COLOR_BUFFER_BIT, GL_LINEAR);
  1156. // The above upscale blit might be jittery at low resolutions (large pass levels). This is especially noticable when moving an element with
  1157. // backdrop blur around or when trying to click/hover an element within a blurred region since it may be rendered at an offset. For more stable
  1158. // and accurate rendering we next upscale the blur image by an exact power-of-two. However, this may not fill the edges completely so we need to
  1159. // do the above first. Note that this strategy may sometimes result in visible seams. Alternatively, we could try to enlargen the window to the
  1160. // next power-of-two size and then downsample and blur that.
  1161. const Rml::Vector2i target_min = src_min * (1 << pass_level);
  1162. const Rml::Vector2i target_max = src_max * (1 << pass_level);
  1163. if (target_min != dst_min || target_max != dst_max)
  1164. {
  1165. glBlitFramebuffer(src_min.x, src_min.y, src_max.x, src_max.y, target_min.x, target_min.y, target_max.x, target_max.y, GL_COLOR_BUFFER_BIT,
  1166. GL_LINEAR);
  1167. }
  1168. // Restore render state.
  1169. SetScissor(original_scissor);
  1170. Gfx::CheckGLError("Blur");
  1171. }
  1172. void RenderInterface_GL3::ReleaseTexture(Rml::TextureHandle texture_handle)
  1173. {
  1174. glDeleteTextures(1, (GLuint*)&texture_handle);
  1175. }
  1176. void RenderInterface_GL3::SetTransform(const Rml::Matrix4f* new_transform)
  1177. {
  1178. transform = (new_transform ? (projection * (*new_transform)) : projection);
  1179. program_transform_dirty.set();
  1180. }
  1181. enum class FilterType { Invalid = 0, Passthrough, Blur, DropShadow, ColorMatrix };
  1182. struct CompiledFilter {
  1183. FilterType type;
  1184. // Passthrough
  1185. float blend_factor;
  1186. // Blur
  1187. float sigma;
  1188. // Drop shadow
  1189. Rml::Vector2f offset;
  1190. Rml::Colourb color;
  1191. // ColorMatrix
  1192. Rml::Matrix4f color_matrix;
  1193. };
  1194. Rml::CompiledFilterHandle RenderInterface_GL3::CompileFilter(const Rml::String& name, const Rml::Dictionary& parameters)
  1195. {
  1196. CompiledFilter filter = {};
  1197. if (name == "opacity")
  1198. {
  1199. filter.type = FilterType::Passthrough;
  1200. filter.blend_factor = Rml::Get(parameters, "value", 1.0f);
  1201. }
  1202. else if (name == "blur")
  1203. {
  1204. filter.type = FilterType::Blur;
  1205. filter.sigma = 0.5f * Rml::Get(parameters, "radius", 1.0f);
  1206. }
  1207. else if (name == "drop-shadow")
  1208. {
  1209. filter.type = FilterType::DropShadow;
  1210. filter.sigma = Rml::Get(parameters, "sigma", 0.f);
  1211. filter.color = Rml::Get(parameters, "color", Rml::Colourb());
  1212. filter.offset = Rml::Get(parameters, "offset", Rml::Vector2f(0.f));
  1213. }
  1214. else if (name == "brightness")
  1215. {
  1216. filter.type = FilterType::ColorMatrix;
  1217. const float value = Rml::Get(parameters, "value", 1.0f);
  1218. filter.color_matrix = Rml::Matrix4f::Diag(value, value, value, 1.f);
  1219. }
  1220. else if (name == "contrast")
  1221. {
  1222. filter.type = FilterType::ColorMatrix;
  1223. const float value = Rml::Get(parameters, "value", 1.0f);
  1224. const float grayness = 0.5f - 0.5f * value;
  1225. filter.color_matrix = Rml::Matrix4f::Diag(value, value, value, 1.f);
  1226. filter.color_matrix.SetColumn(3, Rml::Vector4f(grayness, grayness, grayness, 1.f));
  1227. }
  1228. else if (name == "invert")
  1229. {
  1230. filter.type = FilterType::ColorMatrix;
  1231. const float value = Rml::Math::Clamp(Rml::Get(parameters, "value", 1.0f), 0.f, 1.f);
  1232. const float inverted = 1.f - 2.f * value;
  1233. filter.color_matrix = Rml::Matrix4f::Diag(inverted, inverted, inverted, 1.f);
  1234. filter.color_matrix.SetColumn(3, Rml::Vector4f(value, value, value, 1.f));
  1235. }
  1236. else if (name == "grayscale")
  1237. {
  1238. filter.type = FilterType::ColorMatrix;
  1239. const float value = Rml::Get(parameters, "value", 1.0f);
  1240. const float rev_value = 1.f - value;
  1241. const Rml::Vector3f gray = value * Rml::Vector3f(0.2126f, 0.7152f, 0.0722f);
  1242. // clang-format off
  1243. filter.color_matrix = Rml::Matrix4f::FromRows(
  1244. {gray.x + rev_value, gray.y, gray.z, 0.f},
  1245. {gray.x, gray.y + rev_value, gray.z, 0.f},
  1246. {gray.x, gray.y, gray.z + rev_value, 0.f},
  1247. {0.f, 0.f, 0.f, 1.f}
  1248. );
  1249. // clang-format on
  1250. }
  1251. else if (name == "sepia")
  1252. {
  1253. filter.type = FilterType::ColorMatrix;
  1254. const float value = Rml::Get(parameters, "value", 1.0f);
  1255. const float rev_value = 1.f - value;
  1256. const Rml::Vector3f r_mix = value * Rml::Vector3f(0.393f, 0.769f, 0.189f);
  1257. const Rml::Vector3f g_mix = value * Rml::Vector3f(0.349f, 0.686f, 0.168f);
  1258. const Rml::Vector3f b_mix = value * Rml::Vector3f(0.272f, 0.534f, 0.131f);
  1259. // clang-format off
  1260. filter.color_matrix = Rml::Matrix4f::FromRows(
  1261. {r_mix.x + rev_value, r_mix.y, r_mix.z, 0.f},
  1262. {g_mix.x, g_mix.y + rev_value, g_mix.z, 0.f},
  1263. {b_mix.x, b_mix.y, b_mix.z + rev_value, 0.f},
  1264. {0.f, 0.f, 0.f, 1.f}
  1265. );
  1266. // clang-format on
  1267. }
  1268. else if (name == "hue-rotate")
  1269. {
  1270. // Hue-rotation and saturation values based on: https://www.w3.org/TR/filter-effects-1/#attr-valuedef-type-huerotate
  1271. filter.type = FilterType::ColorMatrix;
  1272. const float value = Rml::Get(parameters, "value", 1.0f);
  1273. const float s = Rml::Math::Sin(value);
  1274. const float c = Rml::Math::Cos(value);
  1275. // clang-format off
  1276. filter.color_matrix = Rml::Matrix4f::FromRows(
  1277. {0.213f + 0.787f * c - 0.213f * s, 0.715f - 0.715f * c - 0.715f * s, 0.072f - 0.072f * c + 0.928f * s, 0.f},
  1278. {0.213f - 0.213f * c + 0.143f * s, 0.715f + 0.285f * c + 0.140f * s, 0.072f - 0.072f * c - 0.283f * s, 0.f},
  1279. {0.213f - 0.213f * c - 0.787f * s, 0.715f - 0.715f * c + 0.715f * s, 0.072f + 0.928f * c + 0.072f * s, 0.f},
  1280. {0.f, 0.f, 0.f, 1.f}
  1281. );
  1282. // clang-format on
  1283. }
  1284. else if (name == "saturate")
  1285. {
  1286. filter.type = FilterType::ColorMatrix;
  1287. const float value = Rml::Get(parameters, "value", 1.0f);
  1288. // clang-format off
  1289. filter.color_matrix = Rml::Matrix4f::FromRows(
  1290. {0.213f + 0.787f * value, 0.715f - 0.715f * value, 0.072f - 0.072f * value, 0.f},
  1291. {0.213f - 0.213f * value, 0.715f + 0.285f * value, 0.072f - 0.072f * value, 0.f},
  1292. {0.213f - 0.213f * value, 0.715f - 0.715f * value, 0.072f + 0.928f * value, 0.f},
  1293. {0.f, 0.f, 0.f, 1.f}
  1294. );
  1295. // clang-format on
  1296. }
  1297. if (filter.type != FilterType::Invalid)
  1298. return reinterpret_cast<Rml::CompiledFilterHandle>(new CompiledFilter(std::move(filter)));
  1299. Rml::Log::Message(Rml::Log::LT_WARNING, "Unsupported filter type '%s'.", name.c_str());
  1300. return {};
  1301. }
  1302. void RenderInterface_GL3::ReleaseCompiledFilter(Rml::CompiledFilterHandle filter)
  1303. {
  1304. delete reinterpret_cast<CompiledFilter*>(filter);
  1305. }
  1306. enum class CompiledShaderType { Invalid = 0, Gradient };
  1307. struct CompiledShader {
  1308. CompiledShaderType type;
  1309. // Gradient
  1310. ShaderGradientFunction gradient_function;
  1311. Rml::Vector2f p;
  1312. Rml::Vector2f v;
  1313. Rml::Vector<float> stop_positions;
  1314. Rml::Vector<Rml::Colourf> stop_colors;
  1315. };
  1316. Rml::CompiledShaderHandle RenderInterface_GL3::CompileShader(const Rml::String& name, const Rml::Dictionary& parameters)
  1317. {
  1318. auto ApplyColorStopList = [](CompiledShader& shader, const Rml::Dictionary& shader_parameters) {
  1319. auto it = shader_parameters.find("color_stop_list");
  1320. RMLUI_ASSERT(it != shader_parameters.end() && it->second.GetType() == Rml::Variant::COLORSTOPLIST);
  1321. const Rml::ColorStopList& color_stop_list = it->second.GetReference<Rml::ColorStopList>();
  1322. const int num_stops = Rml::Math::Min((int)color_stop_list.size(), MAX_NUM_STOPS);
  1323. shader.stop_positions.resize(num_stops);
  1324. shader.stop_colors.resize(num_stops);
  1325. for (int i = 0; i < num_stops; i++)
  1326. {
  1327. const Rml::ColorStop& stop = color_stop_list[i];
  1328. RMLUI_ASSERT(stop.position.unit == Rml::Unit::NUMBER);
  1329. shader.stop_positions[i] = stop.position.number;
  1330. shader.stop_colors[i] = ToPremultipliedAlpha(stop.color);
  1331. }
  1332. };
  1333. CompiledShader shader = {};
  1334. if (name == "linear-gradient")
  1335. {
  1336. shader.type = CompiledShaderType::Gradient;
  1337. const bool repeating = Rml::Get(parameters, "repeating", false);
  1338. shader.gradient_function = (repeating ? ShaderGradientFunction::RepeatingLinear : ShaderGradientFunction::Linear);
  1339. shader.p = Rml::Get(parameters, "p0", Rml::Vector2f(0.f));
  1340. shader.v = Rml::Get(parameters, "p1", Rml::Vector2f(0.f)) - shader.p;
  1341. ApplyColorStopList(shader, parameters);
  1342. }
  1343. if (shader.type != CompiledShaderType::Invalid)
  1344. return reinterpret_cast<Rml::CompiledShaderHandle>(new CompiledShader(std::move(shader)));
  1345. Rml::Log::Message(Rml::Log::LT_WARNING, "Unsupported shader type '%s'.", name.c_str());
  1346. return {};
  1347. }
  1348. void RenderInterface_GL3::RenderShader(Rml::CompiledShaderHandle shader_handle, Rml::CompiledGeometryHandle geometry_handle,
  1349. Rml::Vector2f translation, Rml::TextureHandle /*texture*/)
  1350. {
  1351. RMLUI_ASSERT(geometry_handle);
  1352. const CompiledShader& shader = *reinterpret_cast<CompiledShader*>(shader_handle);
  1353. const CompiledShaderType type = shader.type;
  1354. const Gfx::CompiledGeometryData& geometry = *reinterpret_cast<Gfx::CompiledGeometryData*>(geometry_handle);
  1355. switch (type)
  1356. {
  1357. case CompiledShaderType::Gradient:
  1358. {
  1359. RMLUI_ASSERT(shader.stop_positions.size() == shader.stop_colors.size());
  1360. const int num_stops = (int)shader.stop_positions.size();
  1361. UseProgram(ProgramId::Gradient);
  1362. glUniform1i(GetUniformLocation(UniformId::Func), static_cast<int>(shader.gradient_function));
  1363. glUniform2f(GetUniformLocation(UniformId::P), shader.p.x, shader.p.y);
  1364. glUniform2f(GetUniformLocation(UniformId::V), shader.v.x, shader.v.y);
  1365. glUniform1i(GetUniformLocation(UniformId::NumStops), num_stops);
  1366. glUniform1fv(GetUniformLocation(UniformId::StopPositions), num_stops, shader.stop_positions.data());
  1367. glUniform4fv(GetUniformLocation(UniformId::StopColors), num_stops, shader.stop_colors[0]);
  1368. SubmitTransformUniform(translation);
  1369. glBindVertexArray(geometry.vao);
  1370. glDrawElements(GL_TRIANGLES, geometry.draw_count, GL_UNSIGNED_INT, (const GLvoid*)0);
  1371. glBindVertexArray(0);
  1372. }
  1373. break;
  1374. case CompiledShaderType::Invalid:
  1375. {
  1376. Rml::Log::Message(Rml::Log::LT_WARNING, "Unhandled render shader %d.", (int)type);
  1377. }
  1378. break;
  1379. }
  1380. Gfx::CheckGLError("RenderShader");
  1381. }
  1382. void RenderInterface_GL3::ReleaseCompiledShader(Rml::CompiledShaderHandle effect_handle)
  1383. {
  1384. delete reinterpret_cast<CompiledShader*>(effect_handle);
  1385. }
  1386. void RenderInterface_GL3::BlitTopLayerToPostprocessPrimary()
  1387. {
  1388. const Gfx::FramebufferData& source = render_layers.GetTopLayer();
  1389. const Gfx::FramebufferData& destination = render_layers.GetPostprocessPrimary();
  1390. glBindFramebuffer(GL_READ_FRAMEBUFFER, source.framebuffer);
  1391. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination.framebuffer);
  1392. // Blit and resolve MSAA. Any active scissor state will restrict the size of the blit region.
  1393. glBlitFramebuffer(0, 0, source.width, source.height, 0, 0, destination.width, destination.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
  1394. }
  1395. void RenderInterface_GL3::RenderFilters(const Rml::FilterHandleList& filter_handles)
  1396. {
  1397. for (const Rml::CompiledFilterHandle filter_handle : filter_handles)
  1398. {
  1399. const CompiledFilter& filter = *reinterpret_cast<const CompiledFilter*>(filter_handle);
  1400. const FilterType type = filter.type;
  1401. switch (type)
  1402. {
  1403. case FilterType::Passthrough:
  1404. {
  1405. UseProgram(ProgramId::Passthrough);
  1406. glBlendFunc(GL_CONSTANT_ALPHA, GL_ZERO);
  1407. glBlendColor(0.0f, 0.0f, 0.0f, filter.blend_factor);
  1408. const Gfx::FramebufferData& source = render_layers.GetPostprocessPrimary();
  1409. const Gfx::FramebufferData& destination = render_layers.GetPostprocessSecondary();
  1410. Gfx::BindTexture(source);
  1411. glBindFramebuffer(GL_FRAMEBUFFER, destination.framebuffer);
  1412. DrawFullscreenQuad();
  1413. render_layers.SwapPostprocessPrimarySecondary();
  1414. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  1415. }
  1416. break;
  1417. case FilterType::Blur:
  1418. {
  1419. glDisable(GL_BLEND);
  1420. const Gfx::FramebufferData& source_destination = render_layers.GetPostprocessPrimary();
  1421. const Gfx::FramebufferData& temp = render_layers.GetPostprocessSecondary();
  1422. const Rml::Rectanglei window_flipped = VerticallyFlipped(scissor_state, viewport_height);
  1423. RenderBlur(filter.sigma, source_destination, temp, window_flipped);
  1424. glEnable(GL_BLEND);
  1425. }
  1426. break;
  1427. case FilterType::DropShadow:
  1428. {
  1429. UseProgram(ProgramId::DropShadow);
  1430. glDisable(GL_BLEND);
  1431. Rml::Colourf color = ToPremultipliedAlpha(filter.color);
  1432. glUniform4fv(GetUniformLocation(UniformId::Color), 1, &color[0]);
  1433. const Gfx::FramebufferData& primary = render_layers.GetPostprocessPrimary();
  1434. const Gfx::FramebufferData& secondary = render_layers.GetPostprocessSecondary();
  1435. Gfx::BindTexture(primary);
  1436. glBindFramebuffer(GL_FRAMEBUFFER, secondary.framebuffer);
  1437. const Rml::Rectanglei window_flipped = VerticallyFlipped(scissor_state, viewport_height);
  1438. SetTexCoordLimits(GetUniformLocation(UniformId::TexCoordMin), GetUniformLocation(UniformId::TexCoordMax), window_flipped,
  1439. {primary.width, primary.height});
  1440. const Rml::Vector2f uv_offset = filter.offset / Rml::Vector2f(-(float)viewport_width, (float)viewport_height);
  1441. DrawFullscreenQuad(uv_offset);
  1442. if (filter.sigma >= 0.5f)
  1443. {
  1444. const Gfx::FramebufferData& tertiary = render_layers.GetPostprocessTertiary();
  1445. RenderBlur(filter.sigma, secondary, tertiary, window_flipped);
  1446. }
  1447. UseProgram(ProgramId::Passthrough);
  1448. BindTexture(primary);
  1449. glEnable(GL_BLEND);
  1450. DrawFullscreenQuad();
  1451. render_layers.SwapPostprocessPrimarySecondary();
  1452. }
  1453. break;
  1454. case FilterType::ColorMatrix:
  1455. {
  1456. UseProgram(ProgramId::ColorMatrix);
  1457. glDisable(GL_BLEND);
  1458. const GLint uniform_location = program_data->uniforms.Get(ProgramId::ColorMatrix, UniformId::ColorMatrix);
  1459. constexpr bool transpose = std::is_same<decltype(filter.color_matrix), Rml::RowMajorMatrix4f>::value;
  1460. glUniformMatrix4fv(uniform_location, 1, transpose, filter.color_matrix.data());
  1461. const Gfx::FramebufferData& source = render_layers.GetPostprocessPrimary();
  1462. const Gfx::FramebufferData& destination = render_layers.GetPostprocessSecondary();
  1463. Gfx::BindTexture(source);
  1464. glBindFramebuffer(GL_FRAMEBUFFER, destination.framebuffer);
  1465. DrawFullscreenQuad();
  1466. render_layers.SwapPostprocessPrimarySecondary();
  1467. glEnable(GL_BLEND);
  1468. }
  1469. break;
  1470. case FilterType::Invalid:
  1471. {
  1472. Rml::Log::Message(Rml::Log::LT_WARNING, "Unhandled render filter %d.", (int)type);
  1473. }
  1474. break;
  1475. }
  1476. }
  1477. Gfx::CheckGLError("RenderFilter");
  1478. }
  1479. void RenderInterface_GL3::PushLayer(Rml::LayerFill layer_fill)
  1480. {
  1481. if (layer_fill == Rml::LayerFill::Clone)
  1482. render_layers.PushLayerClone();
  1483. else
  1484. render_layers.PushLayer();
  1485. glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetTopLayer().framebuffer);
  1486. if (layer_fill == Rml::LayerFill::Clear)
  1487. glClear(GL_COLOR_BUFFER_BIT);
  1488. }
  1489. void RenderInterface_GL3::PopLayer(Rml::BlendMode blend_mode, const Rml::FilterHandleList& filters)
  1490. {
  1491. using Rml::BlendMode;
  1492. if (blend_mode == BlendMode::Discard)
  1493. {
  1494. RMLUI_ASSERT(filters.empty());
  1495. render_layers.PopLayer();
  1496. glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetTopLayer().framebuffer);
  1497. return;
  1498. }
  1499. // Blit stack to filter rendering buffer. Do this regardless of whether we actually have any filters to be applied,
  1500. // because we need to resolve the multi-sampled framebuffer in any case.
  1501. // @performance If we have BlendMode::Replace and no filters or mask then we can just blit directly to the destination.
  1502. BlitTopLayerToPostprocessPrimary();
  1503. // Render the filters, the PostprocessPrimary framebuffer is used for both input and output.
  1504. RenderFilters(filters);
  1505. // Pop the active layer, thereby activating the beneath layer.
  1506. render_layers.PopLayer();
  1507. // Render to the activated layer. Apply any mask if active.
  1508. glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetTopLayer().framebuffer);
  1509. Gfx::BindTexture(render_layers.GetPostprocessPrimary());
  1510. UseProgram(ProgramId::Passthrough);
  1511. if (blend_mode == BlendMode::Replace)
  1512. glDisable(GL_BLEND);
  1513. DrawFullscreenQuad();
  1514. if (blend_mode == BlendMode::Replace)
  1515. glEnable(GL_BLEND);
  1516. Gfx::CheckGLError("PopLayer");
  1517. }
  1518. Rml::TextureHandle RenderInterface_GL3::SaveLayerAsTexture(Rml::Vector2i dimensions)
  1519. {
  1520. Rml::TextureHandle render_texture = {};
  1521. if (!GenerateTexture(render_texture, nullptr, dimensions))
  1522. return {};
  1523. BlitTopLayerToPostprocessPrimary();
  1524. RMLUI_ASSERT(scissor_state.Valid() && render_texture);
  1525. const Rml::Rectanglei initial_scissor_state = scissor_state;
  1526. EnableScissorRegion(false);
  1527. const Gfx::FramebufferData& source = render_layers.GetPostprocessPrimary();
  1528. const Gfx::FramebufferData& destination = render_layers.GetPostprocessSecondary();
  1529. glBindFramebuffer(GL_READ_FRAMEBUFFER, source.framebuffer);
  1530. glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination.framebuffer);
  1531. Rml::Rectanglei bounds = initial_scissor_state;
  1532. // Flip the image vertically, as that convention is used for textures, and move to origin.
  1533. glBlitFramebuffer( //
  1534. bounds.Left(), source.height - bounds.Bottom(), // src0
  1535. bounds.Right(), source.height - bounds.Top(), // src1
  1536. 0, bounds.Height(), // dst0
  1537. bounds.Width(), 0, // dst1
  1538. GL_COLOR_BUFFER_BIT, GL_NEAREST //
  1539. );
  1540. glBindTexture(GL_TEXTURE_2D, (GLuint)render_texture);
  1541. const Gfx::FramebufferData& texture_source = destination;
  1542. glBindFramebuffer(GL_READ_FRAMEBUFFER, texture_source.framebuffer);
  1543. glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, bounds.Width(), bounds.Height());
  1544. SetScissor(initial_scissor_state);
  1545. glBindFramebuffer(GL_FRAMEBUFFER, render_layers.GetTopLayer().framebuffer);
  1546. Gfx::CheckGLError("SaveLayerAsTexture");
  1547. return render_texture;
  1548. }
  1549. void RenderInterface_GL3::UseProgram(ProgramId program_id)
  1550. {
  1551. RMLUI_ASSERT(program_data);
  1552. if (active_program != program_id)
  1553. {
  1554. if (program_id != ProgramId::None)
  1555. glUseProgram(program_data->programs[program_id]);
  1556. active_program = program_id;
  1557. }
  1558. }
  1559. int RenderInterface_GL3::GetUniformLocation(UniformId uniform_id) const
  1560. {
  1561. return program_data->uniforms.Get(active_program, uniform_id);
  1562. }
  1563. void RenderInterface_GL3::SubmitTransformUniform(Rml::Vector2f translation)
  1564. {
  1565. static_assert((size_t)ProgramId::Count < MaxNumPrograms, "Maximum number of programs exceeded.");
  1566. const size_t program_index = (size_t)active_program;
  1567. if (program_transform_dirty.test(program_index))
  1568. {
  1569. glUniformMatrix4fv(GetUniformLocation(UniformId::Transform), 1, false, transform.data());
  1570. program_transform_dirty.set(program_index, false);
  1571. }
  1572. glUniform2fv(GetUniformLocation(UniformId::Translate), 1, &translation.x);
  1573. Gfx::CheckGLError("SubmitTransformUniform");
  1574. }
  1575. RenderInterface_GL3::RenderLayerStack::RenderLayerStack()
  1576. {
  1577. fb_postprocess.resize(3);
  1578. }
  1579. RenderInterface_GL3::RenderLayerStack::~RenderLayerStack()
  1580. {
  1581. DestroyFramebuffers();
  1582. }
  1583. void RenderInterface_GL3::RenderLayerStack::PushLayer()
  1584. {
  1585. RMLUI_ASSERT(layers_size <= (int)fb_layers.size());
  1586. if (layers_size == (int)fb_layers.size())
  1587. {
  1588. // All framebuffers should share a single stencil buffer.
  1589. GLuint shared_depth_stencil = (fb_layers.empty() ? 0 : fb_layers.front().depth_stencil_buffer);
  1590. fb_layers.push_back(Gfx::FramebufferData{});
  1591. Gfx::CreateFramebuffer(fb_layers.back(), width, height, NUM_MSAA_SAMPLES, Gfx::FramebufferAttachment::DepthStencil, shared_depth_stencil);
  1592. }
  1593. layers_size += 1;
  1594. }
  1595. void RenderInterface_GL3::RenderLayerStack::PushLayerClone()
  1596. {
  1597. RMLUI_ASSERT(layers_size > 0);
  1598. fb_layers.insert(fb_layers.begin() + layers_size, Gfx::FramebufferData{fb_layers[layers_size - 1]});
  1599. layers_size += 1;
  1600. }
  1601. void RenderInterface_GL3::RenderLayerStack::PopLayer()
  1602. {
  1603. RMLUI_ASSERT(layers_size > 0);
  1604. layers_size -= 1;
  1605. // Only cloned framebuffers are removed. Other framebuffers remain for later re-use.
  1606. if (IsCloneOfBelow(layers_size))
  1607. fb_layers.erase(fb_layers.begin() + layers_size);
  1608. }
  1609. const Gfx::FramebufferData& RenderInterface_GL3::RenderLayerStack::GetTopLayer() const
  1610. {
  1611. RMLUI_ASSERT(layers_size > 0);
  1612. return fb_layers[layers_size - 1];
  1613. }
  1614. void RenderInterface_GL3::RenderLayerStack::SwapPostprocessPrimarySecondary()
  1615. {
  1616. std::swap(fb_postprocess[0], fb_postprocess[1]);
  1617. }
  1618. void RenderInterface_GL3::RenderLayerStack::BeginFrame(int new_width, int new_height)
  1619. {
  1620. RMLUI_ASSERT(layers_size == 0);
  1621. if (new_width != width || new_height != height)
  1622. {
  1623. width = new_width;
  1624. height = new_height;
  1625. DestroyFramebuffers();
  1626. }
  1627. PushLayer();
  1628. }
  1629. void RenderInterface_GL3::RenderLayerStack::EndFrame()
  1630. {
  1631. RMLUI_ASSERT(layers_size == 1);
  1632. PopLayer();
  1633. }
  1634. void RenderInterface_GL3::RenderLayerStack::DestroyFramebuffers()
  1635. {
  1636. RMLUI_ASSERTMSG(layers_size == 0, "Do not call this during frame rendering, that is, between BeginFrame() and EndFrame().");
  1637. for (Gfx::FramebufferData& fb : fb_layers)
  1638. Gfx::DestroyFramebuffer(fb);
  1639. fb_layers.clear();
  1640. for (Gfx::FramebufferData& fb : fb_postprocess)
  1641. Gfx::DestroyFramebuffer(fb);
  1642. }
  1643. bool RenderInterface_GL3::RenderLayerStack::IsCloneOfBelow(int layer_index) const
  1644. {
  1645. const bool result =
  1646. (layer_index >= 1 && layer_index < (int)fb_layers.size() && fb_layers[layer_index].framebuffer == fb_layers[layer_index - 1].framebuffer);
  1647. return result;
  1648. }
  1649. const Gfx::FramebufferData& RenderInterface_GL3::RenderLayerStack::EnsureFramebufferPostprocess(int index)
  1650. {
  1651. RMLUI_ASSERT(index < (int)fb_postprocess.size())
  1652. Gfx::FramebufferData& fb = fb_postprocess[index];
  1653. if (!fb.framebuffer)
  1654. Gfx::CreateFramebuffer(fb, width, height, 0, Gfx::FramebufferAttachment::None, 0);
  1655. return fb;
  1656. }
  1657. bool RmlGL3::Initialize(Rml::String* out_message)
  1658. {
  1659. #if defined RMLUI_PLATFORM_EMSCRIPTEN
  1660. if (out_message)
  1661. *out_message = "Started Emscripten WebGL renderer.";
  1662. #elif !defined RMLUI_GL3_CUSTOM_LOADER
  1663. const int gl_version = gladLoaderLoadGL();
  1664. if (gl_version == 0)
  1665. {
  1666. if (out_message)
  1667. *out_message = "Failed to initialize OpenGL context.";
  1668. return false;
  1669. }
  1670. if (out_message)
  1671. *out_message = Rml::CreateString(128, "Loaded OpenGL %d.%d.", GLAD_VERSION_MAJOR(gl_version), GLAD_VERSION_MINOR(gl_version));
  1672. #endif
  1673. return true;
  1674. }
  1675. void RmlGL3::Shutdown()
  1676. {
  1677. #if !defined RMLUI_PLATFORM_EMSCRIPTEN && !defined RMLUI_GL3_CUSTOM_LOADER
  1678. gladLoaderUnloadGL();
  1679. #endif
  1680. }