Graphics.cpp 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431
  1. #include "Graphics.h"
  2. #include "Buffer.h"
  3. #include "SDL_vulkan.h"
  4. #include "window/Window.h"
  5. #include "common/Exception.h"
  6. #include "Shader.h"
  7. #include "graphics/Texture.h"
  8. #include "Vulkan.h"
  9. #include <vector>
  10. #include <cstring>
  11. #include <set>
  12. #include <fstream>
  13. #include <iostream>
  14. #include <array>
  15. namespace love {
  16. namespace graphics {
  17. namespace vulkan {
  18. static VkIndexType getVulkanIndexBufferType(IndexDataType type) {
  19. switch (type) {
  20. case INDEX_UINT16: return VK_INDEX_TYPE_UINT16;
  21. case INDEX_UINT32: return VK_INDEX_TYPE_UINT32;
  22. default:
  23. throw love::Exception("unknown Index Data type");
  24. }
  25. }
  26. const std::vector<const char*> validationLayers = {
  27. "VK_LAYER_KHRONOS_validation"
  28. };
  29. const std::vector<const char*> deviceExtensions = {
  30. VK_KHR_SWAPCHAIN_EXTENSION_NAME
  31. };
  32. #ifdef NDEBUG
  33. const bool enableValidationLayers = false;
  34. #else
  35. const bool enableValidationLayers = true;
  36. #endif
  37. const int MAX_FRAMES_IN_FLIGHT = 2;
  38. static std::vector<char> readFile(const std::string& filename) {
  39. std::ifstream file(filename, std::ios::ate | std::ios::binary);
  40. if (!file.is_open()) {
  41. throw std::runtime_error("failed to open file!");
  42. }
  43. size_t fileSize = (size_t)file.tellg();
  44. std::vector<char> buffer(fileSize);
  45. file.seekg(0);
  46. file.read(buffer.data(), fileSize);
  47. file.close();
  48. return buffer;
  49. }
  50. const char* Graphics::getName() const {
  51. return "love.graphics.vulkan";
  52. }
  53. Graphics::Graphics() {
  54. }
  55. // START OVERRIDEN FUNCTIONS
  56. love::graphics::Buffer* Graphics::newBuffer(const love::graphics::Buffer::Settings& settings, const std::vector<love::graphics::Buffer::DataDeclaration>& format, const void* data, size_t size, size_t arraylength) {
  57. std::cout << "newBuffer ";
  58. return nullptr;
  59. }
  60. void Graphics::startRecordingGraphicsCommands() {
  61. vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
  62. while (true) {
  63. VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
  64. if (result == VK_ERROR_OUT_OF_DATE_KHR) {
  65. recreateSwapChain();
  66. continue;
  67. }
  68. else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
  69. throw love::Exception("failed to acquire swap chain image");
  70. }
  71. break;
  72. }
  73. VkCommandBufferBeginInfo beginInfo{};
  74. beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  75. beginInfo.flags = 0;
  76. beginInfo.pInheritanceInfo = nullptr;
  77. std::cout << "beginCommandBuffer(imageIndex=" << imageIndex << ") ";
  78. if (vkBeginCommandBuffer(commandBuffers.at(imageIndex), &beginInfo) != VK_SUCCESS) {
  79. throw love::Exception("failed to begin recording command buffer");
  80. }
  81. VkRenderPassBeginInfo renderPassInfo{};
  82. renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
  83. renderPassInfo.renderPass = renderPass;
  84. renderPassInfo.framebuffer = swapChainFramBuffers.at(imageIndex);
  85. renderPassInfo.renderArea.offset = { 0, 0 };
  86. renderPassInfo.renderArea.extent = swapChainExtent;
  87. renderPassInfo.clearValueCount = 1;
  88. renderPassInfo.pClearValues = &clearColor;
  89. const auto& commandBuffer = commandBuffers.at(imageIndex);
  90. vkCmdBeginRenderPass(commandBuffers.at(imageIndex), &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
  91. currentGraphicsPipeline = VK_NULL_HANDLE;
  92. }
  93. void Graphics::endRecordingGraphicsCommands() {
  94. const auto& commandBuffer = commandBuffers.at(imageIndex);
  95. std::cout << "endCommandBuffer(imageIndex=" << imageIndex << ") ";
  96. vkCmdEndRenderPass(commandBuffers.at(imageIndex));
  97. if (vkEndCommandBuffer(commandBuffers.at(imageIndex)) != VK_SUCCESS) {
  98. throw love::Exception("failed to record command buffer");
  99. }
  100. }
  101. void Graphics::present(void* screenshotCallbackdata) {
  102. flushBatchedDraws();
  103. endRecordingGraphicsCommands();
  104. prepareDraw(currentFrame);
  105. if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
  106. vkWaitForFences(device, 1, &imagesInFlight.at(imageIndex), VK_TRUE, UINT64_MAX);
  107. }
  108. imagesInFlight[imageIndex] = inFlightFences[currentFrame];
  109. VkSubmitInfo submitInfo{};
  110. submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  111. VkSemaphore waitSemaphores[] = { imageAvailableSemaphores.at(currentFrame) };
  112. VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
  113. submitInfo.waitSemaphoreCount = 1;
  114. submitInfo.pWaitSemaphores = waitSemaphores;
  115. submitInfo.pWaitDstStageMask = waitStages;
  116. submitInfo.commandBufferCount = 1;
  117. submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
  118. VkSemaphore signalSemaphores[] = { renderFinishedSemaphores.at(currentFrame) };
  119. submitInfo.signalSemaphoreCount = 1;
  120. submitInfo.pSignalSemaphores = signalSemaphores;
  121. vkResetFences(device, 1, &inFlightFences[currentFrame]);
  122. if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences.at(currentFrame)) != VK_SUCCESS) {
  123. throw love::Exception("failed to submit draw command buffer");
  124. }
  125. VkPresentInfoKHR presentInfo{};
  126. presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
  127. presentInfo.waitSemaphoreCount = 1;
  128. presentInfo.pWaitSemaphores = signalSemaphores;
  129. VkSwapchainKHR swapChains[] = { swapChain };
  130. presentInfo.swapchainCount = 1;
  131. presentInfo.pSwapchains = swapChains;
  132. presentInfo.pImageIndices = &imageIndex;
  133. VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo);
  134. if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
  135. framebufferResized = false;
  136. recreateSwapChain();
  137. }
  138. else if (result != VK_SUCCESS) {
  139. throw love::Exception("failed to present swap chain image");
  140. }
  141. std::cout << "present" << std::endl;
  142. currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
  143. updatedBatchedDrawBuffers();
  144. startRecordingGraphicsCommands();
  145. }
  146. void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) {
  147. std::cout << "setViewPortSize ";
  148. this->width = width;
  149. this->height = height;
  150. this->pixelWidth = pixelwidth;
  151. this->pixelHeight = pixelheight;
  152. resetProjection();
  153. recreateSwapChain();
  154. }
  155. bool Graphics::setMode(void* context, int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil, int msaa) {
  156. std::cout << "setMode ";
  157. createVulkanInstance();
  158. createSurface();
  159. pickPhysicalDevice();
  160. createLogicalDevice();
  161. initVMA();
  162. initCapabilities();
  163. createSwapChain();
  164. createImageViews();
  165. createRenderPass();
  166. createDefaultShaders();
  167. createDescriptorSetLayout();
  168. createFramebuffers();
  169. createCommandPool();
  170. createCommandBuffers();
  171. createUniformBuffers();
  172. createDefaultTexture();
  173. createDescriptorPool();
  174. createSyncObjects();
  175. startRecordingGraphicsCommands();
  176. currentFrame = 0;
  177. created = true;
  178. float whiteColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  179. batchedDrawBuffers.clear();
  180. batchedDrawBuffers.reserve(MAX_FRAMES_IN_FLIGHT);
  181. for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
  182. batchedDrawBuffers.emplace_back();
  183. // Initial sizes that should be good enough for most cases. It will
  184. // resize to fit if needed, later.
  185. batchedDrawBuffers[i].vertexBuffer1 = new StreamBuffer(vmaAllocator, BUFFERUSAGE_VERTEX, 1024 * 1024 * 1);
  186. batchedDrawBuffers[i].vertexBuffer2 = new StreamBuffer(vmaAllocator, BUFFERUSAGE_VERTEX, 256 * 1024 * 1);
  187. batchedDrawBuffers[i].indexBuffer = new StreamBuffer(vmaAllocator, BUFFERUSAGE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
  188. // sometimes the VertexColor is not set, so we manually adjust it to white color
  189. batchedDrawBuffers[i].constantColorBuffer = new StreamBuffer(vmaAllocator, BUFFERUSAGE_VERTEX, sizeof(whiteColor));
  190. auto mapInfo = batchedDrawBuffers[i].constantColorBuffer->map(sizeof(whiteColor));
  191. memcpy(mapInfo.data, whiteColor, sizeof(whiteColor));
  192. batchedDrawBuffers[i].constantColorBuffer->unmap(sizeof(whiteColor));
  193. batchedDrawBuffers[i].constantColorBuffer->markUsed(sizeof(whiteColor));
  194. }
  195. updatedBatchedDrawBuffers();
  196. return true;
  197. }
  198. void Graphics::initCapabilities() {
  199. std::cout << "initCapabilities ";
  200. // todo
  201. capabilities.features[FEATURE_MULTI_RENDER_TARGET_FORMATS] = false;
  202. capabilities.features[FEATURE_CLAMP_ZERO] = false;
  203. capabilities.features[FEATURE_CLAMP_ONE] = false;
  204. capabilities.features[FEATURE_BLEND_MINMAX] = false;
  205. capabilities.features[FEATURE_LIGHTEN] = false;
  206. capabilities.features[FEATURE_FULL_NPOT] = false;
  207. capabilities.features[FEATURE_PIXEL_SHADER_HIGHP] = false;
  208. capabilities.features[FEATURE_SHADER_DERIVATIVES] = false;
  209. capabilities.features[FEATURE_GLSL3] = false;
  210. capabilities.features[FEATURE_GLSL4] = false;
  211. capabilities.features[FEATURE_INSTANCING] = false;
  212. capabilities.features[FEATURE_TEXEL_BUFFER] = false;
  213. capabilities.features[FEATURE_INDEX_BUFFER_32BIT] = true;
  214. capabilities.features[FEATURE_COPY_BUFFER] = false;
  215. capabilities.features[FEATURE_COPY_BUFFER_TO_TEXTURE] = false;
  216. capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = false;
  217. capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = false;
  218. static_assert(FEATURE_MAX_ENUM == 17, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
  219. VkPhysicalDeviceProperties properties;
  220. vkGetPhysicalDeviceProperties(physicalDevice, &properties);
  221. capabilities.limits[LIMIT_POINT_SIZE] = properties.limits.pointSizeRange[1];
  222. capabilities.limits[LIMIT_TEXTURE_SIZE] = properties.limits.maxImageDimension2D;
  223. capabilities.limits[LIMIT_TEXTURE_LAYERS] = properties.limits.maxImageArrayLayers;
  224. capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = properties.limits.maxImageDimension3D;
  225. capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = properties.limits.maxImageDimensionCube;
  226. capabilities.limits[LIMIT_TEXEL_BUFFER_SIZE] = properties.limits.maxTexelBufferElements; // ?
  227. capabilities.limits[LIMIT_SHADER_STORAGE_BUFFER_SIZE] = properties.limits.maxStorageBufferRange; // ?
  228. capabilities.limits[LIMIT_THREADGROUPS_X] = 0; // todo
  229. capabilities.limits[LIMIT_THREADGROUPS_Y] = 0; // todo
  230. capabilities.limits[LIMIT_THREADGROUPS_Z] = 0; // todo
  231. capabilities.limits[LIMIT_RENDER_TARGETS] = 1; // todo
  232. capabilities.limits[LIMIT_TEXTURE_MSAA] = 1; // todo
  233. capabilities.limits[LIMIT_ANISOTROPY] = 1.0f; // todo
  234. static_assert(LIMIT_MAX_ENUM == 13, "Graphics::initCapabilities must be updated when adding a new system limit!");
  235. capabilities.textureTypes[TEXTURE_2D] = true;
  236. capabilities.textureTypes[TEXTURE_VOLUME] = false;
  237. capabilities.textureTypes[TEXTURE_2D_ARRAY] = false;
  238. capabilities.textureTypes[TEXTURE_CUBE] = false;
  239. }
  240. void Graphics::unSetMode() {
  241. std::cout << "unSetMode ";
  242. created = false;
  243. cleanup();
  244. }
  245. void Graphics::draw(const DrawIndexedCommand& cmd) {
  246. std::cout << "drawIndexed ";
  247. std::vector<VkBuffer> buffers;
  248. std::vector<VkDeviceSize> offsets;
  249. bool useConstantColorBuffer;
  250. GraphicsPipelineConfiguration configuration;
  251. createVulkanVertexFormat(*cmd.attributes, useConstantColorBuffer, configuration);
  252. for (uint32_t i = 0; i < 2; i++) {
  253. buffers.push_back((VkBuffer)cmd.buffers->info[i].buffer->getHandle());
  254. offsets.push_back((VkDeviceSize)cmd.buffers->info[i].offset);
  255. }
  256. if (useConstantColorBuffer) {
  257. buffers.push_back((VkBuffer)batchedDrawBuffers[currentFrame].constantColorBuffer->getHandle());
  258. offsets.push_back((VkDeviceSize)0);
  259. }
  260. if (cmd.texture == nullptr) {
  261. setTexture(standardTexture);
  262. }
  263. else {
  264. setTexture(cmd.texture);
  265. }
  266. ensureGraphicsPipelineConfiguration(configuration);
  267. vkCmdBindDescriptorSets(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, getDescriptorSet(currentFrame), 0, nullptr);
  268. vkCmdBindVertexBuffers(commandBuffers.at(imageIndex), 0, buffers.size(), buffers.data(), offsets.data());
  269. vkCmdBindIndexBuffer(commandBuffers.at(imageIndex), (VkBuffer)cmd.indexBuffer->getHandle(), 0, getVulkanIndexBufferType(cmd.indexType));
  270. vkCmdDrawIndexed(commandBuffers.at(imageIndex), static_cast<uint32_t>(cmd.indexCount), 1, 0, 0, 0);
  271. }
  272. graphics::StreamBuffer* Graphics::newStreamBuffer(BufferUsage type, size_t size) {
  273. std::cout << "newStreamBuffer ";
  274. return new StreamBuffer(vmaAllocator, type, size);
  275. }
  276. Matrix4 Graphics::computeDeviceProjection(const Matrix4& projection, bool rendertotexture) const {
  277. uint32 flags = DEVICE_PROJECTION_DEFAULT;
  278. return calculateDeviceProjection(projection, 0);
  279. }
  280. // END IMPLEMENTATION OVERRIDDEN FUNCTIONS
  281. void Graphics::updatedBatchedDrawBuffers() {
  282. batchedDrawState.vb[0] = batchedDrawBuffers[currentFrame].vertexBuffer1;
  283. batchedDrawState.vb[0]->nextFrame();
  284. batchedDrawState.vb[1] = batchedDrawBuffers[currentFrame].vertexBuffer2;
  285. batchedDrawState.vb[1]->nextFrame();
  286. batchedDrawState.indexBuffer = batchedDrawBuffers[currentFrame].indexBuffer;
  287. batchedDrawState.indexBuffer->nextFrame();
  288. }
  289. VkDescriptorSet* Graphics::getDescriptorSet(int currentFrame) {
  290. auto it = textureToDescriptorSetsMap.find(currentTexture);
  291. if (it == textureToDescriptorSetsMap.end()) {
  292. textureToDescriptorSetsMap[currentTexture] = createDescriptorSets(currentTexture);
  293. }
  294. return &textureToDescriptorSetsMap.at(currentTexture)[currentFrame];
  295. }
  296. VkCommandBuffer Graphics::beginSingleTimeCommands() {
  297. VkCommandBufferAllocateInfo allocInfo{};
  298. allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  299. allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
  300. allocInfo.commandPool = commandPool;
  301. allocInfo.commandBufferCount = 1;
  302. VkCommandBuffer commandBuffer;
  303. vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);
  304. VkCommandBufferBeginInfo beginInfo{};
  305. beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  306. beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
  307. vkBeginCommandBuffer(commandBuffer, &beginInfo);
  308. return commandBuffer;
  309. }
  310. void Graphics::endSingleTimeCommands(VkCommandBuffer commandBuffer) {
  311. vkEndCommandBuffer(commandBuffer);
  312. VkSubmitInfo submitInfo{};
  313. submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  314. submitInfo.commandBufferCount = 1;
  315. submitInfo.pCommandBuffers = &commandBuffer;
  316. vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
  317. vkQueueWaitIdle(graphicsQueue);
  318. vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
  319. }
  320. void Graphics::prepareDraw(uint32_t currentImage) {
  321. auto& buffer = uniformBuffers.at(currentImage);
  322. love::graphics::Shader::BuiltinUniformData data;
  323. data.transformMatrix = getTransform();
  324. data.projectionMatrix = getDeviceProjection();
  325. // The normal matrix is the transpose of the inverse of the rotation portion
  326. // (top-left 3x3) of the transform matrix.
  327. {
  328. Matrix3 normalmatrix = Matrix3(data.transformMatrix).transposedInverse();
  329. const float* e = normalmatrix.getElements();
  330. for (int i = 0; i < 3; i++)
  331. {
  332. data.normalMatrix[i].x = e[i * 3 + 0];
  333. data.normalMatrix[i].y = e[i * 3 + 1];
  334. data.normalMatrix[i].z = e[i * 3 + 2];
  335. data.normalMatrix[i].w = 0.0f;
  336. }
  337. }
  338. // Store DPI scale in an unused component of another vector.
  339. data.normalMatrix[0].w = (float)getCurrentDPIScale();
  340. // Same with point size.
  341. data.normalMatrix[1].w = getPointSize();
  342. data.screenSizeParams.x = swapChainExtent.width;
  343. data.screenSizeParams.y = swapChainExtent.height;
  344. data.screenSizeParams.z = 1.0f;
  345. data.screenSizeParams.w = 0.0f;
  346. data.constantColor = getColor();
  347. gammaCorrectColor(data.constantColor);
  348. auto mappedInfo = buffer->map(0);
  349. memcpy(mappedInfo.data, &data, sizeof(data));
  350. buffer->unmap(0);
  351. }
  352. void Graphics::createVulkanInstance() {
  353. if (enableValidationLayers && !checkValidationSupport()) {
  354. throw love::Exception("validation layers requested, but not available");
  355. }
  356. VkApplicationInfo appInfo{};
  357. appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
  358. appInfo.pApplicationName = "LOVE";
  359. appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); //todo, get this version from somewhere else?
  360. appInfo.pEngineName = "LOVE Engine";
  361. appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); //todo, same as above
  362. appInfo.apiVersion = VK_API_VERSION_1_0;
  363. VkInstanceCreateInfo createInfo{};
  364. createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
  365. createInfo.pApplicationInfo = &appInfo;
  366. createInfo.pNext = nullptr;
  367. auto window = Module::getInstance<love::window::Window>(M_WINDOW);
  368. const void* handle = window->getHandle();
  369. unsigned int count;
  370. if (SDL_Vulkan_GetInstanceExtensions((SDL_Window*)handle, &count, nullptr) != SDL_TRUE) {
  371. throw love::Exception("couldn't retrieve sdl vulkan extensions");
  372. }
  373. std::vector<const char*> extensions = {}; // can add more here
  374. size_t addition_extension_count = extensions.size();
  375. extensions.resize(addition_extension_count + count);
  376. if (SDL_Vulkan_GetInstanceExtensions((SDL_Window*)handle, &count, extensions.data() + addition_extension_count) != SDL_TRUE) {
  377. throw love::Exception("couldn't retrieve sdl vulkan extensions");
  378. }
  379. createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
  380. createInfo.ppEnabledExtensionNames = extensions.data();
  381. if (enableValidationLayers) {
  382. createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
  383. createInfo.ppEnabledLayerNames = validationLayers.data();
  384. }
  385. else {
  386. createInfo.enabledLayerCount = 0;
  387. createInfo.ppEnabledLayerNames = nullptr;
  388. }
  389. if (vkCreateInstance(
  390. &createInfo,
  391. nullptr,
  392. &instance) != VK_SUCCESS) {
  393. throw love::Exception("couldn't create vulkan instance");
  394. }
  395. }
  396. bool Graphics::checkValidationSupport() {
  397. uint32_t layerCount;
  398. vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
  399. std::vector<VkLayerProperties> availableLayers(layerCount);
  400. vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
  401. for (const char* layerName : validationLayers) {
  402. bool layerFound = false;
  403. for (const auto& layerProperties : availableLayers) {
  404. if (strcmp(layerName, layerProperties.layerName) == 0) {
  405. layerFound = true;
  406. break;
  407. }
  408. }
  409. if (!layerFound) {
  410. return false;
  411. }
  412. }
  413. return true;
  414. }
  415. void Graphics::pickPhysicalDevice() {
  416. uint32_t deviceCount = 0;
  417. vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
  418. if (deviceCount == 0) {
  419. throw love::Exception("failed to find GPUs with Vulkan support");
  420. }
  421. std::vector<VkPhysicalDevice> devices(deviceCount);
  422. vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
  423. std::multimap<int, VkPhysicalDevice> candidates;
  424. for (const auto& device : devices) {
  425. int score = rateDeviceSuitability(device);
  426. candidates.insert(std::make_pair(score, device));
  427. }
  428. if (candidates.rbegin()->first > 0) {
  429. physicalDevice = candidates.rbegin()->second;
  430. }
  431. else {
  432. throw love::Exception("failed to find a suitable gpu");
  433. }
  434. }
  435. bool Graphics::checkDeviceExtensionSupport(VkPhysicalDevice device) {
  436. uint32_t extensionCount;
  437. vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
  438. std::vector<VkExtensionProperties> availableExtensions(extensionCount);
  439. vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
  440. std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
  441. for (const auto& extension : availableExtensions) {
  442. requiredExtensions.erase(extension.extensionName);
  443. }
  444. return requiredExtensions.empty();
  445. }
  446. // if the score is nonzero then the device is suitable.
  447. // A higher rating means generally better performance
  448. // if the score is 0 the device is unsuitable
  449. int Graphics::rateDeviceSuitability(VkPhysicalDevice device) {
  450. VkPhysicalDeviceProperties deviceProperties;
  451. VkPhysicalDeviceFeatures deviceFeatures;
  452. vkGetPhysicalDeviceProperties(device, &deviceProperties);
  453. vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
  454. int score = 1;
  455. // optional
  456. if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
  457. score += 1000;
  458. }
  459. // definitely needed
  460. QueueFamilyIndices indices = findQueueFamilies(device);
  461. if (!indices.isComplete()) {
  462. score = 0;
  463. }
  464. bool extensionsSupported = checkDeviceExtensionSupport(device);
  465. if (!extensionsSupported) {
  466. score = 0;
  467. }
  468. if (extensionsSupported) {
  469. auto swapChainSupport = querySwapChainSupport(device);
  470. bool swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
  471. if (!swapChainAdequate) {
  472. score = 0;
  473. }
  474. }
  475. if (!deviceFeatures.samplerAnisotropy) {
  476. score = 0;
  477. }
  478. return score;
  479. }
  480. Graphics::QueueFamilyIndices Graphics::findQueueFamilies(VkPhysicalDevice device) {
  481. QueueFamilyIndices indices;
  482. uint32_t queueFamilyCount = 0;
  483. vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
  484. std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
  485. vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
  486. int i = 0;
  487. for (const auto& queueFamily : queueFamilies) {
  488. if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
  489. indices.graphicsFamily = i;
  490. }
  491. VkBool32 presentSupport = false;
  492. vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
  493. if (presentSupport) {
  494. indices.presentFamily = i;
  495. }
  496. if (indices.isComplete()) {
  497. break;
  498. }
  499. i++;
  500. }
  501. return indices;
  502. }
  503. void Graphics::createLogicalDevice() {
  504. QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
  505. std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
  506. std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
  507. float queuePriority = 1.0f;
  508. for (uint32_t queueFamily : uniqueQueueFamilies) {
  509. VkDeviceQueueCreateInfo queueCreateInfo{};
  510. queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
  511. queueCreateInfo.queueFamilyIndex = queueFamily;
  512. queueCreateInfo.queueCount = 1;
  513. queueCreateInfo.pQueuePriorities = &queuePriority;
  514. queueCreateInfos.push_back(queueCreateInfo);
  515. }
  516. VkPhysicalDeviceFeatures deviceFeatures{};
  517. deviceFeatures.samplerAnisotropy = VK_TRUE;
  518. VkDeviceCreateInfo createInfo{};
  519. createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
  520. createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
  521. createInfo.pQueueCreateInfos = queueCreateInfos.data();
  522. createInfo.pEnabledFeatures = &deviceFeatures;
  523. createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
  524. createInfo.ppEnabledExtensionNames = deviceExtensions.data();
  525. // can this be removed?
  526. if (enableValidationLayers) {
  527. createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
  528. createInfo.ppEnabledLayerNames = validationLayers.data();
  529. }
  530. else {
  531. createInfo.enabledLayerCount = 0;
  532. }
  533. if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
  534. throw love::Exception("failed to create logical device");
  535. }
  536. vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
  537. vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
  538. }
  539. void Graphics::initVMA() {
  540. VmaVulkanFunctions vulkanFunctions = {};
  541. vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr;
  542. vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;
  543. VmaAllocatorCreateInfo allocatorCreateInfo = {};
  544. allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2;
  545. allocatorCreateInfo.physicalDevice = physicalDevice;
  546. allocatorCreateInfo.device = device;
  547. allocatorCreateInfo.instance = instance;
  548. allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
  549. vmaCreateAllocator(&allocatorCreateInfo, &vmaAllocator);
  550. }
  551. void Graphics::createSurface() {
  552. auto window = Module::getInstance<love::window::Window>(M_WINDOW);
  553. const void* handle = window->getHandle();
  554. if (SDL_Vulkan_CreateSurface((SDL_Window*)handle, instance, &surface) != SDL_TRUE) {
  555. throw love::Exception("failed to create window surface");
  556. }
  557. }
  558. Graphics::SwapChainSupportDetails Graphics::querySwapChainSupport(VkPhysicalDevice device) {
  559. SwapChainSupportDetails details;
  560. vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
  561. uint32_t formatCount;
  562. vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
  563. if (formatCount != 0) {
  564. details.formats.resize(formatCount);
  565. vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
  566. }
  567. uint32_t presentModeCount;
  568. vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
  569. if (presentModeCount != 0) {
  570. details.presentModes.resize(presentModeCount);
  571. vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
  572. }
  573. return details;
  574. }
  575. void Graphics::createSwapChain() {
  576. SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);
  577. VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
  578. VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
  579. VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
  580. uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
  581. if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
  582. imageCount = swapChainSupport.capabilities.maxImageCount;
  583. }
  584. VkSwapchainCreateInfoKHR createInfo{};
  585. createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
  586. createInfo.surface = surface;
  587. createInfo.minImageCount = imageCount;
  588. createInfo.imageFormat = surfaceFormat.format;
  589. createInfo.imageColorSpace = surfaceFormat.colorSpace;
  590. createInfo.imageExtent = extent;
  591. createInfo.imageArrayLayers = 1;
  592. createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
  593. QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
  594. uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
  595. if (indices.graphicsFamily != indices.presentFamily) {
  596. createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
  597. createInfo.queueFamilyIndexCount = 2;
  598. createInfo.pQueueFamilyIndices = queueFamilyIndices;
  599. }
  600. else {
  601. createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
  602. createInfo.queueFamilyIndexCount = 0;
  603. createInfo.pQueueFamilyIndices = nullptr;
  604. }
  605. createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
  606. createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
  607. createInfo.presentMode = presentMode;
  608. createInfo.clipped = VK_TRUE;
  609. createInfo.oldSwapchain = VK_NULL_HANDLE;
  610. if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
  611. throw love::Exception("failed to create swap chain");
  612. }
  613. vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
  614. swapChainImages.resize(imageCount);
  615. vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
  616. swapChainImageFormat = surfaceFormat.format;
  617. swapChainExtent = extent;
  618. }
  619. VkSurfaceFormatKHR Graphics::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
  620. for (const auto& availableFormat : availableFormats) {
  621. if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
  622. return availableFormat;
  623. }
  624. }
  625. return availableFormats[0];
  626. }
  627. VkPresentModeKHR Graphics::chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {
  628. // needed ?
  629. for (const auto& availablePresentMode : availablePresentModes) {
  630. if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
  631. return availablePresentMode;
  632. }
  633. }
  634. return VK_PRESENT_MODE_FIFO_KHR;
  635. }
  636. VkExtent2D Graphics::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
  637. if (capabilities.currentExtent.width != UINT32_MAX) {
  638. return capabilities.currentExtent;
  639. }
  640. else {
  641. auto window = Module::getInstance<love::window::Window>(M_WINDOW);
  642. const void* handle = window->getHandle();
  643. int width, height;
  644. // is this the equivalent of glfwGetFramebufferSize ?
  645. SDL_Vulkan_GetDrawableSize((SDL_Window*)handle, &width, &height);
  646. VkExtent2D actualExtent = {
  647. static_cast<uint32_t>(width),
  648. static_cast<uint32_t>(height)
  649. };
  650. actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
  651. actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
  652. return actualExtent;
  653. }
  654. }
  655. void Graphics::createImageViews() {
  656. swapChainImageViews.resize(swapChainImages.size());
  657. for (size_t i = 0; i < swapChainImages.size(); i++) {
  658. VkImageViewCreateInfo createInfo{};
  659. createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  660. createInfo.image = swapChainImages.at(i);
  661. createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
  662. createInfo.format = swapChainImageFormat;
  663. createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
  664. createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
  665. createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
  666. createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
  667. createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  668. createInfo.subresourceRange.baseMipLevel = 0;
  669. createInfo.subresourceRange.levelCount = 1;
  670. createInfo.subresourceRange.baseArrayLayer = 0;
  671. createInfo.subresourceRange.layerCount = 1;
  672. if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews.at(i)) != VK_SUCCESS) {
  673. throw love::Exception("failed to create image views");
  674. }
  675. }
  676. }
  677. void Graphics::createRenderPass() {
  678. VkAttachmentDescription colorAttachment{};
  679. colorAttachment.format = swapChainImageFormat;
  680. colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
  681. colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
  682. colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
  683. colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
  684. colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
  685. colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  686. colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
  687. VkAttachmentReference colorAttachmentRef{};
  688. colorAttachmentRef.attachment = 0;
  689. colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
  690. VkSubpassDescription subpass{};
  691. subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
  692. subpass.colorAttachmentCount = 1;
  693. subpass.pColorAttachments = &colorAttachmentRef;
  694. VkSubpassDependency dependency{};
  695. dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
  696. dependency.dstSubpass = 0;
  697. dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  698. dependency.srcAccessMask = 0;
  699. dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  700. dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  701. VkRenderPassCreateInfo renderPassInfo{};
  702. renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
  703. renderPassInfo.attachmentCount = 1;
  704. renderPassInfo.pAttachments = &colorAttachment;
  705. renderPassInfo.subpassCount = 1;
  706. renderPassInfo.pSubpasses = &subpass;
  707. renderPassInfo.dependencyCount = 1;
  708. renderPassInfo.pDependencies = &dependency;
  709. if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
  710. throw love::Exception("failed to create render pass");
  711. }
  712. }
  713. static VkShaderModule createShaderModule(VkDevice device, const std::vector<char>& code) {
  714. VkShaderModuleCreateInfo createInfo{};
  715. createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
  716. createInfo.codeSize = code.size();
  717. createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
  718. VkShaderModule shaderModule;
  719. if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
  720. throw love::Exception("failed to create shader module");
  721. }
  722. return shaderModule;
  723. }
  724. void Graphics::createDefaultShaders() {
  725. for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) {
  726. auto stype = (Shader::StandardShader)i;
  727. if (!Shader::standardShaders[i]) {
  728. std::vector<std::string> stages;
  729. stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_VERTEX));
  730. stages.push_back(Shader::getDefaultCode(stype, SHADERSTAGE_PIXEL));
  731. Shader::standardShaders[i] = newShader(stages, { { {"vulkan", "1"} } });
  732. }
  733. }
  734. }
  735. void Graphics::createDescriptorSetLayout() {
  736. VkDescriptorSetLayoutBinding uboLayoutBinding{};
  737. uboLayoutBinding.binding = 0;
  738. uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  739. uboLayoutBinding.descriptorCount = 1;
  740. uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
  741. VkDescriptorSetLayoutBinding samplerLayoutBinding{};
  742. samplerLayoutBinding.binding = 1;
  743. samplerLayoutBinding.descriptorCount = 1;
  744. samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
  745. samplerLayoutBinding.pImmutableSamplers = nullptr;
  746. samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
  747. std::array<VkDescriptorSetLayoutBinding, 2> bindings = { uboLayoutBinding, samplerLayoutBinding };
  748. VkDescriptorSetLayoutCreateInfo layoutInfo{};
  749. layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
  750. layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
  751. layoutInfo.pBindings = bindings.data();
  752. if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout) != VK_SUCCESS) {
  753. throw love::Exception("failed to create descriptor set layout");
  754. }
  755. }
  756. void Graphics::createUniformBuffers() {
  757. VkDeviceSize bufferSize = sizeof(graphics::Shader::BuiltinUniformData);
  758. for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
  759. uniformBuffers.push_back(std::make_unique<StreamBuffer>(vmaAllocator, BUFFERUSAGE_UNIFORM, bufferSize));
  760. }
  761. }
  762. void Graphics::createDescriptorPool() {
  763. std::array<VkDescriptorPoolSize, 2> poolSizes{};
  764. poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  765. poolSizes[0].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
  766. poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
  767. poolSizes[1].descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
  768. VkDescriptorPoolCreateInfo poolInfo{};
  769. poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
  770. poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
  771. poolInfo.pPoolSizes = poolSizes.data();
  772. // FIXME: When using more than 128 textures at once we will run out of memory.
  773. // we probably want to reuse descriptors per flight image
  774. // and use multiple pools in case of too many allocations
  775. poolInfo.maxSets = 128 * static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
  776. if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
  777. throw love::Exception("failed to create descriptor pool");
  778. }
  779. }
  780. std::vector<VkDescriptorSet> Graphics::createDescriptorSets(graphics::Texture* texture) {
  781. std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout);
  782. VkDescriptorSetAllocateInfo allocInfo{};
  783. allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
  784. allocInfo.descriptorPool = descriptorPool;
  785. allocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
  786. allocInfo.pSetLayouts = layouts.data();
  787. std::vector<VkDescriptorSet> newDescriptorSets;
  788. newDescriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
  789. VkResult result = vkAllocateDescriptorSets(device, &allocInfo, newDescriptorSets.data());
  790. if (result != VK_SUCCESS) {
  791. switch (result) {
  792. case VK_ERROR_OUT_OF_HOST_MEMORY:
  793. throw love::Exception("failed to allocate descriptor sets: out of host memory");
  794. case VK_ERROR_OUT_OF_DEVICE_MEMORY:
  795. throw love::Exception("failed to allocate descriptor sets: out of device memory");
  796. case VK_ERROR_FRAGMENTED_POOL:
  797. throw love::Exception("failed to allocate descriptor sets: fragmented pool");
  798. case VK_ERROR_OUT_OF_POOL_MEMORY:
  799. throw love::Exception("failed to allocate descriptor sets: out of pool memory");
  800. default:
  801. throw love::Exception("failed to allocate descriptor sets");
  802. }
  803. }
  804. for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
  805. VkDescriptorBufferInfo bufferInfo{};
  806. bufferInfo.buffer = (VkBuffer)uniformBuffers.at(i)->getHandle();
  807. bufferInfo.offset = 0;
  808. bufferInfo.range = sizeof(graphics::Shader::BuiltinUniformData);
  809. VkDescriptorImageInfo imageInfo{};
  810. imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
  811. Texture* vkTexture = (Texture*)texture;
  812. imageInfo.imageView = vkTexture->getImageView();
  813. imageInfo.sampler = vkTexture->getSampler();
  814. std::array<VkWriteDescriptorSet, 2> descriptorWrite{};
  815. descriptorWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
  816. descriptorWrite[0].dstSet = newDescriptorSets[i];
  817. descriptorWrite[0].dstBinding = 0;
  818. descriptorWrite[0].dstArrayElement = 0;
  819. descriptorWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  820. descriptorWrite[0].descriptorCount = 1;
  821. descriptorWrite[0].pBufferInfo = &bufferInfo;
  822. descriptorWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
  823. descriptorWrite[1].dstSet = newDescriptorSets[i];
  824. descriptorWrite[1].dstBinding = 1;
  825. descriptorWrite[1].dstArrayElement = 0;
  826. descriptorWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
  827. descriptorWrite[1].descriptorCount = 1;
  828. descriptorWrite[1].pImageInfo = &imageInfo;
  829. vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrite.size()), descriptorWrite.data(), 0, nullptr);
  830. }
  831. return newDescriptorSets;
  832. }
  833. void Graphics::createVulkanVertexFormat(
  834. VertexAttributes vertexAttributes,
  835. bool& useConstantVertexColor,
  836. GraphicsPipelineConfiguration& configuration) {
  837. std::set<uint32_t> usedBuffers;
  838. std::vector<VkVertexInputBindingDescription> bindingDescriptions;
  839. std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
  840. auto allBits = vertexAttributes.enableBits;
  841. bool usesColor = false;
  842. for (uint32_t i = 0; i < 32; i++) { // change to loop like in opengl implementation ?
  843. uint32 bit = 1u << i;
  844. if (allBits & bit) {
  845. if (i == ATTRIB_COLOR) {
  846. usesColor = true;
  847. }
  848. auto attrib = vertexAttributes.attribs[i];
  849. auto bufferBinding = attrib.bufferIndex;
  850. if (usedBuffers.find(bufferBinding) == usedBuffers.end()) { // use .contains() when c++20 is enabled
  851. usedBuffers.insert(bufferBinding);
  852. VkVertexInputBindingDescription bindingDescription{};
  853. bindingDescription.binding = bufferBinding;
  854. bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
  855. bindingDescription.stride = vertexAttributes.bufferLayouts[bufferBinding].stride;
  856. bindingDescriptions.push_back(bindingDescription);
  857. }
  858. VkVertexInputAttributeDescription attributeDescription{};
  859. attributeDescription.location = i;
  860. attributeDescription.binding = bufferBinding;
  861. attributeDescription.offset = attrib.offsetFromVertex;
  862. attributeDescription.format = Vulkan::getVulkanVertexFormat(attrib.format);
  863. attributeDescriptions.push_back(attributeDescription);
  864. }
  865. }
  866. // do we need to use a constant VertexColor?
  867. if (!usesColor) {
  868. constexpr uint32_t constantColorBufferBinding = 2;
  869. VkVertexInputBindingDescription bindingDescription{};
  870. bindingDescription.binding = constantColorBufferBinding;
  871. bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
  872. bindingDescription.stride = 0; // no stride, will always read the same color multiple times.
  873. bindingDescriptions.push_back(bindingDescription);
  874. VkVertexInputAttributeDescription attributeDescription{};
  875. attributeDescription.binding = constantColorBufferBinding;
  876. attributeDescription.location = ATTRIB_COLOR;
  877. attributeDescription.offset = 0;
  878. attributeDescription.format = VK_FORMAT_R32G32B32A32_SFLOAT;
  879. useConstantVertexColor = true;
  880. }
  881. else {
  882. useConstantVertexColor = false;
  883. }
  884. configuration.vertexInputBindingDescriptions = bindingDescriptions;
  885. configuration.vertexInputAttributeDescriptions = attributeDescriptions;
  886. }
  887. VkPipeline Graphics::createGraphicsPipeline(GraphicsPipelineConfiguration configuration) {
  888. auto shader = reinterpret_cast<love::graphics::vulkan::Shader*>(love::graphics::vulkan::Shader::standardShaders[Shader::STANDARD_DEFAULT]);
  889. auto shaderStages = shader->getShaderStages();
  890. VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
  891. vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
  892. vertexInputInfo.vertexBindingDescriptionCount = configuration.vertexInputBindingDescriptions.size();
  893. vertexInputInfo.pVertexBindingDescriptions = configuration.vertexInputBindingDescriptions.data();
  894. vertexInputInfo.vertexAttributeDescriptionCount = configuration.vertexInputAttributeDescriptions.size();
  895. vertexInputInfo.pVertexAttributeDescriptions = configuration.vertexInputAttributeDescriptions.data();
  896. VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
  897. inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
  898. inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
  899. inputAssembly.primitiveRestartEnable = VK_FALSE;
  900. VkViewport viewport{};
  901. viewport.x = 0.0f;
  902. viewport.y = 0.0f;
  903. viewport.width = (float)swapChainExtent.width;
  904. viewport.height = (float)swapChainExtent.height;
  905. viewport.minDepth = 0.0f;
  906. viewport.maxDepth = 1.0f;
  907. VkRect2D scissor{};
  908. scissor.offset = { 0, 0 };
  909. scissor.extent = swapChainExtent;
  910. VkPipelineViewportStateCreateInfo viewportState{};
  911. viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
  912. viewportState.viewportCount = 1;
  913. viewportState.pViewports = &viewport;
  914. viewportState.scissorCount = 1;
  915. viewportState.pScissors = &scissor;
  916. VkPipelineRasterizationStateCreateInfo rasterizer{};
  917. rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
  918. rasterizer.depthClampEnable = VK_FALSE;
  919. rasterizer.rasterizerDiscardEnable = VK_FALSE;
  920. rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
  921. rasterizer.lineWidth = 1.0f;
  922. rasterizer.cullMode = VK_CULL_MODE_FRONT_BIT;
  923. rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
  924. rasterizer.depthBiasEnable = VK_FALSE;
  925. rasterizer.depthBiasConstantFactor = 0.0f;
  926. rasterizer.depthBiasClamp = 0.0f;
  927. rasterizer.depthBiasSlopeFactor = 0.0f;
  928. VkPipelineMultisampleStateCreateInfo multisampling{};
  929. multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
  930. multisampling.sampleShadingEnable = VK_FALSE;
  931. multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
  932. multisampling.minSampleShading = 1.0f; // Optional
  933. multisampling.pSampleMask = nullptr; // Optional
  934. multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
  935. multisampling.alphaToOneEnable = VK_FALSE; // Optional
  936. VkPipelineColorBlendAttachmentState colorBlendAttachment{};
  937. colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
  938. colorBlendAttachment.blendEnable = VK_FALSE;
  939. VkPipelineColorBlendStateCreateInfo colorBlending{};
  940. colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
  941. colorBlending.logicOpEnable = VK_FALSE;
  942. colorBlending.logicOp = VK_LOGIC_OP_COPY;
  943. colorBlending.attachmentCount = 1;
  944. colorBlending.pAttachments = &colorBlendAttachment;
  945. colorBlending.blendConstants[0] = 0.0f;
  946. colorBlending.blendConstants[1] = 0.0f;
  947. colorBlending.blendConstants[2] = 0.0f;
  948. colorBlending.blendConstants[3] = 0.0f;
  949. VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
  950. pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
  951. pipelineLayoutInfo.setLayoutCount = 1;
  952. pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout;
  953. pipelineLayoutInfo.pushConstantRangeCount = 0;
  954. if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
  955. throw love::Exception("failed to create pipeline layout");
  956. }
  957. VkGraphicsPipelineCreateInfo pipelineInfo{};
  958. pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
  959. pipelineInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
  960. pipelineInfo.pStages = shaderStages.data();
  961. pipelineInfo.pVertexInputState = &vertexInputInfo;
  962. pipelineInfo.pInputAssemblyState = &inputAssembly;
  963. pipelineInfo.pViewportState = &viewportState;
  964. pipelineInfo.pRasterizationState = &rasterizer;
  965. pipelineInfo.pMultisampleState = &multisampling;
  966. pipelineInfo.pDepthStencilState = nullptr;
  967. pipelineInfo.pColorBlendState = &colorBlending;
  968. pipelineInfo.pDynamicState = nullptr;
  969. pipelineInfo.layout = pipelineLayout;
  970. pipelineInfo.renderPass = renderPass;
  971. pipelineInfo.subpass = 0;
  972. pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
  973. pipelineInfo.basePipelineIndex = -1;
  974. VkPipeline graphicsPipeline;
  975. if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
  976. throw love::Exception("failed to create graphics pipeline");
  977. }
  978. return graphicsPipeline;
  979. }
  980. void Graphics::ensureGraphicsPipelineConfiguration(GraphicsPipelineConfiguration configuration) {
  981. VkPipeline pipeline = VK_NULL_HANDLE;
  982. for (auto const& p : graphicsPipelines) {
  983. if (p.first == configuration) {
  984. pipeline = p.second;
  985. break;
  986. }
  987. }
  988. if (pipeline != VK_NULL_HANDLE) {
  989. if (currentGraphicsPipeline != pipeline) {
  990. vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
  991. currentGraphicsPipeline = pipeline;
  992. }
  993. } else {
  994. VkPipeline newPipeLine = createGraphicsPipeline(configuration);
  995. graphicsPipelines.push_back(std::make_pair(configuration, newPipeLine));
  996. vkCmdBindPipeline(commandBuffers.at(imageIndex), VK_PIPELINE_BIND_POINT_GRAPHICS, newPipeLine);
  997. currentGraphicsPipeline = newPipeLine;
  998. }
  999. }
  1000. void Graphics::createFramebuffers() {
  1001. swapChainFramBuffers.resize(swapChainImageViews.size());
  1002. for (size_t i = 0; i < swapChainImageViews.size(); i++) {
  1003. VkImageView attachments[] = {
  1004. swapChainImageViews.at(i)
  1005. };
  1006. VkFramebufferCreateInfo framebufferInfo{};
  1007. framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
  1008. framebufferInfo.renderPass = renderPass;
  1009. framebufferInfo.attachmentCount = 1;
  1010. framebufferInfo.pAttachments = attachments;
  1011. framebufferInfo.width = swapChainExtent.width;
  1012. framebufferInfo.height = swapChainExtent.height;
  1013. framebufferInfo.layers = 1;
  1014. if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramBuffers.at(i)) != VK_SUCCESS) {
  1015. throw love::Exception("failed to create framebuffers");
  1016. }
  1017. }
  1018. }
  1019. void Graphics::createCommandPool() {
  1020. QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
  1021. VkCommandPoolCreateInfo poolInfo{};
  1022. poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
  1023. poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
  1024. poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
  1025. if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
  1026. throw love::Exception("failed to create command pool");
  1027. }
  1028. }
  1029. void Graphics::createCommandBuffers() {
  1030. commandBuffers.resize(swapChainFramBuffers.size());
  1031. VkCommandBufferAllocateInfo allocInfo{};
  1032. allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  1033. allocInfo.commandPool = commandPool;
  1034. allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
  1035. allocInfo.commandBufferCount = (uint32_t)commandBuffers.size();
  1036. if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
  1037. throw love::Exception("failed to allocate command buffers");
  1038. }
  1039. }
  1040. void Graphics::createSyncObjects() {
  1041. imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
  1042. renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
  1043. inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
  1044. imagesInFlight.resize(swapChainImages.size(), VK_NULL_HANDLE);
  1045. VkSemaphoreCreateInfo semaphoreInfo{};
  1046. semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
  1047. VkFenceCreateInfo fenceInfo{};
  1048. fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
  1049. fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
  1050. for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
  1051. if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores.at(i)) != VK_SUCCESS ||
  1052. vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores.at(i)) != VK_SUCCESS ||
  1053. vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences.at(i)) != VK_SUCCESS) {
  1054. throw love::Exception("failed to create synchronization objects for a frame!");
  1055. }
  1056. }
  1057. }
  1058. void Graphics::createDefaultTexture() {
  1059. Texture::Settings settings;
  1060. standardTexture = newTexture(settings);
  1061. }
  1062. bool operator==(const Graphics::GraphicsPipelineConfiguration& first, const Graphics::GraphicsPipelineConfiguration& other) {
  1063. if (first.vertexInputAttributeDescriptions.size() != other.vertexInputAttributeDescriptions.size()) {
  1064. return false;
  1065. }
  1066. if (first.vertexInputBindingDescriptions.size() != other.vertexInputBindingDescriptions.size()) {
  1067. return false;
  1068. }
  1069. for (uint32_t i = 0; i < first.vertexInputAttributeDescriptions.size(); i++) {
  1070. const VkVertexInputAttributeDescription& x = first.vertexInputAttributeDescriptions[i];
  1071. const VkVertexInputAttributeDescription& y = other.vertexInputAttributeDescriptions[i];
  1072. if (x.binding != y.binding) {
  1073. return false;
  1074. }
  1075. if (x.location != y.location) {
  1076. return false;
  1077. }
  1078. if (x.offset != y.offset) {
  1079. return false;
  1080. }
  1081. if (x.format != y.format) {
  1082. return false;
  1083. }
  1084. }
  1085. for (uint32_t i = 0; i < first.vertexInputBindingDescriptions.size(); i++) {
  1086. const VkVertexInputBindingDescription& x = first.vertexInputBindingDescriptions[i];
  1087. const VkVertexInputBindingDescription& y = other.vertexInputBindingDescriptions[i];
  1088. if (x.binding != y.binding) {
  1089. return false;
  1090. }
  1091. if (x.inputRate != y.inputRate) {
  1092. return false;
  1093. }
  1094. if (x.stride != y.stride) {
  1095. return false;
  1096. }
  1097. }
  1098. return true;
  1099. }
  1100. void Graphics::cleanup() {
  1101. vkDeviceWaitIdle(device);
  1102. cleanupSwapChain();
  1103. for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
  1104. vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
  1105. vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
  1106. vkDestroyFence(device, inFlightFences[i], nullptr);
  1107. }
  1108. vkDestroyDescriptorPool(device, descriptorPool, nullptr);
  1109. vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
  1110. vkDestroyCommandPool(device, commandPool, nullptr);
  1111. vkDestroyDevice(device, nullptr);
  1112. vkDestroySurfaceKHR(instance, surface, nullptr);
  1113. vkDestroyInstance(instance, nullptr);
  1114. }
  1115. void Graphics::cleanupSwapChain() {
  1116. std::cout << "cleanupSwapChain ";
  1117. for (size_t i = 0; i < swapChainFramBuffers.size(); i++) {
  1118. vkDestroyFramebuffer(device, swapChainFramBuffers[i], nullptr);
  1119. }
  1120. vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
  1121. for (auto const& p : graphicsPipelines) {
  1122. vkDestroyPipeline(device, p.second, nullptr);
  1123. }
  1124. graphicsPipelines.clear();
  1125. currentGraphicsPipeline = VK_NULL_HANDLE;
  1126. // vkDestroyPipelineLayout(device, pipelineLayout, nullptr); FIXME
  1127. vkDestroyRenderPass(device, renderPass, nullptr);
  1128. for (size_t i = 0; i < swapChainImageViews.size(); i++) {
  1129. vkDestroyImageView(device, swapChainImageViews[i], nullptr);
  1130. }
  1131. vkDestroySwapchainKHR(device, swapChain, nullptr);
  1132. uniformBuffers.clear();
  1133. textureToDescriptorSetsMap.clear();
  1134. }
  1135. void Graphics::recreateSwapChain() {
  1136. vkDeviceWaitIdle(device);
  1137. cleanupSwapChain();
  1138. createSwapChain();
  1139. createImageViews();
  1140. createRenderPass();
  1141. createFramebuffers();
  1142. createUniformBuffers();
  1143. createDescriptorPool();
  1144. createCommandBuffers();
  1145. startRecordingGraphicsCommands();
  1146. }
  1147. love::graphics::Graphics* createInstance() {
  1148. love::graphics::Graphics* instance = nullptr;
  1149. try {
  1150. instance = new Graphics();
  1151. }
  1152. catch (love::Exception& e) {
  1153. printf("Cannot create Vulkan renderer: %s\n", e.what());
  1154. }
  1155. return instance;
  1156. }
  1157. }
  1158. }
  1159. }