Graphics.cpp 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782
  1. /**
  2. * Copyright (c) 2006-2024 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. // LOVE
  21. #include "common/config.h"
  22. #include "common/math.h"
  23. #include "common/Vector.h"
  24. #include "Graphics.h"
  25. #include "font/Font.h"
  26. #include "StreamBuffer.h"
  27. #include "GraphicsReadback.h"
  28. #include "math/MathModule.h"
  29. #include "window/Window.h"
  30. #include "Buffer.h"
  31. #include "ShaderStage.h"
  32. #include "libraries/xxHash/xxhash.h"
  33. // C++
  34. #include <vector>
  35. #include <sstream>
  36. #include <algorithm>
  37. #include <iterator>
  38. // C
  39. #include <cmath>
  40. #include <cstdio>
  41. #ifdef LOVE_IOS
  42. #include <SDL_syswm.h>
  43. #endif
  44. namespace love
  45. {
  46. namespace graphics
  47. {
  48. namespace opengl
  49. {
  50. static GLenum getGLBlendOperation(BlendOperation op)
  51. {
  52. switch (op)
  53. {
  54. case BLENDOP_ADD: return GL_FUNC_ADD;
  55. case BLENDOP_SUBTRACT: return GL_FUNC_SUBTRACT;
  56. case BLENDOP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT;
  57. case BLENDOP_MIN: return GL_MIN;
  58. case BLENDOP_MAX: return GL_MAX;
  59. case BLENDOP_MAX_ENUM: return 0;
  60. }
  61. return 0;
  62. }
  63. static GLenum getGLBlendFactor(BlendFactor factor)
  64. {
  65. switch (factor)
  66. {
  67. case BLENDFACTOR_ZERO: return GL_ZERO;
  68. case BLENDFACTOR_ONE: return GL_ONE;
  69. case BLENDFACTOR_SRC_COLOR: return GL_SRC_COLOR;
  70. case BLENDFACTOR_ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR;
  71. case BLENDFACTOR_SRC_ALPHA: return GL_SRC_ALPHA;
  72. case BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA;
  73. case BLENDFACTOR_DST_COLOR: return GL_DST_COLOR;
  74. case BLENDFACTOR_ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR;
  75. case BLENDFACTOR_DST_ALPHA: return GL_DST_ALPHA;
  76. case BLENDFACTOR_ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA;
  77. case BLENDFACTOR_SRC_ALPHA_SATURATED: return GL_SRC_ALPHA_SATURATE;
  78. case BLENDFACTOR_MAX_ENUM: return 0;
  79. }
  80. return 0;
  81. }
  82. love::graphics::Graphics *createInstance()
  83. {
  84. love::graphics::Graphics *instance = nullptr;
  85. try
  86. {
  87. instance = new Graphics();
  88. }
  89. catch (love::Exception &e)
  90. {
  91. printf("Cannot create OpenGL renderer: %s\n", e.what());
  92. }
  93. return instance;
  94. }
  95. Graphics::Graphics()
  96. : love::graphics::Graphics("love.graphics.opengl")
  97. , windowHasStencil(false)
  98. , mainVAO(0)
  99. , internalBackbufferFBO(0)
  100. , requestedBackbufferMSAA(0)
  101. , bufferMapMemory(nullptr)
  102. , bufferMapMemorySize(2 * 1024 * 1024)
  103. , pixelFormatUsage()
  104. {
  105. gl = OpenGL();
  106. try
  107. {
  108. bufferMapMemory = new char[bufferMapMemorySize];
  109. }
  110. catch (std::exception &)
  111. {
  112. // Handled in getBufferMapMemory.
  113. }
  114. auto window = getInstance<love::window::Window>(M_WINDOW);
  115. if (window != nullptr)
  116. {
  117. window->setGraphics(this);
  118. // Recreate the window using the current renderer, if needed.
  119. if (window->isOpen())
  120. {
  121. int w, h;
  122. love::window::WindowSettings settings;
  123. window->getWindow(w, h, settings);
  124. window->setWindow(w, h, &settings);
  125. }
  126. }
  127. }
  128. Graphics::~Graphics()
  129. {
  130. delete[] bufferMapMemory;
  131. }
  132. love::graphics::StreamBuffer *Graphics::newStreamBuffer(BufferUsage type, size_t size)
  133. {
  134. return CreateStreamBuffer(type, size);
  135. }
  136. love::graphics::Texture *Graphics::newTexture(const Texture::Settings &settings, const Texture::Slices *data)
  137. {
  138. return new Texture(this, settings, data);
  139. }
  140. love::graphics::Texture *Graphics::newTextureView(love::graphics::Texture *base, const Texture::ViewSettings &viewsettings)
  141. {
  142. return new Texture(this, base, viewsettings);
  143. }
  144. love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStageType stage, const std::string &cachekey, const std::string &source, bool gles)
  145. {
  146. return new ShaderStage(this, stage, source, gles, cachekey);
  147. }
  148. love::graphics::Shader *Graphics::newShaderInternal(StrongRef<love::graphics::ShaderStage> stages[SHADERSTAGE_MAX_ENUM], const Shader::CompileOptions &options)
  149. {
  150. return new Shader(stages, options);
  151. }
  152. love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
  153. {
  154. return new Buffer(this, settings, format, data, size, arraylength);
  155. }
  156. love::graphics::GraphicsReadback *Graphics::newReadbackInternal(ReadbackMethod method, love::graphics::Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset)
  157. {
  158. return new GraphicsReadback(this, method, buffer, offset, size, dest, destoffset);
  159. }
  160. love::graphics::GraphicsReadback *Graphics::newReadbackInternal(ReadbackMethod method, love::graphics::Texture *texture, int slice, int mipmap, const Rect &rect, image::ImageData *dest, int destx, int desty)
  161. {
  162. return new GraphicsReadback(this, method, texture, slice, mipmap, rect, dest, destx, desty);
  163. }
  164. void Graphics::backbufferChanged(int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, bool backbufferdepth, int msaa)
  165. {
  166. bool changed = width != this->width || height != this->height
  167. || pixelwidth != this->pixelWidth || pixelheight != this->pixelHeight;
  168. changed |= backbufferstencil != this->backbufferHasStencil || backbufferdepth != this->backbufferHasDepth;
  169. changed |= msaa != this->requestedBackbufferMSAA;
  170. this->width = width;
  171. this->height = height;
  172. this->pixelWidth = pixelwidth;
  173. this->pixelHeight = pixelheight;
  174. this->backbufferHasStencil = backbufferstencil;
  175. this->backbufferHasDepth = backbufferdepth;
  176. this->requestedBackbufferMSAA = msaa;
  177. if (!isRenderTargetActive())
  178. {
  179. // Set the viewport to top-left corner.
  180. gl.setViewport({0, 0, pixelwidth, pixelheight});
  181. // Re-apply the scissor if it was active, since the rectangle passed to
  182. // glScissor is affected by the viewport dimensions.
  183. if (states.back().scissor)
  184. setScissor(states.back().scissorRect);
  185. resetProjection();
  186. }
  187. if (!changed)
  188. return;
  189. bool useinternalbackbuffer = false;
  190. if (msaa > 1)
  191. useinternalbackbuffer = true;
  192. // Our internal backbuffer code needs glBlitFramebuffer.
  193. if (!(GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_ES_VERSION_3_0
  194. || GLAD_EXT_framebuffer_blit || GLAD_ANGLE_framebuffer_blit || GLAD_NV_framebuffer_blit))
  195. {
  196. if (!(msaa > 1 && GLAD_APPLE_framebuffer_multisample))
  197. useinternalbackbuffer = false;
  198. }
  199. GLuint prevFBO = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
  200. bool restoreFBO = prevFBO != getInternalBackbufferFBO();
  201. if (useinternalbackbuffer)
  202. {
  203. Texture::Settings settings;
  204. settings.width = width;
  205. settings.height = height;
  206. settings.dpiScale = (float)pixelheight / (float)height;
  207. settings.msaa = msaa;
  208. settings.renderTarget = true;
  209. settings.readable.set(false);
  210. settings.format = isGammaCorrect() ? PIXELFORMAT_RGBA8_sRGB : PIXELFORMAT_RGBA8_UNORM;
  211. internalBackbuffer.set(newTexture(settings), Acquire::NORETAIN);
  212. internalBackbufferDepthStencil.set(nullptr);
  213. if (backbufferstencil || backbufferdepth)
  214. {
  215. if (backbufferstencil && backbufferdepth)
  216. settings.format = PIXELFORMAT_DEPTH24_UNORM_STENCIL8;
  217. else if (backbufferstencil)
  218. settings.format = PIXELFORMAT_STENCIL8;
  219. else if (backbufferdepth)
  220. settings.format = PIXELFORMAT_DEPTH24_UNORM;
  221. internalBackbufferDepthStencil.set(newTexture(settings), Acquire::NORETAIN);
  222. }
  223. RenderTargets rts;
  224. rts.colors.push_back(internalBackbuffer.get());
  225. rts.depthStencil.texture = internalBackbufferDepthStencil;
  226. internalBackbufferFBO = bindCachedFBO(rts);
  227. }
  228. else
  229. {
  230. internalBackbuffer.set(nullptr);
  231. internalBackbufferDepthStencil.set(nullptr);
  232. internalBackbufferFBO = 0;
  233. }
  234. if (restoreFBO)
  235. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, prevFBO);
  236. }
  237. GLuint Graphics::getInternalBackbufferFBO() const
  238. {
  239. if (internalBackbufferFBO != 0)
  240. return internalBackbufferFBO;
  241. else
  242. return getSystemBackbufferFBO();
  243. }
  244. GLuint Graphics::getSystemBackbufferFBO() const
  245. {
  246. #ifdef LOVE_IOS
  247. // Hack: iOS uses a custom FBO.
  248. SDL_SysWMinfo info = {};
  249. SDL_VERSION(&info.version);
  250. SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
  251. if (info.info.uikit.resolveFramebuffer != 0)
  252. return info.info.uikit.resolveFramebuffer;
  253. else
  254. return info.info.uikit.framebuffer;
  255. #else
  256. return 0;
  257. #endif
  258. }
  259. bool Graphics::setMode(void */*context*/, int width, int height, int pixelwidth, int pixelheight, bool backbufferstencil, bool backbufferdepth, int msaa)
  260. {
  261. // Okay, setup OpenGL.
  262. gl.initContext();
  263. if (gl.isCoreProfile())
  264. {
  265. glGenVertexArrays(1, &mainVAO);
  266. glBindVertexArray(mainVAO);
  267. }
  268. gl.setupContext();
  269. created = true;
  270. initCapabilities();
  271. // Enable blending
  272. gl.setEnableState(OpenGL::ENABLE_BLEND, true);
  273. // Auto-generated mipmaps should be the best quality possible
  274. if (!gl.isCoreProfile())
  275. glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
  276. if (!GLAD_ES_VERSION_2_0 && !gl.isCoreProfile())
  277. {
  278. // Make sure antialiasing works when set elsewhere
  279. glEnable(GL_MULTISAMPLE);
  280. // Enable texturing
  281. glEnable(GL_TEXTURE_2D);
  282. }
  283. if (!GLAD_ES_VERSION_2_0)
  284. glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
  285. gl.setTextureUnit(0);
  286. // Set pixel row alignment - code that calls glTexSubImage and glReadPixels
  287. // assumes there's no row alignment, but OpenGL defaults to 4 bytes.
  288. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  289. glPixelStorei(GL_PACK_ALIGNMENT, 1);
  290. // Always enable seamless cubemap filtering when possible.
  291. if (GLAD_VERSION_3_2 || GLAD_ARB_seamless_cube_map)
  292. glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
  293. // Set whether drawing converts input from linear -> sRGB colorspace.
  294. if (!gl.bugs.brokenSRGB && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB
  295. || GLAD_EXT_framebuffer_sRGB || GLAD_ES_VERSION_3_0))
  296. {
  297. if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
  298. gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, isGammaCorrect());
  299. }
  300. else
  301. setGammaCorrect(false);
  302. setDebug(isDebugEnabled());
  303. backbufferChanged(width, height, pixelwidth, pixelheight, backbufferstencil, backbufferdepth, msaa);
  304. if (batchedDrawState.vb[0] == nullptr)
  305. {
  306. // Initial sizes that should be good enough for most cases. It will
  307. // resize to fit if needed, later.
  308. batchedDrawState.vb[0] = CreateStreamBuffer(BUFFERUSAGE_VERTEX, 1024 * 1024 * 1);
  309. batchedDrawState.vb[1] = CreateStreamBuffer(BUFFERUSAGE_VERTEX, 256 * 1024 * 1);
  310. batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
  311. }
  312. // Reload all volatile objects.
  313. if (!Volatile::loadAll())
  314. ::printf("Could not reload all volatile objects.\n");
  315. createQuadIndexBuffer();
  316. // Restore the graphics state.
  317. restoreState(states.back());
  318. // We always need a default shader.
  319. for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++)
  320. {
  321. auto stype = (Shader::StandardShader) i;
  322. if (i == Shader::STANDARD_ARRAY && !capabilities.textureTypes[TEXTURE_2D_ARRAY])
  323. continue;
  324. // Apparently some intel GMA drivers on windows fail to compile shaders
  325. // which use array textures despite claiming support for the extension.
  326. try
  327. {
  328. if (!Shader::standardShaders[i])
  329. {
  330. std::vector<std::string> stages;
  331. Shader::CompileOptions opts;
  332. stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX));
  333. stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL));
  334. Shader::standardShaders[i] = newShader(stages, opts);
  335. }
  336. }
  337. catch (love::Exception &)
  338. {
  339. if (i == Shader::STANDARD_ARRAY)
  340. capabilities.textureTypes[TEXTURE_2D_ARRAY] = false;
  341. else
  342. throw;
  343. }
  344. }
  345. // A shader should always be active, but the default shader shouldn't be
  346. // returned by getShader(), so we don't do setShader(defaultShader).
  347. if (!Shader::current)
  348. Shader::standardShaders[Shader::STANDARD_DEFAULT]->attach();
  349. return true;
  350. }
  351. void Graphics::unSetMode()
  352. {
  353. if (!isCreated())
  354. return;
  355. flushBatchedDraws();
  356. internalBackbuffer.set(nullptr);
  357. internalBackbufferDepthStencil.set(nullptr);
  358. // Unload all volatile objects. These must be reloaded after the display
  359. // mode change.
  360. Volatile::unloadAll();
  361. clearTemporaryResources();
  362. for (const auto &pair : framebufferObjects)
  363. gl.deleteFramebuffer(pair.second);
  364. framebufferObjects.clear();
  365. if (mainVAO != 0)
  366. {
  367. glDeleteVertexArrays(1, &mainVAO);
  368. mainVAO = 0;
  369. }
  370. gl.deInitContext();
  371. created = false;
  372. }
  373. void Graphics::setActive(bool enable)
  374. {
  375. flushBatchedDraws();
  376. // Make sure all pending OpenGL commands have fully executed before
  377. // returning, when going from active to inactive. This is required on iOS.
  378. if (isCreated() && this->active && !enable)
  379. glFinish();
  380. active = enable;
  381. }
  382. static bool computeDispatchBarriers(Shader *shader, GLbitfield &preDispatchBarriers, GLbitfield &postDispatchBarriers)
  383. {
  384. for (auto buffer : shader->getActiveWritableStorageBuffers())
  385. {
  386. if (buffer == nullptr)
  387. return false;
  388. auto usage = buffer->getUsageFlags();
  389. postDispatchBarriers |= GL_BUFFER_UPDATE_BARRIER_BIT;
  390. if (usage & BUFFERUSAGEFLAG_SHADER_STORAGE)
  391. {
  392. preDispatchBarriers |= GL_SHADER_STORAGE_BARRIER_BIT;
  393. postDispatchBarriers |= GL_SHADER_STORAGE_BARRIER_BIT;
  394. }
  395. // TODO: does this need a pre dispatch barrier too?
  396. if (usage & BUFFERUSAGEFLAG_INDIRECT_ARGUMENTS)
  397. postDispatchBarriers |= GL_COMMAND_BARRIER_BIT;
  398. if (usage & BUFFERUSAGEFLAG_TEXEL)
  399. postDispatchBarriers |= GL_TEXTURE_FETCH_BARRIER_BIT;
  400. if (usage & BUFFERUSAGEFLAG_INDEX)
  401. postDispatchBarriers |= GL_ELEMENT_ARRAY_BARRIER_BIT;
  402. if (usage & BUFFERUSAGEFLAG_VERTEX)
  403. postDispatchBarriers |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
  404. postDispatchBarriers |= GL_PIXEL_BUFFER_BARRIER_BIT;
  405. }
  406. for (const auto &binding : shader->getStorageTextureBindings())
  407. {
  408. if (binding.texture == nullptr)
  409. return false;
  410. if (binding.access == GL_READ_ONLY)
  411. continue;
  412. preDispatchBarriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
  413. postDispatchBarriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
  414. | GL_TEXTURE_UPDATE_BARRIER_BIT
  415. | GL_TEXTURE_FETCH_BARRIER_BIT;
  416. if (binding.texture->isRenderTarget())
  417. postDispatchBarriers |= GL_FRAMEBUFFER_BARRIER_BIT;
  418. }
  419. return true;
  420. }
  421. bool Graphics::dispatch(love::graphics::Shader *s, int x, int y, int z)
  422. {
  423. auto shader = (Shader *) s;
  424. GLbitfield preDispatchBarriers = 0;
  425. GLbitfield postDispatchBarriers = 0;
  426. if (!computeDispatchBarriers(shader, preDispatchBarriers, postDispatchBarriers))
  427. return false;
  428. // glMemoryBarrier before dispatch to make sure non-compute-read ->
  429. // compute-write is synced.
  430. // TODO: is this needed? spec language around GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
  431. // makes me think so.
  432. // This is overly conservative (dispatch -> dispatch will have redundant
  433. // barriers).
  434. if (preDispatchBarriers != 0)
  435. glMemoryBarrier(preDispatchBarriers);
  436. glDispatchCompute(x, y, z);
  437. // Not as (theoretically) efficient as issuing the barrier right before
  438. // they're used later, but much less complicated.
  439. if (postDispatchBarriers != 0)
  440. glMemoryBarrier(postDispatchBarriers);
  441. return true;
  442. }
  443. bool Graphics::dispatch(love::graphics::Shader *s, love::graphics::Buffer *indirectargs, size_t argsoffset)
  444. {
  445. auto shader = (Shader *) s;
  446. GLbitfield preDispatchBarriers = 0;
  447. GLbitfield postDispatchBarriers = 0;
  448. if (!computeDispatchBarriers(shader, preDispatchBarriers, postDispatchBarriers))
  449. return false;
  450. if (preDispatchBarriers != 0)
  451. glMemoryBarrier(preDispatchBarriers);
  452. // Note: OpenGL has separate bind points for draw versus dispatch indirect
  453. // buffers. Our gl.bindBuffer wrapper uses the draw bind point, so we can't
  454. // use it here.
  455. glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, (GLuint)indirectargs->getHandle());
  456. glDispatchComputeIndirect(argsoffset);
  457. // Not as (theoretically) efficient as issuing the barrier right before
  458. // they're used later, but much less complicated.
  459. if (postDispatchBarriers != 0)
  460. glMemoryBarrier(postDispatchBarriers);
  461. return true;
  462. }
  463. void Graphics::draw(const DrawCommand &cmd)
  464. {
  465. gl.prepareDraw(this);
  466. gl.setVertexAttributes(*cmd.attributes, *cmd.buffers);
  467. gl.bindTextureToUnit(cmd.texture, 0, false);
  468. gl.setCullMode(cmd.cullMode);
  469. GLenum glprimitivetype = OpenGL::getGLPrimitiveType(cmd.primitiveType);
  470. if (cmd.indirectBuffer != nullptr)
  471. {
  472. gl.bindBuffer(BUFFERUSAGE_INDIRECT_ARGUMENTS, (GLuint) cmd.indirectBuffer->getHandle());
  473. glDrawArraysIndirect(glprimitivetype, BUFFER_OFFSET(cmd.indirectBufferOffset));
  474. }
  475. else if (cmd.instanceCount > 1)
  476. glDrawArraysInstanced(glprimitivetype, cmd.vertexStart, cmd.vertexCount, cmd.instanceCount);
  477. else
  478. glDrawArrays(glprimitivetype, cmd.vertexStart, cmd.vertexCount);
  479. ++drawCalls;
  480. }
  481. void Graphics::draw(const DrawIndexedCommand &cmd)
  482. {
  483. gl.prepareDraw(this);
  484. gl.setVertexAttributes(*cmd.attributes, *cmd.buffers);
  485. gl.bindTextureToUnit(cmd.texture, 0, false);
  486. gl.setCullMode(cmd.cullMode);
  487. const void *gloffset = BUFFER_OFFSET(cmd.indexBufferOffset);
  488. GLenum glprimitivetype = OpenGL::getGLPrimitiveType(cmd.primitiveType);
  489. GLenum gldatatype = OpenGL::getGLIndexDataType(cmd.indexType);
  490. gl.bindBuffer(BUFFERUSAGE_INDEX, cmd.indexBuffer->getHandle());
  491. if (cmd.indirectBuffer != nullptr)
  492. {
  493. // Note: OpenGL doesn't support indirect indexed draws with a non-zero
  494. // index buffer offset.
  495. gl.bindBuffer(BUFFERUSAGE_INDIRECT_ARGUMENTS, (GLuint) cmd.indirectBuffer->getHandle());
  496. glDrawElementsIndirect(glprimitivetype, gldatatype, BUFFER_OFFSET(cmd.indirectBufferOffset));
  497. }
  498. else if (cmd.instanceCount > 1)
  499. glDrawElementsInstanced(glprimitivetype, cmd.indexCount, gldatatype, gloffset, cmd.instanceCount);
  500. else
  501. glDrawElements(glprimitivetype, cmd.indexCount, gldatatype, gloffset);
  502. ++drawCalls;
  503. }
  504. static inline void advanceVertexOffsets(const VertexAttributes &attributes, BufferBindings &buffers, int vertexcount)
  505. {
  506. // TODO: Figure out a better way to avoid touching the same buffer multiple
  507. // times, if multiple attributes share the buffer.
  508. uint32 touchedbuffers = 0;
  509. for (unsigned int i = 0; i < VertexAttributes::MAX; i++)
  510. {
  511. if (!attributes.isEnabled(i))
  512. continue;
  513. auto &attrib = attributes.attribs[i];
  514. uint32 bufferbit = 1u << attrib.bufferIndex;
  515. if ((touchedbuffers & bufferbit) == 0)
  516. {
  517. touchedbuffers |= bufferbit;
  518. const auto &layout = attributes.bufferLayouts[attrib.bufferIndex];
  519. buffers.info[attrib.bufferIndex].offset += layout.stride * vertexcount;
  520. }
  521. }
  522. }
  523. void Graphics::drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture)
  524. {
  525. const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
  526. const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
  527. gl.prepareDraw(this);
  528. gl.bindTextureToUnit(texture, 0, false);
  529. gl.setCullMode(CULL_NONE);
  530. gl.bindBuffer(BUFFERUSAGE_INDEX, quadIndexBuffer->getHandle());
  531. if (gl.isBaseVertexSupported())
  532. {
  533. gl.setVertexAttributes(attributes, buffers);
  534. int basevertex = start * 4;
  535. for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW)
  536. {
  537. int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
  538. glDrawElementsBaseVertex(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0), basevertex);
  539. ++drawCalls;
  540. basevertex += quadcount * 4;
  541. }
  542. }
  543. else
  544. {
  545. BufferBindings bufferscopy = buffers;
  546. if (start > 0)
  547. advanceVertexOffsets(attributes, bufferscopy, start * 4);
  548. for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW)
  549. {
  550. gl.setVertexAttributes(attributes, bufferscopy);
  551. int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
  552. glDrawElements(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));
  553. ++drawCalls;
  554. if (count > MAX_QUADS_PER_DRAW)
  555. advanceVertexOffsets(attributes, bufferscopy, quadcount * 4);
  556. }
  557. }
  558. }
  559. static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/)
  560. {
  561. if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
  562. return;
  563. // Human-readable strings for the debug info.
  564. const char *sourceStr = OpenGL::debugSourceString(source);
  565. const char *typeStr = OpenGL::debugTypeString(type);
  566. const char *severityStr = OpenGL::debugSeverityString(severity);
  567. const char *fmt = "OpenGL: [source=%s, type=%s, severity=%s, id=%d]: %s\n";
  568. printf(fmt, sourceStr, typeStr, severityStr, id, msg);
  569. }
  570. void Graphics::setDebug(bool enable)
  571. {
  572. // Make sure debug output is supported. The AMD ext. is a bit different
  573. // so we don't make use of it, since AMD drivers now support KHR_debug.
  574. if (!(GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_2 || GLAD_KHR_debug || GLAD_ARB_debug_output))
  575. return;
  576. // TODO: We don't support GL_KHR_debug in GLES yet.
  577. if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_2)
  578. return;
  579. // Ugly hack to reduce code duplication.
  580. if (GLAD_ARB_debug_output && !(GLAD_VERSION_4_3 || GLAD_KHR_debug))
  581. {
  582. fp_glDebugMessageCallback = (pfn_glDebugMessageCallback) fp_glDebugMessageCallbackARB;
  583. fp_glDebugMessageControl = (pfn_glDebugMessageControl) fp_glDebugMessageControlARB;
  584. }
  585. if (!enable)
  586. {
  587. // Disable the debug callback function.
  588. glDebugMessageCallback(nullptr, nullptr);
  589. // We can disable debug output entirely with KHR_debug.
  590. if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_2 || GLAD_KHR_debug)
  591. glDisable(GL_DEBUG_OUTPUT);
  592. return;
  593. }
  594. // We don't want asynchronous debug output.
  595. glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
  596. glDebugMessageCallback(debugCB, nullptr);
  597. // Initially, enable everything.
  598. glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE);
  599. // Disable messages about deprecated OpenGL functionality.
  600. glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
  601. glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
  602. if (GLAD_VERSION_4_3 || GLAD_ES_VERSION_3_2 || GLAD_KHR_debug)
  603. glEnable(GL_DEBUG_OUTPUT);
  604. ::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
  605. }
  606. void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture)
  607. {
  608. const DisplayState &state = states.back();
  609. OpenGL::TempDebugGroup debuggroup("setRenderTargets");
  610. endPass(false);
  611. bool iswindow = rts.getFirstTarget().texture == nullptr;
  612. Winding vertexwinding = state.winding;
  613. if (iswindow)
  614. {
  615. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
  616. }
  617. else
  618. {
  619. bindCachedFBO(rts);
  620. // Flip front face winding when rendering to a texture, since our
  621. // projection matrix is flipped.
  622. // Note: projection matrix is set at a higher level.
  623. vertexwinding = vertexwinding == WINDING_CW ? WINDING_CCW : WINDING_CW;
  624. }
  625. glFrontFace(vertexwinding == WINDING_CW ? GL_CW : GL_CCW);
  626. gl.setViewport({0, 0, pixelw, pixelh});
  627. // Re-apply the scissor if it was active, since the rectangle passed to
  628. // glScissor is affected by the viewport dimensions.
  629. if (state.scissor)
  630. setScissor(state.scissorRect);
  631. // Make sure the correct sRGB setting is used when drawing to the textures.
  632. if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
  633. {
  634. if (hasSRGBtexture != gl.isStateEnabled(OpenGL::ENABLE_FRAMEBUFFER_SRGB))
  635. gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, hasSRGBtexture);
  636. }
  637. }
  638. void Graphics::endPass(bool presenting)
  639. {
  640. auto &rts = states.back().renderTargets;
  641. love::graphics::Texture *depthstencil = rts.depthStencil.texture.get();
  642. // Discard the depth/stencil buffer if we're using an internal cached one,
  643. // or if we're presenting the backbuffer to the display.
  644. if ((depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
  645. || (presenting && !rts.getFirstTarget().texture.get()))
  646. {
  647. discard({}, true);
  648. }
  649. // Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
  650. // don't have to worry about resolving to slices.
  651. if (rts.colors.size() > 0 && rts.colors[0].texture->getMSAA() > 1)
  652. {
  653. int mip = rts.colors[0].mipmap;
  654. int w = rts.colors[0].texture->getPixelWidth(mip);
  655. int h = rts.colors[0].texture->getPixelHeight(mip);
  656. for (int i = 0; i < (int) rts.colors.size(); i++)
  657. {
  658. Texture *c = (Texture *) rts.colors[i].texture.get();
  659. if (!c->isReadable())
  660. continue;
  661. glReadBuffer(GL_COLOR_ATTACHMENT0 + i);
  662. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, c->getFBO());
  663. if (GLAD_APPLE_framebuffer_multisample)
  664. glResolveMultisampleFramebufferAPPLE();
  665. else
  666. glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
  667. }
  668. }
  669. if (depthstencil != nullptr && depthstencil->getMSAA() > 1 && depthstencil->isReadable())
  670. {
  671. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, ((Texture *) depthstencil)->getFBO());
  672. if (GLAD_APPLE_framebuffer_multisample)
  673. glResolveMultisampleFramebufferAPPLE();
  674. else
  675. {
  676. int mip = rts.depthStencil.mipmap;
  677. int w = depthstencil->getPixelWidth(mip);
  678. int h = depthstencil->getPixelHeight(mip);
  679. PixelFormat format = depthstencil->getPixelFormat();
  680. GLbitfield mask = 0;
  681. if (isPixelFormatDepth(format))
  682. mask |= GL_DEPTH_BUFFER_BIT;
  683. if (isPixelFormatStencil(format))
  684. mask |= GL_STENCIL_BUFFER_BIT;
  685. if (mask != 0)
  686. glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, mask, GL_NEAREST);
  687. }
  688. }
  689. }
  690. void Graphics::clear(OptionalColorD c, OptionalInt stencil, OptionalDouble depth)
  691. {
  692. if (c.hasValue)
  693. {
  694. bool hasintegerformat = false;
  695. const auto &rts = states.back().renderTargets;
  696. for (const auto &rt : rts.colors)
  697. {
  698. if (rt.texture.get() && isPixelFormatInteger(rt.texture->getPixelFormat()))
  699. hasintegerformat = true;
  700. }
  701. // This variant of clear() uses glClear() which can't clear integer formats,
  702. // so we switch to the MRT variant if needed.
  703. if (hasintegerformat)
  704. {
  705. std::vector<OptionalColorD> colors(rts.colors.size());
  706. for (size_t i = 0; i < colors.size(); i++)
  707. colors[i] = c;
  708. clear(colors, stencil, depth);
  709. return;
  710. }
  711. }
  712. if (c.hasValue || stencil.hasValue || depth.hasValue)
  713. flushBatchedDraws();
  714. GLbitfield flags = 0;
  715. if (c.hasValue)
  716. {
  717. Colorf cf((float)c.value.r, (float)c.value.g, (float)c.value.b, (float)c.value.a);
  718. gammaCorrectColor(cf);
  719. glClearColor(cf.r, cf.g, cf.b, cf.a);
  720. flags |= GL_COLOR_BUFFER_BIT;
  721. }
  722. if (stencil.hasValue)
  723. {
  724. glClearStencil(stencil.value);
  725. flags |= GL_STENCIL_BUFFER_BIT;
  726. }
  727. if (depth.hasValue)
  728. {
  729. gl.clearDepth(depth.value);
  730. flags |= GL_DEPTH_BUFFER_BIT;
  731. }
  732. if (flags != 0)
  733. {
  734. OpenGL::CleanClearState cs(flags);
  735. glClear(flags);
  736. }
  737. if (c.hasValue && gl.bugs.clearRequiresDriverTextureStateUpdate && Shader::current)
  738. {
  739. // This seems to be enough to fix the bug for me. Other methods I've
  740. // tried (e.g. dummy draws) don't work in all cases.
  741. gl.useProgram(0);
  742. gl.useProgram((GLuint) Shader::current->getHandle());
  743. }
  744. }
  745. void Graphics::clear(const std::vector<OptionalColorD> &colors, OptionalInt stencil, OptionalDouble depth)
  746. {
  747. if (colors.size() == 0 && !stencil.hasValue && !depth.hasValue)
  748. return;
  749. const auto &rts = states.back().renderTargets.colors;
  750. int ncolorRTs = (int) rts.size();
  751. int ncolors = (int) colors.size();
  752. if (ncolors <= 1 && (ncolorRTs == 0 || (ncolorRTs == 1 && rts[0].texture != nullptr && !isPixelFormatInteger(rts[0].texture->getPixelFormat()))))
  753. {
  754. clear(ncolors > 0 ? colors[0] : OptionalColorD(), stencil, depth);
  755. return;
  756. }
  757. flushBatchedDraws();
  758. bool drawbuffersmodified = false;
  759. ncolors = std::min(ncolors, ncolorRTs);
  760. for (int i = 0; i < ncolors; i++)
  761. {
  762. if (!colors[i].hasValue)
  763. continue;
  764. PixelFormatType datatype = PIXELFORMATTYPE_UNORM;
  765. if (rts[i].texture != nullptr)
  766. datatype = getPixelFormatInfo(rts[i].texture->getPixelFormat()).dataType;
  767. ColorD c = colors[i].value;
  768. if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0)
  769. {
  770. if (datatype == PIXELFORMATTYPE_SINT)
  771. {
  772. const GLint carray[] = {(GLint)c.r, (GLint)c.g, (GLint)c.b, (GLint)c.a};
  773. glClearBufferiv(GL_COLOR, i, carray);
  774. }
  775. else if (datatype == PIXELFORMATTYPE_UINT)
  776. {
  777. const GLuint carray[] = {(GLuint)c.r, (GLuint)c.g, (GLuint)c.b, (GLuint)c.a};
  778. glClearBufferuiv(GL_COLOR, i, carray);
  779. }
  780. else
  781. {
  782. Colorf cf((float)c.r, (float)c.g, (float)c.b, (float)c.a);
  783. gammaCorrectColor(cf);
  784. const GLfloat carray[] = {cf.r, cf.g, cf.b, cf.a};
  785. glClearBufferfv(GL_COLOR, i, carray);
  786. }
  787. }
  788. else
  789. {
  790. Colorf cf((float)c.r, (float)c.g, (float)c.b, (float)c.a);
  791. gammaCorrectColor(cf);
  792. glDrawBuffer(GL_COLOR_ATTACHMENT0 + i);
  793. glClearColor(cf.r, cf.g, cf.b, cf.a);
  794. glClear(GL_COLOR_BUFFER_BIT);
  795. drawbuffersmodified = true;
  796. }
  797. }
  798. // Revert to the expected draw buffers once we're done, if glClearBuffer
  799. // wasn't supported.
  800. if (drawbuffersmodified)
  801. {
  802. GLenum bufs[MAX_COLOR_RENDER_TARGETS];
  803. for (int i = 0; i < ncolorRTs; i++)
  804. bufs[i] = GL_COLOR_ATTACHMENT0 + i;
  805. glDrawBuffers(ncolorRTs, bufs);
  806. }
  807. GLbitfield flags = 0;
  808. if (stencil.hasValue)
  809. {
  810. glClearStencil(stencil.value);
  811. flags |= GL_STENCIL_BUFFER_BIT;
  812. }
  813. if (depth.hasValue)
  814. {
  815. gl.clearDepth(depth.value);
  816. flags |= GL_DEPTH_BUFFER_BIT;
  817. }
  818. if (flags != 0)
  819. {
  820. OpenGL::CleanClearState cs(flags);
  821. glClear(flags);
  822. }
  823. if (gl.bugs.clearRequiresDriverTextureStateUpdate && Shader::current)
  824. {
  825. // This seems to be enough to fix the bug for me. Other methods I've
  826. // tried (e.g. dummy draws) don't work in all cases.
  827. gl.useProgram(0);
  828. gl.useProgram((GLuint) Shader::current->getHandle());
  829. }
  830. }
  831. void Graphics::discard(const std::vector<bool> &colorbuffers, bool depthstencil)
  832. {
  833. flushBatchedDraws();
  834. discard(OpenGL::FRAMEBUFFER_ALL, colorbuffers, depthstencil);
  835. }
  836. void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil)
  837. {
  838. if (!(GLAD_VERSION_4_3 || GLAD_ARB_invalidate_subdata || GLAD_ES_VERSION_3_0 || GLAD_EXT_discard_framebuffer))
  839. return;
  840. GLenum gltarget = GL_FRAMEBUFFER;
  841. if (target == OpenGL::FRAMEBUFFER_READ)
  842. gltarget = GL_READ_FRAMEBUFFER;
  843. else if (target == OpenGL::FRAMEBUFFER_DRAW)
  844. gltarget = GL_DRAW_FRAMEBUFFER;
  845. std::vector<GLenum> attachments;
  846. attachments.reserve(colorbuffers.size());
  847. // glDiscardFramebuffer uses different attachment enums for the default FBO.
  848. if (gl.getFramebuffer(target) == 0)
  849. {
  850. if (colorbuffers.size() > 0 && colorbuffers[0])
  851. attachments.push_back(GL_COLOR);
  852. if (depthstencil)
  853. {
  854. attachments.push_back(GL_STENCIL);
  855. attachments.push_back(GL_DEPTH);
  856. }
  857. }
  858. else
  859. {
  860. int rendertargetcount = std::max((int) states.back().renderTargets.colors.size(), 1);
  861. for (int i = 0; i < (int) colorbuffers.size(); i++)
  862. {
  863. if (colorbuffers[i] && i < rendertargetcount)
  864. attachments.push_back(GL_COLOR_ATTACHMENT0 + i);
  865. }
  866. if (depthstencil)
  867. {
  868. attachments.push_back(GL_STENCIL_ATTACHMENT);
  869. attachments.push_back(GL_DEPTH_ATTACHMENT);
  870. }
  871. }
  872. // Hint for the driver that it doesn't need to save these buffers.
  873. if (GLAD_VERSION_4_3 || GLAD_ARB_invalidate_subdata || GLAD_ES_VERSION_3_0)
  874. glInvalidateFramebuffer(gltarget, (GLint) attachments.size(), &attachments[0]);
  875. else if (GLAD_EXT_discard_framebuffer)
  876. glDiscardFramebufferEXT(gltarget, (GLint) attachments.size(), &attachments[0]);
  877. }
  878. void Graphics::cleanupRenderTexture(love::graphics::Texture *texture)
  879. {
  880. if (!texture->isRenderTarget())
  881. return;
  882. for (auto it = framebufferObjects.begin(); it != framebufferObjects.end(); /**/)
  883. {
  884. bool hastexture = false;
  885. const auto &rts = it->first;
  886. for (const RenderTarget &rt : rts.colors)
  887. {
  888. if (rt.texture == texture)
  889. {
  890. hastexture = true;
  891. break;
  892. }
  893. }
  894. hastexture = hastexture || rts.depthStencil.texture == texture;
  895. if (hastexture)
  896. {
  897. if (isCreated())
  898. gl.deleteFramebuffer(it->second);
  899. it = framebufferObjects.erase(it);
  900. }
  901. else
  902. ++it;
  903. }
  904. }
  905. GLuint Graphics::bindCachedFBO(const RenderTargets &targets)
  906. {
  907. GLuint fbo = framebufferObjects[targets];
  908. if (fbo != 0)
  909. {
  910. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
  911. }
  912. else
  913. {
  914. int msaa = targets.getFirstTarget().texture->getMSAA();
  915. bool hasDS = targets.depthStencil.texture != nullptr;
  916. glGenFramebuffers(1, &fbo);
  917. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
  918. int ncolortargets = 0;
  919. GLenum drawbuffers[MAX_COLOR_RENDER_TARGETS];
  920. auto attachRT = [&](const RenderTarget &rt)
  921. {
  922. bool renderbuffer = msaa > 1 || !rt.texture->isReadable();
  923. OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(rt.texture->getPixelFormat(), renderbuffer);
  924. if (fmt.framebufferAttachments[0] == GL_COLOR_ATTACHMENT0)
  925. {
  926. fmt.framebufferAttachments[0] = GL_COLOR_ATTACHMENT0 + ncolortargets;
  927. drawbuffers[ncolortargets] = fmt.framebufferAttachments[0];
  928. ncolortargets++;
  929. }
  930. GLuint handle = (GLuint) rt.texture->getRenderTargetHandle();
  931. for (GLenum attachment : fmt.framebufferAttachments)
  932. {
  933. if (attachment == GL_NONE)
  934. continue;
  935. else if (renderbuffer)
  936. glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, handle);
  937. else
  938. {
  939. TextureType textype = rt.texture->getTextureType();
  940. int layer = textype == TEXTURE_CUBE ? 0 : rt.slice;
  941. int face = textype == TEXTURE_CUBE ? rt.slice : 0;
  942. int level = rt.mipmap;
  943. gl.framebufferTexture(attachment, textype, handle, level, layer, face);
  944. }
  945. }
  946. };
  947. for (const auto &rt : targets.colors)
  948. attachRT(rt);
  949. if (hasDS)
  950. attachRT(targets.depthStencil);
  951. if (ncolortargets > 1)
  952. glDrawBuffers(ncolortargets, drawbuffers);
  953. else if (ncolortargets == 0 && hasDS && (GLAD_ES_VERSION_3_0 || !GLAD_ES_VERSION_2_0))
  954. {
  955. // glDrawBuffers is an ext in GL2. glDrawBuffer doesn't exist in ES3.
  956. GLenum none = GL_NONE;
  957. if (GLAD_ES_VERSION_3_0)
  958. glDrawBuffers(1, &none);
  959. else
  960. glDrawBuffer(GL_NONE);
  961. glReadBuffer(GL_NONE);
  962. }
  963. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  964. if (status != GL_FRAMEBUFFER_COMPLETE)
  965. {
  966. gl.deleteFramebuffer(fbo);
  967. const char *sstr = OpenGL::framebufferStatusString(status);
  968. throw love::Exception("Could not create Framebuffer Object! %s", sstr);
  969. }
  970. framebufferObjects[targets] = fbo;
  971. }
  972. return fbo;
  973. }
  974. void Graphics::present(void *screenshotCallbackData)
  975. {
  976. if (!isActive())
  977. return;
  978. if (isRenderTargetActive())
  979. throw love::Exception("present cannot be called while a render target is active.");
  980. deprecations.draw(this);
  981. flushBatchedDraws();
  982. endPass(true);
  983. int w = getPixelWidth();
  984. int h = getPixelHeight();
  985. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
  986. // Copy internal backbuffer to system backbuffer. When MSAA is used this
  987. // is a direct MSAA resolve.
  988. if (internalBackbuffer.get())
  989. {
  990. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_DRAW, getSystemBackbufferFBO());
  991. // Discard system backbuffer to prevent it from copying its contents
  992. // from VRAM to chip memory.
  993. discard(OpenGL::FRAMEBUFFER_DRAW, {true}, true);
  994. // updateBackbuffer checks for glBlitFramebuffer support.
  995. if (GLAD_APPLE_framebuffer_multisample && internalBackbuffer->getMSAA() > 1)
  996. glResolveMultisampleFramebufferAPPLE();
  997. else
  998. glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
  999. // Discarding the internal backbuffer directly after resolving it should
  1000. // eliminate any copy back to vram it might need to do.
  1001. discard(OpenGL::FRAMEBUFFER_READ, {true}, false);
  1002. }
  1003. if (!pendingScreenshotCallbacks.empty())
  1004. {
  1005. size_t row = 4 * w;
  1006. size_t size = row * h;
  1007. GLubyte *pixels = nullptr;
  1008. GLubyte *screenshot = nullptr;
  1009. try
  1010. {
  1011. pixels = new GLubyte[size];
  1012. screenshot = new GLubyte[size];
  1013. }
  1014. catch (std::exception &)
  1015. {
  1016. delete[] pixels;
  1017. delete[] screenshot;
  1018. throw love::Exception("Out of memory.");
  1019. }
  1020. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getSystemBackbufferFBO());
  1021. glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  1022. // Replace alpha values with full opacity.
  1023. for (size_t i = 3; i < size; i += 4)
  1024. pixels[i] = 255;
  1025. // OpenGL sucks and reads pixels from the lower-left. Let's fix that.
  1026. GLubyte *src = pixels - row;
  1027. GLubyte *dst = screenshot + size;
  1028. for (int i = 0; i < h; ++i)
  1029. memcpy(dst-=row, src+=row, row);
  1030. delete[] pixels;
  1031. auto imagemodule = Module::getInstance<love::image::Image>(M_IMAGE);
  1032. for (int i = 0; i < (int) pendingScreenshotCallbacks.size(); i++)
  1033. {
  1034. const auto &info = pendingScreenshotCallbacks[i];
  1035. image::ImageData *img = nullptr;
  1036. try
  1037. {
  1038. img = imagemodule->newImageData(w, h, PIXELFORMAT_RGBA8_UNORM, screenshot);
  1039. }
  1040. catch (love::Exception &)
  1041. {
  1042. delete[] screenshot;
  1043. info.callback(&info, nullptr, nullptr);
  1044. for (int j = i + 1; j < (int) pendingScreenshotCallbacks.size(); j++)
  1045. {
  1046. const auto &ninfo = pendingScreenshotCallbacks[j];
  1047. ninfo.callback(&ninfo, nullptr, nullptr);
  1048. }
  1049. pendingScreenshotCallbacks.clear();
  1050. throw;
  1051. }
  1052. info.callback(&info, img, screenshotCallbackData);
  1053. img->release();
  1054. }
  1055. delete[] screenshot;
  1056. pendingScreenshotCallbacks.clear();
  1057. }
  1058. #ifdef LOVE_IOS
  1059. // Hack: SDL's color renderbuffer must be bound when swapBuffers is called.
  1060. SDL_SysWMinfo info = {};
  1061. SDL_VERSION(&info.version);
  1062. SDL_GetWindowWMInfo(SDL_GL_GetCurrentWindow(), &info);
  1063. glBindRenderbuffer(GL_RENDERBUFFER, info.info.uikit.colorbuffer);
  1064. #endif
  1065. for (StreamBuffer *buffer : batchedDrawState.vb)
  1066. buffer->nextFrame();
  1067. batchedDrawState.indexBuffer->nextFrame();
  1068. auto window = getInstance<love::window::Window>(M_WINDOW);
  1069. if (window != nullptr)
  1070. window->swapBuffers();
  1071. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, getInternalBackbufferFBO());
  1072. // Reset the per-frame stat counts.
  1073. drawCalls = 0;
  1074. gl.stats.shaderSwitches = 0;
  1075. renderTargetSwitchCount = 0;
  1076. drawCallsBatched = 0;
  1077. updatePendingReadbacks();
  1078. updateTemporaryResources();
  1079. }
  1080. int Graphics::getRequestedBackbufferMSAA() const
  1081. {
  1082. return requestedBackbufferMSAA;
  1083. }
  1084. int Graphics::getBackbufferMSAA() const
  1085. {
  1086. return internalBackbuffer.get() ? internalBackbuffer->getMSAA() : 0;
  1087. }
  1088. void Graphics::setScissor(const Rect &rect)
  1089. {
  1090. flushBatchedDraws();
  1091. DisplayState &state = states.back();
  1092. if (!gl.isStateEnabled(OpenGL::ENABLE_SCISSOR_TEST))
  1093. gl.setEnableState(OpenGL::ENABLE_SCISSOR_TEST, true);
  1094. double dpiscale = getCurrentDPIScale();
  1095. Rect glrect;
  1096. glrect.x = (int) (rect.x * dpiscale);
  1097. glrect.y = (int) (rect.y * dpiscale);
  1098. glrect.w = (int) (rect.w * dpiscale);
  1099. glrect.h = (int) (rect.h * dpiscale);
  1100. // OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor.
  1101. gl.setScissor(glrect, isRenderTargetActive());
  1102. state.scissor = true;
  1103. state.scissorRect = rect;
  1104. }
  1105. void Graphics::setScissor()
  1106. {
  1107. if (states.back().scissor)
  1108. flushBatchedDraws();
  1109. states.back().scissor = false;
  1110. if (gl.isStateEnabled(OpenGL::ENABLE_SCISSOR_TEST))
  1111. gl.setEnableState(OpenGL::ENABLE_SCISSOR_TEST, false);
  1112. }
  1113. void Graphics::setStencilState(const StencilState &s)
  1114. {
  1115. validateStencilState(s);
  1116. flushBatchedDraws();
  1117. bool enablestencil = s.action != STENCIL_KEEP || s.compare != COMPARE_ALWAYS;
  1118. if (enablestencil != gl.isStateEnabled(OpenGL::ENABLE_STENCIL_TEST))
  1119. gl.setEnableState(OpenGL::ENABLE_STENCIL_TEST, enablestencil);
  1120. GLenum glaction = GL_KEEP;
  1121. switch (s.action)
  1122. {
  1123. case STENCIL_KEEP:
  1124. glaction = GL_KEEP;
  1125. break;
  1126. case STENCIL_ZERO:
  1127. glaction = GL_ZERO;
  1128. break;
  1129. case STENCIL_REPLACE:
  1130. glaction = GL_REPLACE;
  1131. break;
  1132. case STENCIL_INCREMENT:
  1133. glaction = GL_INCR;
  1134. break;
  1135. case STENCIL_DECREMENT:
  1136. glaction = GL_DECR;
  1137. break;
  1138. case STENCIL_INCREMENT_WRAP:
  1139. glaction = GL_INCR_WRAP;
  1140. break;
  1141. case STENCIL_DECREMENT_WRAP:
  1142. glaction = GL_DECR_WRAP;
  1143. break;
  1144. case STENCIL_INVERT:
  1145. glaction = GL_INVERT;
  1146. break;
  1147. case STENCIL_MAX_ENUM:
  1148. glaction = GL_KEEP;
  1149. break;
  1150. }
  1151. /**
  1152. * GPUs do the comparison opposite to what makes sense for love's API. For
  1153. * example, if the compare function is GREATER then the stencil test will
  1154. * pass if the reference value is greater than the value in the stencil
  1155. * buffer. With our API it's more intuitive to assume that
  1156. * setStencilState(STENCIL_KEEP, COMPARE_GREATER, 4) will make it pass if the
  1157. * stencil buffer has a value greater than 4.
  1158. **/
  1159. GLenum glcompare = OpenGL::getGLCompareMode(getReversedCompareMode(s.compare));
  1160. if (enablestencil)
  1161. {
  1162. glStencilFunc(glcompare, s.value, s.readMask);
  1163. glStencilOp(GL_KEEP, GL_KEEP, glaction);
  1164. }
  1165. if (s.writeMask != gl.getStencilWriteMask())
  1166. gl.setStencilWriteMask(s.writeMask);
  1167. states.back().stencil = s;
  1168. }
  1169. void Graphics::setDepthMode(CompareMode compare, bool write)
  1170. {
  1171. validateDepthState(write);
  1172. DisplayState &state = states.back();
  1173. if (state.depthTest != compare || state.depthWrite != write)
  1174. flushBatchedDraws();
  1175. state.depthTest = compare;
  1176. state.depthWrite = write;
  1177. bool depthenable = compare != COMPARE_ALWAYS || write;
  1178. if (depthenable != gl.isStateEnabled(OpenGL::ENABLE_DEPTH_TEST))
  1179. gl.setEnableState(OpenGL::ENABLE_DEPTH_TEST, depthenable);
  1180. if (depthenable)
  1181. {
  1182. glDepthFunc(OpenGL::getGLCompareMode(compare));
  1183. gl.setDepthWrites(write);
  1184. }
  1185. }
  1186. void Graphics::setFrontFaceWinding(Winding winding)
  1187. {
  1188. DisplayState &state = states.back();
  1189. if (state.winding != winding)
  1190. flushBatchedDraws();
  1191. state.winding = winding;
  1192. if (isRenderTargetActive())
  1193. winding = winding == WINDING_CW ? WINDING_CCW : WINDING_CW;
  1194. glFrontFace(winding == WINDING_CW ? GL_CW : GL_CCW);
  1195. }
  1196. void Graphics::setColor(Colorf c)
  1197. {
  1198. c.r = std::min(std::max(c.r, 0.0f), 1.0f);
  1199. c.g = std::min(std::max(c.g, 0.0f), 1.0f);
  1200. c.b = std::min(std::max(c.b, 0.0f), 1.0f);
  1201. c.a = std::min(std::max(c.a, 0.0f), 1.0f);
  1202. states.back().color = c;
  1203. }
  1204. void Graphics::setColorMask(ColorChannelMask mask)
  1205. {
  1206. flushBatchedDraws();
  1207. uint32 maskbits =
  1208. ((mask.r ? 1 : 0) << 0) | ((mask.g ? 1 : 0) << 1) |
  1209. ((mask.b ? 1 : 0) << 2) | ((mask.a ? 1 : 0) << 3);
  1210. gl.setColorWriteMask(maskbits);
  1211. states.back().colorMask = mask;
  1212. }
  1213. void Graphics::setBlendState(const BlendState &blend)
  1214. {
  1215. if (!(blend == states.back().blend))
  1216. flushBatchedDraws();
  1217. if (blend.operationRGB == BLENDOP_MAX || blend.operationA == BLENDOP_MAX
  1218. || blend.operationRGB == BLENDOP_MIN || blend.operationA == BLENDOP_MIN)
  1219. {
  1220. if (!capabilities.features[FEATURE_BLEND_MINMAX])
  1221. throw love::Exception("The 'min' and 'max' blend operations are not supported on this system.");
  1222. }
  1223. if (blend.enable != gl.isStateEnabled(OpenGL::ENABLE_BLEND))
  1224. gl.setEnableState(OpenGL::ENABLE_BLEND, blend.enable);
  1225. if (blend.enable)
  1226. {
  1227. GLenum opRGB = getGLBlendOperation(blend.operationRGB);
  1228. GLenum opA = getGLBlendOperation(blend.operationA);
  1229. GLenum srcRGB = getGLBlendFactor(blend.srcFactorRGB);
  1230. GLenum srcA = getGLBlendFactor(blend.srcFactorA);
  1231. GLenum dstRGB = getGLBlendFactor(blend.dstFactorRGB);
  1232. GLenum dstA = getGLBlendFactor(blend.dstFactorA);
  1233. glBlendEquationSeparate(opRGB, opA);
  1234. glBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA);
  1235. }
  1236. states.back().blend = blend;
  1237. }
  1238. void Graphics::setPointSize(float size)
  1239. {
  1240. if (size != states.back().pointSize)
  1241. flushBatchedDraws();
  1242. states.back().pointSize = size;
  1243. }
  1244. void Graphics::setWireframe(bool enable)
  1245. {
  1246. // Not supported in OpenGL ES.
  1247. if (GLAD_ES_VERSION_2_0)
  1248. return;
  1249. flushBatchedDraws();
  1250. glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL);
  1251. states.back().wireframe = enable;
  1252. }
  1253. void *Graphics::getBufferMapMemory(size_t size)
  1254. {
  1255. // We don't need anything more complicated because get/release calls are
  1256. // never interleaved (as of when this comment was written.)
  1257. if (bufferMapMemory == nullptr || size > bufferMapMemorySize)
  1258. return malloc(size);
  1259. return bufferMapMemory;
  1260. }
  1261. void Graphics::releaseBufferMapMemory(void *mem)
  1262. {
  1263. if (mem != bufferMapMemory)
  1264. free(mem);
  1265. }
  1266. Renderer Graphics::getRenderer() const
  1267. {
  1268. return RENDERER_OPENGL;
  1269. }
  1270. bool Graphics::usesGLSLES() const
  1271. {
  1272. return GLAD_ES_VERSION_2_0;
  1273. }
  1274. Graphics::RendererInfo Graphics::getRendererInfo() const
  1275. {
  1276. RendererInfo info;
  1277. if (GLAD_ES_VERSION_2_0)
  1278. info.name = "OpenGL ES";
  1279. else
  1280. info.name = "OpenGL";
  1281. const char *str = (const char *) glGetString(GL_VERSION);
  1282. if (str)
  1283. info.version = str;
  1284. else
  1285. throw love::Exception("Cannot retrieve renderer version information.");
  1286. str = (const char *) glGetString(GL_VENDOR);
  1287. if (str)
  1288. info.vendor = str;
  1289. else
  1290. throw love::Exception("Cannot retrieve renderer vendor information.");
  1291. str = (const char *) glGetString(GL_RENDERER);
  1292. if (str)
  1293. info.device = str;
  1294. else
  1295. throw love::Exception("Cannot retrieve renderer device information.");
  1296. return info;
  1297. }
  1298. void Graphics::getAPIStats(int &shaderswitches) const
  1299. {
  1300. shaderswitches = gl.stats.shaderSwitches;
  1301. }
  1302. void Graphics::initCapabilities()
  1303. {
  1304. capabilities.features[FEATURE_MULTI_RENDER_TARGET_FORMATS] = gl.isMultiFormatMRTSupported();
  1305. capabilities.features[FEATURE_CLAMP_ZERO] = gl.isClampZeroOneTextureWrapSupported();
  1306. capabilities.features[FEATURE_CLAMP_ONE] = gl.isClampZeroOneTextureWrapSupported();
  1307. capabilities.features[FEATURE_BLEND_MINMAX] = GLAD_VERSION_1_4 || GLAD_ES_VERSION_3_0 || GLAD_EXT_blend_minmax;
  1308. capabilities.features[FEATURE_LIGHTEN] = capabilities.features[FEATURE_BLEND_MINMAX];
  1309. capabilities.features[FEATURE_FULL_NPOT] = GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot;
  1310. capabilities.features[FEATURE_PIXEL_SHADER_HIGHP] = gl.isPixelShaderHighpSupported();
  1311. capabilities.features[FEATURE_SHADER_DERIVATIVES] = GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_standard_derivatives;
  1312. capabilities.features[FEATURE_GLSL3] = GLAD_ES_VERSION_3_0 || gl.isCoreProfile();
  1313. capabilities.features[FEATURE_GLSL4] = GLAD_ES_VERSION_3_1 || (gl.isCoreProfile() && GLAD_VERSION_4_3);
  1314. capabilities.features[FEATURE_INSTANCING] = gl.isInstancingSupported();
  1315. capabilities.features[FEATURE_TEXEL_BUFFER] = gl.isBufferUsageSupported(BUFFERUSAGE_TEXEL);
  1316. capabilities.features[FEATURE_INDEX_BUFFER_32BIT] = GLAD_VERSION_1_1 || GLAD_ES_VERSION_3_0 || GLAD_OES_element_index_uint;
  1317. capabilities.features[FEATURE_COPY_BUFFER] = gl.isCopyBufferSupported();
  1318. capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = gl.isCopyBufferToTextureSupported();
  1319. capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = gl.isCopyTextureToBufferSupported();
  1320. capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = gl.isCopyRenderTargetToBufferSupported();
  1321. capabilities.features[FEATURE_MIPMAP_RANGE] = GLAD_VERSION_1_2 || GLAD_ES_VERSION_3_0;
  1322. capabilities.features[FEATURE_INDIRECT_DRAW] = capabilities.features[FEATURE_GLSL4];
  1323. static_assert(FEATURE_MAX_ENUM == 19, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
  1324. capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize();
  1325. capabilities.limits[LIMIT_TEXTURE_SIZE] = gl.getMax2DTextureSize();
  1326. capabilities.limits[LIMIT_TEXTURE_LAYERS] = gl.getMaxTextureLayers();
  1327. capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = gl.getMax3DTextureSize();
  1328. capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = gl.getMaxCubeTextureSize();
  1329. capabilities.limits[LIMIT_TEXEL_BUFFER_SIZE] = gl.getMaxTexelBufferSize();
  1330. capabilities.limits[LIMIT_SHADER_STORAGE_BUFFER_SIZE] = gl.getMaxShaderStorageBufferSize();
  1331. capabilities.limits[LIMIT_THREADGROUPS_X] = gl.getMaxComputeWorkGroupsX();
  1332. capabilities.limits[LIMIT_THREADGROUPS_Y] = gl.getMaxComputeWorkGroupsY();
  1333. capabilities.limits[LIMIT_THREADGROUPS_Z] = gl.getMaxComputeWorkGroupsZ();
  1334. capabilities.limits[LIMIT_RENDER_TARGETS] = gl.getMaxRenderTargets();
  1335. capabilities.limits[LIMIT_TEXTURE_MSAA] = gl.getMaxSamples();
  1336. capabilities.limits[LIMIT_ANISOTROPY] = gl.getMaxAnisotropy();
  1337. static_assert(LIMIT_MAX_ENUM == 13, "Graphics::initCapabilities must be updated when adding a new system limit!");
  1338. for (int i = 0; i < TEXTURE_MAX_ENUM; i++)
  1339. capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
  1340. for (int i = 0; i < PIXELFORMAT_MAX_ENUM; i++)
  1341. {
  1342. auto format = (PixelFormat) i;
  1343. pixelFormatUsage[i][0] = computePixelFormatUsage(format, false);
  1344. pixelFormatUsage[i][1] = computePixelFormatUsage(format, true);
  1345. }
  1346. #ifdef LOVE_ANDROID
  1347. // This can't be done in initContext with the rest of the bug checks because
  1348. // isPixelFormatSupported relies on state initialized here / after init.
  1349. if (GLAD_ES_VERSION_3_0 && !isPixelFormatSupported(PIXELFORMAT_R8_UNORM, PIXELFORMATUSAGEFLAGS_SAMPLE | PIXELFORMATUSAGEFLAGS_RENDERTARGET))
  1350. {
  1351. gl.bugs.brokenR8PixelFormat = true;
  1352. pixelFormatUsage[PIXELFORMAT_R8_UNORM][0] = computePixelFormatUsage(PIXELFORMAT_R8_UNORM, false);
  1353. pixelFormatUsage[PIXELFORMAT_R8_UNORM][1] = computePixelFormatUsage(PIXELFORMAT_R8_UNORM, true);
  1354. }
  1355. #endif
  1356. }
  1357. uint32 Graphics::computePixelFormatUsage(PixelFormat format, bool readable)
  1358. {
  1359. uint32 usage = OpenGL::getPixelFormatUsageFlags(format);
  1360. if (readable && (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) == 0)
  1361. return 0;
  1362. // Even though we might have the necessary OpenGL version or extension,
  1363. // drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching
  1364. // a texture to a FBO whose format the driver doesn't like. So we should
  1365. // test with an actual FBO.
  1366. // Avoid the test for depth/stencil formats - not every GL version
  1367. // guarantees support for depth/stencil-only render targets (which we would
  1368. // need for the test below to work), and we already do some finagling in
  1369. // convertPixelFormat to try to use the best-supported internal
  1370. // depth/stencil format for a particular driver.
  1371. if ((usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0 && !isPixelFormatDepthStencil(format))
  1372. {
  1373. GLuint texture = 0;
  1374. GLuint renderbuffer = 0;
  1375. OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, !readable);
  1376. GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
  1377. GLuint fbo = 0;
  1378. glGenFramebuffers(1, &fbo);
  1379. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
  1380. // Make sure at least something is bound to a color attachment. I believe
  1381. // this is required on ES2 but I'm not positive.
  1382. if (isPixelFormatDepthStencil(format))
  1383. {
  1384. love::graphics::Texture *tex = getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT);
  1385. gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, (GLuint) tex->getHandle(), 0, 0, 0);
  1386. }
  1387. if (readable)
  1388. {
  1389. glGenTextures(1, &texture);
  1390. gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false);
  1391. SamplerState s;
  1392. s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST;
  1393. gl.setSamplerState(TEXTURE_2D, s);
  1394. gl.rawTexStorage(TEXTURE_2D, 1, format, 1, 1);
  1395. }
  1396. else
  1397. {
  1398. glGenRenderbuffers(1, &renderbuffer);
  1399. glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
  1400. glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1);
  1401. }
  1402. for (GLenum attachment : fmt.framebufferAttachments)
  1403. {
  1404. if (attachment == GL_NONE)
  1405. continue;
  1406. if (readable)
  1407. gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0);
  1408. else
  1409. glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer);
  1410. }
  1411. if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
  1412. usage &= ~PIXELFORMATUSAGEFLAGS_RENDERTARGET;
  1413. gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
  1414. gl.deleteFramebuffer(fbo);
  1415. if (texture != 0)
  1416. gl.deleteTexture(texture);
  1417. if (renderbuffer != 0)
  1418. glDeleteRenderbuffers(1, &renderbuffer);
  1419. }
  1420. return usage;
  1421. }
  1422. bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage)
  1423. {
  1424. format = getSizedFormat(format);
  1425. bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0;
  1426. return (usage & pixelFormatUsage[format][readable ? 1 : 0]) == usage;
  1427. }
  1428. } // opengl
  1429. } // graphics
  1430. } // love