2
0

Canvas.cpp 12 KB

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