Canvas.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. // Copyright (C) 2009-2023, 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/GpuMemory/RebarTransientMemoryPool.h>
  10. #include <AnKi/Window/Input.h>
  11. #include <AnKi/Gr/Sampler.h>
  12. #include <AnKi/Gr/GrManager.h>
  13. namespace anki {
  14. Canvas::~Canvas()
  15. {
  16. if(m_imCtx)
  17. {
  18. ImGui::DestroyContext(m_imCtx);
  19. }
  20. }
  21. Error Canvas::init(FontPtr font, U32 fontHeight, U32 width, U32 height)
  22. {
  23. m_font = font;
  24. m_dfltFontHeight = fontHeight;
  25. resize(width, height);
  26. // Create program
  27. ANKI_CHECK(ResourceManager::getSingleton().loadResource("ShaderBinaries/Ui.ankiprogbin", m_prog));
  28. for(U32 i = 0; i < kShaderCount; ++i)
  29. {
  30. const ShaderProgramResourceVariant* variant;
  31. ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog);
  32. variantInitInfo.addMutation("TEXTURE_TYPE", i);
  33. m_prog->getOrCreateVariant(variantInitInfo, variant);
  34. m_grProgs[i].reset(&variant->getProgram());
  35. }
  36. // Sampler
  37. SamplerInitInfo samplerInit("Canvas");
  38. samplerInit.m_minMagFilter = SamplingFilter::kLinear;
  39. samplerInit.m_mipmapFilter = SamplingFilter::kLinear;
  40. samplerInit.m_addressing = SamplingAddressing::kRepeat;
  41. m_linearLinearRepeatSampler = GrManager::getSingleton().newSampler(samplerInit);
  42. samplerInit.m_minMagFilter = SamplingFilter::kNearest;
  43. samplerInit.m_mipmapFilter = SamplingFilter::kNearest;
  44. m_nearestNearestRepeatSampler = GrManager::getSingleton().newSampler(samplerInit);
  45. // Create the context
  46. m_imCtx = ImGui::CreateContext(font->getImFontAtlas());
  47. ImGui::SetCurrentContext(m_imCtx);
  48. ImGui::GetIO().IniFilename = nullptr;
  49. ImGui::GetIO().LogFilename = nullptr;
  50. ImGui::StyleColorsLight();
  51. #define ANKI_HANDLE(ak, im) ImGui::GetIO().KeyMap[im] = static_cast<int>(ak);
  52. ANKI_HANDLE(KeyCode::kTab, ImGuiKey_Tab)
  53. ANKI_HANDLE(KeyCode::kLeft, ImGuiKey_LeftArrow)
  54. ANKI_HANDLE(KeyCode::kRight, ImGuiKey_RightArrow)
  55. ANKI_HANDLE(KeyCode::kUp, ImGuiKey_UpArrow)
  56. ANKI_HANDLE(KeyCode::kDown, ImGuiKey_DownArrow)
  57. ANKI_HANDLE(KeyCode::kPageUp, ImGuiKey_PageUp)
  58. ANKI_HANDLE(KeyCode::kPageDown, ImGuiKey_PageDown)
  59. ANKI_HANDLE(KeyCode::kHome, ImGuiKey_Home)
  60. ANKI_HANDLE(KeyCode::kEnd, ImGuiKey_End)
  61. ANKI_HANDLE(KeyCode::kInsert, ImGuiKey_Insert)
  62. ANKI_HANDLE(KeyCode::kDelete, ImGuiKey_Delete)
  63. ANKI_HANDLE(KeyCode::kBackspace, ImGuiKey_Backspace)
  64. ANKI_HANDLE(KeyCode::kSpace, ImGuiKey_Space)
  65. ANKI_HANDLE(KeyCode::kReturn, ImGuiKey_Enter)
  66. // ANKI_HANDLE(KeyCode::RETURN2, ImGuiKey_Enter)
  67. ANKI_HANDLE(KeyCode::kEscape, ImGuiKey_Escape)
  68. ANKI_HANDLE(KeyCode::kA, ImGuiKey_A)
  69. ANKI_HANDLE(KeyCode::kC, ImGuiKey_C)
  70. ANKI_HANDLE(KeyCode::kV, ImGuiKey_V)
  71. ANKI_HANDLE(KeyCode::kX, ImGuiKey_X)
  72. ANKI_HANDLE(KeyCode::kY, ImGuiKey_Y)
  73. ANKI_HANDLE(KeyCode::kZ, ImGuiKey_Z)
  74. #undef ANKI_HANDLE
  75. ImGui::SetCurrentContext(nullptr);
  76. return Error::kNone;
  77. }
  78. void Canvas::handleInput()
  79. {
  80. const Input& in = Input::getSingleton();
  81. // Begin
  82. ImGui::SetCurrentContext(m_imCtx);
  83. ImGuiIO& io = ImGui::GetIO();
  84. // Handle mouse
  85. Array<U32, 4> viewport = {0, 0, m_width, m_height};
  86. Vec2 mousePosf = in.getMousePosition() / 2.0f + 0.5f;
  87. mousePosf.y() = 1.0f - mousePosf.y();
  88. const UVec2 mousePos(U32(mousePosf.x() * F32(viewport[2])), U32(mousePosf.y() * F32(viewport[3])));
  89. io.MousePos.x = F32(mousePos.x());
  90. io.MousePos.y = F32(mousePos.y());
  91. io.MouseClicked[0] = in.getMouseButton(MouseButton::kLeft) == 1;
  92. io.MouseDown[0] = in.getMouseButton(MouseButton::kLeft) > 0;
  93. if(in.getMouseButton(MouseButton::kScrollUp) == 1)
  94. {
  95. io.MouseWheel = F32(in.getMouseButton(MouseButton::kScrollUp));
  96. }
  97. else if(in.getMouseButton(MouseButton::kScrollDown) == 1)
  98. {
  99. io.MouseWheel = -F32(in.getMouseButton(MouseButton::kScrollDown));
  100. }
  101. io.KeyCtrl = (in.getKey(KeyCode::kLeftCtrl) || in.getKey(KeyCode::kRightCtrl));
  102. // Handle keyboard
  103. #define ANKI_HANDLE(ak) io.KeysDown[int(ak)] = (in.getKey(ak) == 1);
  104. ANKI_HANDLE(KeyCode::kTab)
  105. ANKI_HANDLE(KeyCode::kLeft)
  106. ANKI_HANDLE(KeyCode::kRight)
  107. ANKI_HANDLE(KeyCode::kUp)
  108. ANKI_HANDLE(KeyCode::kDown)
  109. ANKI_HANDLE(KeyCode::kPageUp)
  110. ANKI_HANDLE(KeyCode::kPageDown)
  111. ANKI_HANDLE(KeyCode::kHome)
  112. ANKI_HANDLE(KeyCode::kEnd)
  113. ANKI_HANDLE(KeyCode::kInsert)
  114. ANKI_HANDLE(KeyCode::kDelete)
  115. ANKI_HANDLE(KeyCode::kBackspace)
  116. ANKI_HANDLE(KeyCode::kSpace)
  117. ANKI_HANDLE(KeyCode::kReturn)
  118. // ANKI_HANDLE(KeyCode::RETURN2)
  119. ANKI_HANDLE(KeyCode::kEscape)
  120. ANKI_HANDLE(KeyCode::kA)
  121. ANKI_HANDLE(KeyCode::kC)
  122. ANKI_HANDLE(KeyCode::kV)
  123. ANKI_HANDLE(KeyCode::kX)
  124. ANKI_HANDLE(KeyCode::kY)
  125. ANKI_HANDLE(KeyCode::kZ)
  126. #undef ANKI_HANDLE
  127. io.AddInputCharactersUTF8(in.getTextInput().cstr());
  128. // End
  129. ImGui::SetCurrentContext(nullptr);
  130. }
  131. void Canvas::beginBuilding()
  132. {
  133. ImGui::SetCurrentContext(m_imCtx);
  134. ImGuiIO& io = ImGui::GetIO();
  135. io.DeltaTime = 1.0f / 60.0f;
  136. io.DisplaySize = ImVec2(F32(m_width), F32(m_height));
  137. ImGui::NewFrame();
  138. ImGui::PushFont(&m_font->getImFont(m_dfltFontHeight));
  139. }
  140. void Canvas::pushFont(const FontPtr& font, U32 fontHeight)
  141. {
  142. m_references.pushBack(UiObjectPtr(const_cast<Font*>(font.get())));
  143. ImGui::PushFont(&font->getImFont(fontHeight));
  144. }
  145. void Canvas::appendToCommandBuffer(CommandBuffer& cmdb)
  146. {
  147. appendToCommandBufferInternal(cmdb);
  148. // Done
  149. ImGui::SetCurrentContext(nullptr);
  150. m_references.destroy();
  151. }
  152. void Canvas::appendToCommandBufferInternal(CommandBuffer& cmdb)
  153. {
  154. ImGui::PopFont();
  155. ImGui::Render();
  156. ImDrawData& drawData = *ImGui::GetDrawData();
  157. // Allocate index and vertex buffers
  158. RebarAllocation vertsToken, indicesToken;
  159. {
  160. if(drawData.TotalVtxCount == 0 || drawData.TotalIdxCount == 0)
  161. {
  162. return;
  163. }
  164. ImDrawVert* verts;
  165. vertsToken = RebarTransientMemoryPool::getSingleton().allocateFrame(drawData.TotalVtxCount, verts);
  166. ImDrawIdx* indices;
  167. indicesToken = RebarTransientMemoryPool::getSingleton().allocateFrame(drawData.TotalIdxCount, indices);
  168. for(I n = 0; n < drawData.CmdListsCount; ++n)
  169. {
  170. const ImDrawList& cmdList = *drawData.CmdLists[n];
  171. memcpy(verts, cmdList.VtxBuffer.Data, cmdList.VtxBuffer.Size * sizeof(ImDrawVert));
  172. memcpy(indices, cmdList.IdxBuffer.Data, cmdList.IdxBuffer.Size * sizeof(ImDrawIdx));
  173. verts += cmdList.VtxBuffer.Size;
  174. indices += cmdList.IdxBuffer.Size;
  175. }
  176. }
  177. cmdb.setBlendFactors(0, BlendFactor::kSrcAlpha, BlendFactor::kOneMinusSrcAlpha);
  178. cmdb.setCullMode(FaceSelectionBit::kNone);
  179. const F32 fbWidth = drawData.DisplaySize.x * drawData.FramebufferScale.x;
  180. const F32 fbHeight = drawData.DisplaySize.y * drawData.FramebufferScale.y;
  181. cmdb.setViewport(0, 0, U32(fbWidth), U32(fbHeight));
  182. cmdb.bindVertexBuffer(0, vertsToken, sizeof(ImDrawVert));
  183. cmdb.setVertexAttribute(VertexAttribute::kPosition, 0, Format::kR32G32_Sfloat, 0);
  184. cmdb.setVertexAttribute(VertexAttribute::kColor, 0, Format::kR8G8B8A8_Unorm, sizeof(Vec2) * 2);
  185. cmdb.setVertexAttribute(VertexAttribute::kTexCoord, 0, Format::kR32G32_Sfloat, sizeof(Vec2));
  186. cmdb.bindIndexBuffer(indicesToken, IndexType::kU16);
  187. // Will project scissor/clipping rectangles into framebuffer space
  188. const Vec2 clipOff = drawData.DisplayPos; // (0,0) unless using multi-viewports
  189. const Vec2 clipScale = drawData.FramebufferScale; // (1,1) unless using retina display which are often (2,2)
  190. // Render
  191. U32 vertOffset = 0;
  192. U32 idxOffset = 0;
  193. for(I32 n = 0; n < drawData.CmdListsCount; n++)
  194. {
  195. const ImDrawList& cmdList = *drawData.CmdLists[n];
  196. for(I32 i = 0; i < cmdList.CmdBuffer.Size; i++)
  197. {
  198. const ImDrawCmd& pcmd = cmdList.CmdBuffer[i];
  199. if(pcmd.UserCallback)
  200. {
  201. // User callback (registered via ImDrawList::AddCallback), ignore
  202. ANKI_UI_LOGW("Got user callback. Will ignore");
  203. }
  204. else
  205. {
  206. // Project scissor/clipping rectangles into framebuffer space. Flip Y
  207. Vec4 clipRect;
  208. clipRect.x() = (pcmd.ClipRect.x - clipOff.x()) * clipScale.x();
  209. clipRect.y() = (fbHeight - pcmd.ClipRect.w - clipOff.y()) * clipScale.y();
  210. clipRect.z() = (pcmd.ClipRect.z - clipOff.x()) * clipScale.x();
  211. clipRect.w() = (fbHeight - pcmd.ClipRect.y - clipOff.y()) * clipScale.y();
  212. if(clipRect.x() < fbWidth && clipRect.y() < fbHeight && clipRect.z() >= 0.0f && clipRect.w() >= 0.0f)
  213. {
  214. // Negative offsets are illegal for vkCmdSetScissor
  215. if(clipRect.x() < 0.0f)
  216. {
  217. clipRect.x() = 0.0f;
  218. }
  219. if(clipRect.y() < 0.0f)
  220. {
  221. clipRect.y() = 0.0f;
  222. }
  223. // Apply scissor/clipping rectangle
  224. cmdb.setScissor(U32(clipRect.x()), U32(clipRect.y()), U32(clipRect.z() - clipRect.x()), U32(clipRect.w() - clipRect.y()));
  225. UiImageId id(pcmd.TextureId);
  226. const UiImageIdExtra* idExtra = nullptr;
  227. TextureViewPtr textureView;
  228. if(id.m_bits.m_extra)
  229. {
  230. idExtra = numberToPtr<const UiImageIdExtra*>(id.m_bits.m_textureViewPtrOrComplex);
  231. textureView = idExtra->m_textureView;
  232. }
  233. else
  234. {
  235. textureView.reset(numberToPtr<TextureView*>(id.m_bits.m_textureViewPtrOrComplex));
  236. }
  237. // Bind program
  238. if(idExtra && idExtra->m_customProgram.isCreated())
  239. {
  240. cmdb.bindShaderProgram(idExtra->m_customProgram.get());
  241. }
  242. else if(textureView.isCreated())
  243. {
  244. cmdb.bindShaderProgram(m_grProgs[kRgbaTex].get());
  245. }
  246. else
  247. {
  248. cmdb.bindShaderProgram(m_grProgs[kNoTex].get());
  249. }
  250. // Bindings
  251. if(textureView.isCreated())
  252. {
  253. cmdb.bindSampler(0, 0, (id.m_bits.m_pointSampling) ? m_nearestNearestRepeatSampler.get() : m_linearLinearRepeatSampler.get());
  254. cmdb.bindTexture(0, 1, textureView.get());
  255. }
  256. // Push constants
  257. class PC
  258. {
  259. public:
  260. Vec4 m_transform;
  261. Array<U8, sizeof(UiImageIdExtra::m_extraPushConstants)> m_extra;
  262. } pc;
  263. pc.m_transform.x() = 2.0f / drawData.DisplaySize.x;
  264. pc.m_transform.y() = -2.0f / drawData.DisplaySize.y;
  265. pc.m_transform.z() = (drawData.DisplayPos.x / drawData.DisplaySize.x) * 2.0f - 1.0f;
  266. pc.m_transform.w() = -((drawData.DisplayPos.y / drawData.DisplaySize.y) * 2.0f - 1.0f);
  267. U32 extraPushConstantsSize = 0;
  268. if(idExtra && idExtra->m_extraPushConstantsSize)
  269. {
  270. ANKI_ASSERT(idExtra->m_extraPushConstantsSize <= sizeof(idExtra->m_extraPushConstants));
  271. memcpy(&pc.m_extra[0], idExtra->m_extraPushConstants.getBegin(), idExtra->m_extraPushConstantsSize);
  272. extraPushConstantsSize = idExtra->m_extraPushConstantsSize;
  273. }
  274. cmdb.setPushConstants(&pc, sizeof(Vec4) + extraPushConstantsSize);
  275. // Draw
  276. cmdb.drawIndexed(PrimitiveTopology::kTriangles, pcmd.ElemCount, 1, idxOffset, vertOffset);
  277. }
  278. }
  279. idxOffset += pcmd.ElemCount;
  280. }
  281. vertOffset += cmdList.VtxBuffer.Size;
  282. }
  283. // Restore state
  284. cmdb.setBlendFactors(0, BlendFactor::kOne, BlendFactor::kZero);
  285. cmdb.setCullMode(FaceSelectionBit::kBack);
  286. }
  287. } // end namespace anki