Canvas.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. // Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Ui/Canvas.h>
  6. #include <AnKi/Ui/Font.h>
  7. #include <AnKi/Ui/UiManager.h>
  8. #include <AnKi/Resource/ResourceManager.h>
  9. #include <AnKi/Core/GpuMemoryPools.h>
  10. #include <AnKi/Input/Input.h>
  11. #include <AnKi/Gr/Sampler.h>
  12. #include <AnKi/Gr/GrManager.h>
  13. namespace anki {
  14. Canvas::Canvas(UiManager* manager)
  15. : UiObject(manager)
  16. {
  17. }
  18. Canvas::~Canvas()
  19. {
  20. if(m_imCtx)
  21. {
  22. setImAllocator();
  23. ImGui::DestroyContext(m_imCtx);
  24. unsetImAllocator();
  25. }
  26. }
  27. Error Canvas::init(FontPtr font, U32 fontHeight, U32 width, U32 height)
  28. {
  29. m_font = font;
  30. m_dfltFontHeight = fontHeight;
  31. resize(width, height);
  32. // Create program
  33. ANKI_CHECK(m_manager->getResourceManager().loadResource("ShaderBinaries/Ui.ankiprogbin", m_prog));
  34. for(U32 i = 0; i < SHADER_COUNT; ++i)
  35. {
  36. const ShaderProgramResourceVariant* variant;
  37. ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog);
  38. variantInitInfo.addMutation("TEXTURE_TYPE", i);
  39. m_prog->getOrCreateVariant(variantInitInfo, variant);
  40. m_grProgs[i] = variant->getProgram();
  41. }
  42. // Sampler
  43. SamplerInitInfo samplerInit("Canvas");
  44. samplerInit.m_minMagFilter = SamplingFilter::LINEAR;
  45. samplerInit.m_mipmapFilter = SamplingFilter::LINEAR;
  46. samplerInit.m_addressing = SamplingAddressing::REPEAT;
  47. m_linearLinearRepeatSampler = m_manager->getGrManager().newSampler(samplerInit);
  48. samplerInit.m_minMagFilter = SamplingFilter::NEAREST;
  49. samplerInit.m_mipmapFilter = SamplingFilter::NEAREST;
  50. m_nearestNearestRepeatSampler = m_manager->getGrManager().newSampler(samplerInit);
  51. // Allocator
  52. m_stackAlloc = StackAllocator<U8>(getAllocator().getMemoryPool().getAllocationCallback(),
  53. getAllocator().getMemoryPool().getAllocationCallbackUserData(), 512_B);
  54. // Create the context
  55. setImAllocator();
  56. m_imCtx = ImGui::CreateContext(font->getImFontAtlas());
  57. ImGui::SetCurrentContext(m_imCtx);
  58. ImGui::GetIO().IniFilename = nullptr;
  59. ImGui::GetIO().LogFilename = nullptr;
  60. ImGui::StyleColorsLight();
  61. #define ANKI_HANDLE(ak, im) ImGui::GetIO().KeyMap[im] = static_cast<int>(ak);
  62. ANKI_HANDLE(KeyCode::TAB, ImGuiKey_Tab)
  63. ANKI_HANDLE(KeyCode::LEFT, ImGuiKey_LeftArrow)
  64. ANKI_HANDLE(KeyCode::RIGHT, ImGuiKey_RightArrow)
  65. ANKI_HANDLE(KeyCode::UP, ImGuiKey_UpArrow)
  66. ANKI_HANDLE(KeyCode::DOWN, ImGuiKey_DownArrow)
  67. ANKI_HANDLE(KeyCode::PAGEUP, ImGuiKey_PageUp)
  68. ANKI_HANDLE(KeyCode::PAGEDOWN, ImGuiKey_PageDown)
  69. ANKI_HANDLE(KeyCode::HOME, ImGuiKey_Home)
  70. ANKI_HANDLE(KeyCode::END, ImGuiKey_End)
  71. ANKI_HANDLE(KeyCode::INSERT, ImGuiKey_Insert)
  72. ANKI_HANDLE(KeyCode::DELETE, ImGuiKey_Delete)
  73. ANKI_HANDLE(KeyCode::BACKSPACE, ImGuiKey_Backspace)
  74. ANKI_HANDLE(KeyCode::SPACE, ImGuiKey_Space)
  75. ANKI_HANDLE(KeyCode::RETURN, ImGuiKey_Enter)
  76. // ANKI_HANDLE(KeyCode::RETURN2, ImGuiKey_Enter)
  77. ANKI_HANDLE(KeyCode::ESCAPE, ImGuiKey_Escape)
  78. ANKI_HANDLE(KeyCode::A, ImGuiKey_A)
  79. ANKI_HANDLE(KeyCode::C, ImGuiKey_C)
  80. ANKI_HANDLE(KeyCode::V, ImGuiKey_V)
  81. ANKI_HANDLE(KeyCode::X, ImGuiKey_X)
  82. ANKI_HANDLE(KeyCode::Y, ImGuiKey_Y)
  83. ANKI_HANDLE(KeyCode::Z, ImGuiKey_Z)
  84. #undef ANKI_HANDLE
  85. ImGui::SetCurrentContext(nullptr);
  86. unsetImAllocator();
  87. return Error::NONE;
  88. }
  89. void Canvas::handleInput()
  90. {
  91. const Input& in = m_manager->getInput();
  92. // Begin
  93. setImAllocator();
  94. ImGui::SetCurrentContext(m_imCtx);
  95. ImGuiIO& io = ImGui::GetIO();
  96. // Handle mouse
  97. Array<U32, 4> viewport = {0, 0, m_width, m_height};
  98. Vec2 mousePosf = in.getMousePosition() / 2.0f + 0.5f;
  99. mousePosf.y() = 1.0f - mousePosf.y();
  100. const UVec2 mousePos(U32(mousePosf.x() * F32(viewport[2])), U32(mousePosf.y() * F32(viewport[3])));
  101. io.MousePos.x = F32(mousePos.x());
  102. io.MousePos.y = F32(mousePos.y());
  103. io.MouseClicked[0] = in.getMouseButton(MouseButton::LEFT) == 1;
  104. io.MouseDown[0] = in.getMouseButton(MouseButton::LEFT) > 0;
  105. if(in.getMouseButton(MouseButton::SCROLL_UP) == 1)
  106. {
  107. io.MouseWheel = F32(in.getMouseButton(MouseButton::SCROLL_UP));
  108. }
  109. else if(in.getMouseButton(MouseButton::SCROLL_DOWN) == 1)
  110. {
  111. io.MouseWheel = -F32(in.getMouseButton(MouseButton::SCROLL_DOWN));
  112. }
  113. io.KeyCtrl = (in.getKey(KeyCode::LCTRL) || in.getKey(KeyCode::RCTRL));
  114. // Handle keyboard
  115. #define ANKI_HANDLE(ak) io.KeysDown[static_cast<int>(ak)] = (in.getKey(ak) == 1);
  116. ANKI_HANDLE(KeyCode::TAB)
  117. ANKI_HANDLE(KeyCode::LEFT)
  118. ANKI_HANDLE(KeyCode::RIGHT)
  119. ANKI_HANDLE(KeyCode::UP)
  120. ANKI_HANDLE(KeyCode::DOWN)
  121. ANKI_HANDLE(KeyCode::PAGEUP)
  122. ANKI_HANDLE(KeyCode::PAGEDOWN)
  123. ANKI_HANDLE(KeyCode::HOME)
  124. ANKI_HANDLE(KeyCode::END)
  125. ANKI_HANDLE(KeyCode::INSERT)
  126. ANKI_HANDLE(KeyCode::DELETE)
  127. ANKI_HANDLE(KeyCode::BACKSPACE)
  128. ANKI_HANDLE(KeyCode::SPACE)
  129. ANKI_HANDLE(KeyCode::RETURN)
  130. // ANKI_HANDLE(KeyCode::RETURN2)
  131. ANKI_HANDLE(KeyCode::ESCAPE)
  132. ANKI_HANDLE(KeyCode::A)
  133. ANKI_HANDLE(KeyCode::C)
  134. ANKI_HANDLE(KeyCode::V)
  135. ANKI_HANDLE(KeyCode::X)
  136. ANKI_HANDLE(KeyCode::Y)
  137. ANKI_HANDLE(KeyCode::Z)
  138. #undef ANKI_HANDLE
  139. io.AddInputCharactersUTF8(in.getTextInput().cstr());
  140. // End
  141. ImGui::SetCurrentContext(nullptr);
  142. unsetImAllocator();
  143. }
  144. void Canvas::beginBuilding()
  145. {
  146. setImAllocator();
  147. ImGui::SetCurrentContext(m_imCtx);
  148. ImGuiIO& io = ImGui::GetIO();
  149. io.DeltaTime = 1.0f / 60.0f;
  150. io.DisplaySize = ImVec2(F32(m_width), F32(m_height));
  151. ImGui::NewFrame();
  152. ImGui::PushFont(&m_font->getImFont(m_dfltFontHeight));
  153. }
  154. void Canvas::pushFont(const FontPtr& font, U32 fontHeight)
  155. {
  156. m_references.pushBack(m_stackAlloc, IntrusivePtr<UiObject>(const_cast<Font*>(font.get())));
  157. ImGui::PushFont(&font->getImFont(fontHeight));
  158. }
  159. void Canvas::appendToCommandBuffer(CommandBufferPtr cmdb)
  160. {
  161. appendToCommandBufferInternal(cmdb);
  162. // Done
  163. ImGui::SetCurrentContext(nullptr);
  164. m_references.destroy(m_stackAlloc);
  165. m_stackAlloc.getMemoryPool().reset();
  166. }
  167. void Canvas::appendToCommandBufferInternal(CommandBufferPtr& cmdb)
  168. {
  169. ImGui::PopFont();
  170. ImGui::Render();
  171. ImDrawData& drawData = *ImGui::GetDrawData();
  172. // Allocate index and vertex buffers
  173. StagingGpuMemoryToken vertsToken, indicesToken;
  174. {
  175. const U32 verticesSize = U32(drawData.TotalVtxCount) * sizeof(ImDrawVert);
  176. const U32 indicesSize = U32(drawData.TotalIdxCount) * sizeof(ImDrawIdx);
  177. if(verticesSize == 0 || indicesSize == 0)
  178. {
  179. return;
  180. }
  181. ImDrawVert* verts = static_cast<ImDrawVert*>(
  182. m_manager->getStagingGpuMemory().allocateFrame(verticesSize, StagingGpuMemoryType::VERTEX, vertsToken));
  183. ImDrawIdx* indices = static_cast<ImDrawIdx*>(
  184. m_manager->getStagingGpuMemory().allocateFrame(indicesSize, StagingGpuMemoryType::VERTEX, indicesToken));
  185. for(I n = 0; n < drawData.CmdListsCount; ++n)
  186. {
  187. const ImDrawList& cmdList = *drawData.CmdLists[n];
  188. memcpy(verts, cmdList.VtxBuffer.Data, cmdList.VtxBuffer.Size * sizeof(ImDrawVert));
  189. memcpy(indices, cmdList.IdxBuffer.Data, cmdList.IdxBuffer.Size * sizeof(ImDrawIdx));
  190. verts += cmdList.VtxBuffer.Size;
  191. indices += cmdList.IdxBuffer.Size;
  192. }
  193. }
  194. cmdb->setBlendFactors(0, BlendFactor::SRC_ALPHA, BlendFactor::ONE_MINUS_SRC_ALPHA);
  195. cmdb->setCullMode(FaceSelectionBit::NONE);
  196. const F32 fbWidth = drawData.DisplaySize.x * drawData.FramebufferScale.x;
  197. const F32 fbHeight = drawData.DisplaySize.y * drawData.FramebufferScale.y;
  198. cmdb->setViewport(0, 0, U32(fbWidth), U32(fbHeight));
  199. cmdb->bindVertexBuffer(0, vertsToken.m_buffer, vertsToken.m_offset, sizeof(ImDrawVert));
  200. cmdb->setVertexAttribute(0, 0, Format::R32G32_SFLOAT, 0);
  201. cmdb->setVertexAttribute(1, 0, Format::R8G8B8A8_UNORM, sizeof(Vec2) * 2);
  202. cmdb->setVertexAttribute(2, 0, Format::R32G32_SFLOAT, sizeof(Vec2));
  203. cmdb->bindIndexBuffer(indicesToken.m_buffer, indicesToken.m_offset, IndexType::U16);
  204. // Will project scissor/clipping rectangles into framebuffer space
  205. const Vec2 clipOff = drawData.DisplayPos; // (0,0) unless using multi-viewports
  206. const Vec2 clipScale = drawData.FramebufferScale; // (1,1) unless using retina display which are often (2,2)
  207. // Render
  208. U32 vertOffset = 0;
  209. U32 idxOffset = 0;
  210. for(I32 n = 0; n < drawData.CmdListsCount; n++)
  211. {
  212. const ImDrawList& cmdList = *drawData.CmdLists[n];
  213. for(I32 i = 0; i < cmdList.CmdBuffer.Size; i++)
  214. {
  215. const ImDrawCmd& pcmd = cmdList.CmdBuffer[i];
  216. if(pcmd.UserCallback)
  217. {
  218. // User callback (registered via ImDrawList::AddCallback), ignore
  219. ANKI_UI_LOGW("Got user callback. Will ignore");
  220. }
  221. else
  222. {
  223. // Project scissor/clipping rectangles into framebuffer space. Flip Y
  224. Vec4 clipRect;
  225. clipRect.x() = (pcmd.ClipRect.x - clipOff.x()) * clipScale.x();
  226. clipRect.y() = (fbHeight - pcmd.ClipRect.w - clipOff.y()) * clipScale.y();
  227. clipRect.z() = (pcmd.ClipRect.z - clipOff.x()) * clipScale.x();
  228. clipRect.w() = (fbHeight - pcmd.ClipRect.y - clipOff.y()) * clipScale.y();
  229. if(clipRect.x() < fbWidth && clipRect.y() < fbHeight && clipRect.z() >= 0.0f && clipRect.w() >= 0.0f)
  230. {
  231. // Negative offsets are illegal for vkCmdSetScissor
  232. if(clipRect.x() < 0.0f)
  233. {
  234. clipRect.x() = 0.0f;
  235. }
  236. if(clipRect.y() < 0.0f)
  237. {
  238. clipRect.y() = 0.0f;
  239. }
  240. // Apply scissor/clipping rectangle
  241. cmdb->setScissor(U32(clipRect.x()), U32(clipRect.y()), U32(clipRect.z() - clipRect.x()),
  242. U32(clipRect.w() - clipRect.y()));
  243. UiImageId id(pcmd.TextureId);
  244. const UiImageIdExtra* idExtra = nullptr;
  245. TextureViewPtr textureView;
  246. if(id.m_bits.m_extra)
  247. {
  248. idExtra = numberToPtr<const UiImageIdExtra*>(id.m_bits.m_textureViewPtrOrComplex);
  249. textureView = idExtra->m_textureView;
  250. }
  251. else
  252. {
  253. textureView.reset(numberToPtr<TextureView*>(id.m_bits.m_textureViewPtrOrComplex));
  254. }
  255. // Bind program
  256. if(idExtra && idExtra->m_customProgram.isCreated())
  257. {
  258. cmdb->bindShaderProgram(idExtra->m_customProgram);
  259. }
  260. else if(textureView.isCreated())
  261. {
  262. cmdb->bindShaderProgram(m_grProgs[RGBA_TEX]);
  263. }
  264. else
  265. {
  266. cmdb->bindShaderProgram(m_grProgs[NO_TEX]);
  267. }
  268. // Bindings
  269. if(textureView.isCreated())
  270. {
  271. cmdb->bindSampler(0, 0,
  272. (id.m_bits.m_pointSampling) ? m_nearestNearestRepeatSampler
  273. : m_linearLinearRepeatSampler);
  274. cmdb->bindTexture(0, 1, textureView);
  275. }
  276. // Push constants
  277. class PC
  278. {
  279. public:
  280. Vec4 m_transform;
  281. Array<U8, sizeof(UiImageIdExtra::m_extraPushConstants)> m_extra;
  282. } pc;
  283. pc.m_transform.x() = 2.0f / drawData.DisplaySize.x;
  284. pc.m_transform.y() = -2.0f / drawData.DisplaySize.y;
  285. pc.m_transform.z() = (drawData.DisplayPos.x / drawData.DisplaySize.x) * 2.0f - 1.0f;
  286. pc.m_transform.w() = -((drawData.DisplayPos.y / drawData.DisplaySize.y) * 2.0f - 1.0f);
  287. U32 extraPushConstantsSize = 0;
  288. if(idExtra && idExtra->m_extraPushConstantsSize)
  289. {
  290. ANKI_ASSERT(idExtra->m_extraPushConstantsSize <= sizeof(idExtra->m_extraPushConstants));
  291. memcpy(&pc.m_extra[0], idExtra->m_extraPushConstants.getBegin(),
  292. idExtra->m_extraPushConstantsSize);
  293. extraPushConstantsSize = idExtra->m_extraPushConstantsSize;
  294. }
  295. cmdb->setPushConstants(&pc, sizeof(Vec4) + extraPushConstantsSize);
  296. // Draw
  297. cmdb->drawElements(PrimitiveTopology::TRIANGLES, pcmd.ElemCount, 1, idxOffset, vertOffset);
  298. }
  299. }
  300. idxOffset += pcmd.ElemCount;
  301. }
  302. vertOffset += cmdList.VtxBuffer.Size;
  303. }
  304. // Restore state
  305. cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ZERO);
  306. cmdb->setCullMode(FaceSelectionBit::BACK);
  307. }
  308. } // end namespace anki