void set_up_vertex_buffer(struct ft2_source *srcdata) { FT_UInt glyph_index = 0; uint32_t x = 0, space_pos = 0, word_width = 0; size_t len; if (!srcdata->text) return; if (srcdata->custom_width >= 100) srcdata->cx = srcdata->custom_width; else srcdata->cx = get_ft2_text_width(srcdata->text, srcdata); srcdata->cy = srcdata->max_h; obs_enter_graphics(); if (srcdata->vbuf != NULL) { gs_vertbuffer_t *tmpvbuf = srcdata->vbuf; srcdata->vbuf = NULL; gs_vertexbuffer_destroy(tmpvbuf); } srcdata->vbuf = create_uv_vbuffer((uint32_t)wcslen(srcdata->text) * 6, true); if (srcdata->custom_width <= 100) goto skip_word_wrap; if (!srcdata->word_wrap) goto skip_word_wrap; len = wcslen(srcdata->text); for (uint32_t i = 0; i <= len; i++) { if (i == wcslen(srcdata->text)) goto eos_check; if (srcdata->text[i] != L' ' && srcdata->text[i] != L'\n') goto next_char; eos_check:; if (x + word_width > srcdata->custom_width) { if (space_pos != 0) srcdata->text[space_pos] = L'\n'; x = 0; } if (i == wcslen(srcdata->text)) goto eos_skip; x += word_width; word_width = 0; if (srcdata->text[i] == L'\n') x = 0; if (srcdata->text[i] == L' ') space_pos = i; next_char:; glyph_index = FT_Get_Char_Index(srcdata->font_face, srcdata->text[i]); word_width += src_glyph->xadv; eos_skip:; } skip_word_wrap:; fill_vertex_buffer(srcdata); obs_leave_graphics(); }
static void before_present(VkQueue queue, layer_data *my_data, SwapChainData *swapChain, unsigned imageIndex) { VkLayerDispatchTable *pTable = my_data->device_dispatch_table; if (!my_data->fontUploadComplete) { VkSubmitInfo si = {}; si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; si.pNext = nullptr; si.waitSemaphoreCount = 0; si.commandBufferCount = 1; si.signalSemaphoreCount = 0; si.pCommandBuffers = &my_data->fontUploadCmdBuffer; pTable->QueueSubmit(queue, 1, &si, VK_NULL_HANDLE); my_data->fontUploadComplete = true; #ifdef OVERLAY_DEBUG printf("Font image layout transition queued\n"); #endif } WsiImageData *id = swapChain->presentableImages[imageIndex]; /* update the overlay content */ vertex *vertices = nullptr; /* guaranteed not in flight due to WSI surface being available */ VkResult U_ASSERT_ONLY err = pTable->MapMemory(my_data->dev, id->vertexBufferMemory, 0, id->vertexBufferSize, 0, (void **)&vertices); assert(!err); /* write vertices for string in here */ id->numVertices = fill_vertex_buffer(my_data, vertices, imageIndex); pTable->UnmapMemory(my_data->dev, id->vertexBufferMemory); /* JIT record a command buffer to draw the overlay */ VkCommandBufferBeginInfo cbbi; cbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cbbi.pNext = nullptr; cbbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; cbbi.pInheritanceInfo = nullptr; VkImageMemoryBarrier imb; imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imb.pNext = nullptr; imb.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; imb.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; imb.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; imb.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; imb.image = id->image; imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imb.subresourceRange.baseMipLevel = 0; imb.subresourceRange.levelCount = 1; imb.subresourceRange.baseArrayLayer = 0; imb.subresourceRange.layerCount = 1; imb.srcQueueFamilyIndex = my_data->graphicsQueueFamilyIndex; imb.dstQueueFamilyIndex = my_data->graphicsQueueFamilyIndex; VkRenderPassBeginInfo rpbi; rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rpbi.pNext = nullptr; rpbi.renderPass = swapChain->render_pass; rpbi.framebuffer = id->framebuffer; rpbi.renderArea.offset.x = 0; rpbi.renderArea.offset.y = 0; rpbi.renderArea.extent.width = swapChain->width; rpbi.renderArea.extent.height = swapChain->height; rpbi.clearValueCount = 0; rpbi.pClearValues = nullptr; pTable->BeginCommandBuffer(id->cmd, &cbbi); pTable->CmdPipelineBarrier(id->cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0 /* dependency flags */, 0, nullptr, /* memory barriers */ 0, nullptr, /* buffer memory barriers */ 1, &imb); /* image memory barriers */ pTable->CmdBeginRenderPass(id->cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE); pTable->CmdBindPipeline(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, swapChain->pipeline); pTable->CmdBindDescriptorSets(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, my_data->pl, 0, 1, &my_data->desc_set, 0, nullptr); VkDeviceSize offsets[] = {0}; VkBuffer buffers[] = {id->vertexBuffer}; pTable->CmdBindVertexBuffers(id->cmd, 0, 1, buffers, offsets); pTable->CmdDraw(id->cmd, id->numVertices, 1, 0, 0); pTable->CmdEndRenderPass(id->cmd); pTable->EndCommandBuffer(id->cmd); /* Schedule this command buffer for execution. TODO: Do we need to protect * ourselves from an app that didn't wait for the presentation image to * be idle before mangling it? If the app is well-behaved, our command * buffer is guaranteed to have been retired before the app tries to * present it again. */ VkSubmitInfo si = {}; si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; si.pNext = nullptr; si.waitSemaphoreCount = 0; si.commandBufferCount = 1; si.signalSemaphoreCount = 0; si.pCommandBuffers = &id->cmd; pTable->WaitForFences(my_data->dev, 1, &my_data->fence, VK_TRUE, UINT64_MAX); pTable->ResetFences(my_data->dev, 1, &my_data->fence); pTable->QueueSubmit(queue, 1, &si, my_data->fence); /* Reset per-frame stats */ my_data->cmdBuffersThisFrame = 0; }
static void before_present(VkQueue queue, layer_data *my_data, SwapChainData *swapChain, unsigned imageIndex) { VkLayerDispatchTable *pTable = my_data->device_dispatch_table; if (!my_data->fontUploadComplete) { VkSubmitInfo si; si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; si.pNext = nullptr; si.waitSemaphoreCount = 0; si.commandBufferCount = 1; si.signalSemaphoreCount = 0; si.pCommandBuffers = &my_data->fontUploadCmdBuffer; pTable->QueueSubmit(queue, 1, &si, VK_NULL_HANDLE); my_data->fontUploadComplete = true; #ifdef OVERLAY_DEBUG printf("Font image layout transition queued\n"); #endif } WsiImageData *id = swapChain->presentableImages[imageIndex]; /* update the overlay content */ vertex *vertices = nullptr; /* guaranteed not in flight due to WSI surface being available */ VkResult U_ASSERT_ONLY err = pTable->MapMemory(my_data->dev, id->vertexBufferMemory, 0, id->vertexBufferSize, 0, (void **)&vertices); assert(!err); /* write vertices for string in here */ id->numVertices = fill_vertex_buffer(my_data, vertices, imageIndex); pTable->UnmapMemory(my_data->dev, id->vertexBufferMemory); /* JIT record a command buffer to draw the overlay */ VkCommandBufferBeginInfo cbbi; cbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cbbi.pNext = nullptr; cbbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; cbbi.pInheritanceInfo = nullptr; VkRenderPassBeginInfo rpbi; rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rpbi.pNext = nullptr; rpbi.renderPass = swapChain->render_pass; rpbi.framebuffer = id->framebuffer; rpbi.renderArea.offset.x = 0; rpbi.renderArea.offset.y = 0; rpbi.renderArea.extent.width = swapChain->width; rpbi.renderArea.extent.height = swapChain->height; rpbi.clearValueCount = 0; rpbi.pClearValues = nullptr; pTable->BeginCommandBuffer(id->cmd, &cbbi); pTable->CmdBeginRenderPass(id->cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE); pTable->CmdBindPipeline(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, swapChain->pipeline); pTable->CmdBindDescriptorSets(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, my_data->pl, 0, 1, &my_data->desc_set, 0, nullptr); VkDeviceSize offsets[] = {0}; VkBuffer buffers[] = {id->vertexBuffer}; pTable->CmdBindVertexBuffers(id->cmd, 0, 1, buffers, offsets); pTable->CmdDraw(id->cmd, id->numVertices, 1, 0, 0); pTable->CmdEndRenderPass(id->cmd); pTable->EndCommandBuffer(id->cmd); /* Schedule this command buffer for execution. TODO: Do we need to protect * ourselves from an app that didn't wait for the presentation image to * be idle before mangling it? If the app is well-behaved, our command * buffer is guaranteed to have been retired before the app tries to * present it again. */ VkFence null_fence = VK_NULL_HANDLE; VkSubmitInfo si; si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; si.pNext = nullptr; si.waitSemaphoreCount = 0; si.commandBufferCount = 1; si.signalSemaphoreCount = 0; si.pCommandBuffers = &id->cmd; pTable->QueueSubmit(queue, 1, &si, null_fence); /* Reset per-frame stats */ my_data->cmdBuffersThisFrame = 0; }