Buffer.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /**
  2. * Copyright (c) 2006-2023 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. #include "Buffer.h"
  21. #include "common/Exception.h"
  22. #include "graphics/vertex.h"
  23. #include "Graphics.h"
  24. #include <cstdlib>
  25. #include <cstring>
  26. #include <algorithm>
  27. #include <limits>
  28. namespace love
  29. {
  30. namespace graphics
  31. {
  32. namespace opengl
  33. {
  34. static GLenum getGLFormat(DataFormat format)
  35. {
  36. switch (format)
  37. {
  38. case DATAFORMAT_FLOAT: return GL_R32F;
  39. case DATAFORMAT_FLOAT_VEC2: return GL_RG32F;
  40. case DATAFORMAT_FLOAT_VEC3: return GL_RGB32F;
  41. case DATAFORMAT_FLOAT_VEC4: return GL_RGBA32F;
  42. case DATAFORMAT_INT32: return GL_R32I;
  43. case DATAFORMAT_INT32_VEC2: return GL_RG32I;
  44. case DATAFORMAT_INT32_VEC3: return GL_RGB32I;
  45. case DATAFORMAT_INT32_VEC4: return GL_RGBA32I;
  46. case DATAFORMAT_UINT32: return GL_R32UI;
  47. case DATAFORMAT_UINT32_VEC2: return GL_RG32UI;
  48. case DATAFORMAT_UINT32_VEC3: return GL_RGB32UI;
  49. case DATAFORMAT_UINT32_VEC4: return GL_RGBA32UI;
  50. case DATAFORMAT_UNORM8_VEC4: return GL_RGBA8;
  51. case DATAFORMAT_INT8_VEC4: return GL_RGBA8I;
  52. case DATAFORMAT_UINT8_VEC4: return GL_RGBA8UI;
  53. case DATAFORMAT_UNORM16_VEC2: return GL_RG16;
  54. case DATAFORMAT_UNORM16_VEC4: return GL_RGBA16;
  55. case DATAFORMAT_INT16_VEC2: return GL_RG16I;
  56. case DATAFORMAT_INT16_VEC4: return GL_RGBA16I;
  57. case DATAFORMAT_UINT16: return GL_R16UI;
  58. case DATAFORMAT_UINT16_VEC2: return GL_RG16UI;
  59. case DATAFORMAT_UINT16_VEC4: return GL_RGBA16UI;
  60. default: return GL_ZERO;
  61. }
  62. }
  63. Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
  64. : love::graphics::Buffer(gfx, settings, format, size, arraylength)
  65. {
  66. size = getSize();
  67. arraylength = getArrayLength();
  68. if (usageFlags & BUFFERUSAGEFLAG_TEXEL)
  69. mapUsage = BUFFERUSAGE_TEXEL;
  70. else if (usageFlags & BUFFERUSAGEFLAG_VERTEX)
  71. mapUsage = BUFFERUSAGE_VERTEX;
  72. else if (usageFlags & BUFFERUSAGEFLAG_INDEX)
  73. mapUsage = BUFFERUSAGE_INDEX;
  74. else if (usageFlags & BUFFERUSAGEFLAG_SHADER_STORAGE)
  75. mapUsage = BUFFERUSAGE_SHADER_STORAGE;
  76. else if (usageFlags & BUFFERUSAGEFLAG_INDIRECT_ARGUMENTS)
  77. mapUsage = BUFFERUSAGE_INDIRECT_ARGUMENTS;
  78. target = OpenGL::getGLBufferType(mapUsage);
  79. if (dataUsage == BUFFERDATAUSAGE_STREAM)
  80. ownsMemoryMap = true;
  81. std::vector<uint8> emptydata;
  82. if (settings.zeroInitialize && data == nullptr && !GLAD_VERSION_4_3)
  83. {
  84. try
  85. {
  86. emptydata.resize(getSize());
  87. data = emptydata.data();
  88. }
  89. catch (std::exception &)
  90. {
  91. data = nullptr;
  92. }
  93. }
  94. if (!load(data))
  95. {
  96. unloadVolatile();
  97. throw love::Exception("Could not create buffer with %d bytes (out of VRAM?)", size);
  98. }
  99. if (settings.zeroInitialize && data == nullptr && GLAD_VERSION_4_3)
  100. {
  101. gl.bindBuffer(mapUsage, buffer);
  102. glClearBufferData(target, GL_R8UI, GL_RED, GL_UNSIGNED_BYTE, nullptr);
  103. }
  104. }
  105. Buffer::~Buffer()
  106. {
  107. unloadVolatile();
  108. if (memoryMap != nullptr && ownsMemoryMap)
  109. free(memoryMap);
  110. }
  111. bool Buffer::loadVolatile()
  112. {
  113. if (buffer != 0)
  114. return true;
  115. return load(nullptr);
  116. }
  117. void Buffer::unloadVolatile()
  118. {
  119. mapped = false;
  120. if (buffer != 0)
  121. gl.deleteBuffer(buffer);
  122. buffer = 0;
  123. if (texture != 0)
  124. gl.deleteTexture(texture);
  125. texture = 0;
  126. }
  127. bool Buffer::load(const void *initialdata)
  128. {
  129. while (glGetError() != GL_NO_ERROR)
  130. /* Clear the error buffer. */;
  131. glGenBuffers(1, &buffer);
  132. gl.bindBuffer(mapUsage, buffer);
  133. GLenum gldatausage = OpenGL::getGLBufferDataUsage(getDataUsage());
  134. // initialdata can be null.
  135. glBufferData(target, (GLsizeiptr) getSize(), initialdata, gldatausage);
  136. if (getUsageFlags() & BUFFERUSAGEFLAG_TEXEL)
  137. {
  138. glGenTextures(1, &texture);
  139. gl.bindBufferTextureToUnit(texture, 0, false, true);
  140. GLenum glformat = getGLFormat(getDataMember(0).decl.format);
  141. glTexBuffer(target, glformat, buffer);
  142. }
  143. if (debugName.hasValue)
  144. glObjectLabel(GL_BUFFER, buffer, -1, debugName.value.c_str());
  145. return (glGetError() == GL_NO_ERROR);
  146. }
  147. bool Buffer::supportsOrphan() const
  148. {
  149. return dataUsage == BUFFERDATAUSAGE_STREAM || dataUsage == BUFFERDATAUSAGE_DYNAMIC;
  150. }
  151. void *Buffer::map(MapType map, size_t offset, size_t size)
  152. {
  153. if (size == 0)
  154. return nullptr;
  155. if (map == MAP_WRITE_INVALIDATE && (isImmutable() || dataUsage == BUFFERDATAUSAGE_READBACK))
  156. return nullptr;
  157. if (map == MAP_READ_ONLY && dataUsage != BUFFERDATAUSAGE_READBACK)
  158. return nullptr;
  159. Range r(offset, size);
  160. if (!Range(0, getSize()).contains(r))
  161. return nullptr;
  162. char *data = nullptr;
  163. if (map == MAP_READ_ONLY)
  164. {
  165. gl.bindBuffer(mapUsage, buffer);
  166. if (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0)
  167. data = (char *) glMapBufferRange(target, offset, size, GL_MAP_READ_BIT);
  168. else if (GLAD_VERSION_1_1)
  169. data = (char *) glMapBuffer(target, GL_READ_ONLY) + offset;
  170. }
  171. else if (ownsMemoryMap)
  172. {
  173. if (memoryMap == nullptr)
  174. memoryMap = (char *) malloc(getSize());
  175. data = memoryMap;
  176. }
  177. else
  178. {
  179. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  180. data = (char *) gfx->getBufferMapMemory(size);
  181. }
  182. if (data != nullptr)
  183. {
  184. mapped = true;
  185. mappedType = map;
  186. mappedRange = r;
  187. if (!ownsMemoryMap)
  188. memoryMap = data;
  189. }
  190. return data;
  191. }
  192. void Buffer::unmap(size_t usedoffset, size_t usedsize)
  193. {
  194. Range r(usedoffset, usedsize);
  195. if (!mapped || !mappedRange.contains(r))
  196. return;
  197. mapped = false;
  198. if (mappedType == MAP_READ_ONLY)
  199. {
  200. gl.bindBuffer(mapUsage, buffer);
  201. glUnmapBuffer(target);
  202. if (!ownsMemoryMap)
  203. memoryMap = nullptr;
  204. return;
  205. }
  206. // Orphan optimization - see fill().
  207. if (supportsOrphan() && mappedRange.first == 0 && mappedRange.getSize() == getSize())
  208. {
  209. usedoffset = 0;
  210. usedsize = getSize();
  211. }
  212. char *data = memoryMap + (usedoffset - mappedRange.getOffset());
  213. fill(usedoffset, usedsize, data);
  214. if (!ownsMemoryMap)
  215. {
  216. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  217. gfx->releaseBufferMapMemory(memoryMap);
  218. memoryMap = nullptr;
  219. }
  220. }
  221. bool Buffer::fill(size_t offset, size_t size, const void *data)
  222. {
  223. if (size == 0 || isImmutable() || dataUsage == BUFFERDATAUSAGE_READBACK)
  224. return false;
  225. size_t buffersize = getSize();
  226. if (!Range(0, buffersize).contains(Range(offset, size)))
  227. return false;
  228. GLenum gldatausage = OpenGL::getGLBufferDataUsage(dataUsage);
  229. gl.bindBuffer(mapUsage, buffer);
  230. if (supportsOrphan() && size == buffersize)
  231. {
  232. // "orphan" current buffer to avoid implicit synchronisation on the GPU:
  233. // http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
  234. glBufferData(target, (GLsizeiptr) buffersize, nullptr, gldatausage);
  235. #if LOVE_WINDOWS
  236. // TODO: Verify that this intel codepath is a useful optimization.
  237. if (gl.getVendor() == OpenGL::VENDOR_INTEL)
  238. glBufferData(target, (GLsizeiptr) buffersize, data, gldatausage);
  239. else
  240. #endif
  241. glBufferSubData(target, 0, (GLsizeiptr) buffersize, data);
  242. }
  243. else
  244. {
  245. glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, data);
  246. }
  247. return true;
  248. }
  249. void Buffer::clear(size_t offset, size_t size)
  250. {
  251. if (isImmutable())
  252. throw love::Exception("Cannot clear an immutable Buffer.");
  253. else if (isMapped())
  254. throw love::Exception("Cannot clear a mapped Buffer.");
  255. else if (offset + size > getSize())
  256. throw love::Exception("The given offset and size parameters to clear() are not within the Buffer's size.");
  257. else if (offset % 4 != 0 || size % 4 != 0)
  258. throw love::Exception("clear() must be used with offset and size parameters that are multiples of 4 bytes.");
  259. if (GLAD_VERSION_4_3)
  260. {
  261. gl.bindBuffer(mapUsage, buffer);
  262. glClearBufferSubData(target, GL_R8UI, offset, size, GL_RED, GL_UNSIGNED_BYTE, nullptr);
  263. }
  264. else
  265. {
  266. try
  267. {
  268. std::vector<uint8> emptydata(getSize());
  269. fill(0, getSize(), emptydata.data());
  270. }
  271. catch (std::exception &)
  272. {
  273. throw love::Exception("Out of memory.");
  274. }
  275. }
  276. }
  277. void Buffer::copyTo(love::graphics::Buffer *dest, size_t sourceoffset, size_t destoffset, size_t size)
  278. {
  279. // TODO: tracked state for these bind types?
  280. glBindBuffer(GL_COPY_READ_BUFFER, buffer);
  281. glBindBuffer(GL_COPY_WRITE_BUFFER, ((Buffer *) dest)->buffer);
  282. glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, sourceoffset, destoffset, size);
  283. }
  284. } // opengl
  285. } // graphics
  286. } // love