VirGLDemo.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * Copyright (c) 2022, Sahan Fernando <sahan.h.fernando@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/String.h>
  7. #include <AK/Vector.h>
  8. #include <Kernel/API/VirGL.h>
  9. #include <LibGUI/Application.h>
  10. #include <LibGUI/Icon.h>
  11. #include <LibGUI/Window.h>
  12. #include <LibGfx/Matrix4x4.h>
  13. #include <LibMain/Main.h>
  14. #include <fcntl.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <sys/ioctl.h>
  18. #include <sys/ioctl_numbers.h>
  19. #include <unistd.h>
  20. #include "CommandBufferBuilder.h"
  21. #include "VirGLProtocol.h"
  22. #include "Widget.h"
  23. static const char* frag_shader = "FRAG\n"
  24. "PROPERTY FS_COLOR0_WRITES_ALL_CBUFS 1\n"
  25. "DCL IN[0], COLOR, COLOR\n"
  26. "DCL OUT[0], COLOR\n"
  27. " 0: MOV OUT[0], IN[0]\n"
  28. " 1: END\n";
  29. static const char* vert_shader = "VERT\n"
  30. "DCL IN[0]\n"
  31. "DCL IN[1]\n"
  32. "DCL OUT[0], POSITION\n"
  33. "DCL OUT[1], COLOR\n"
  34. "DCL CONST[0..3]\n"
  35. "DCL TEMP[0..1]\n"
  36. " 0: MUL TEMP[0], IN[0].xxxx, CONST[0]\n"
  37. " 1: MAD TEMP[1], IN[0].yyyy, CONST[1], TEMP[0]\n"
  38. " 2: MAD TEMP[0], IN[0].zzzz, CONST[2], TEMP[1]\n"
  39. " 3: MAD OUT[0], IN[0].wwww, CONST[3], TEMP[0]\n"
  40. " 4: MOV_SAT OUT[1], IN[1]\n"
  41. " 5: END\n";
  42. struct VertexData {
  43. float r;
  44. float g;
  45. float b;
  46. float x;
  47. float y;
  48. float z;
  49. };
  50. int gpu_fd;
  51. ResourceID vbo_resource_id;
  52. ResourceID drawtarget;
  53. ResourceID depthbuffer_surface;
  54. ObjectHandle blend_handle;
  55. ObjectHandle drawtarget_surface_handle;
  56. ObjectHandle depthbuffer_surface_handle;
  57. ObjectHandle ve_handle;
  58. ObjectHandle frag_shader_handle;
  59. ObjectHandle vert_shader_handle;
  60. ObjectHandle rasterizer_handle;
  61. ObjectHandle dsa_handle;
  62. Vector<VertexData> g_vertices;
  63. static ObjectHandle allocate_handle()
  64. {
  65. static u32 last_allocated_handle = 0;
  66. return { ++last_allocated_handle };
  67. }
  68. static void upload_command_buffer(Vector<u32> const& command_buffer)
  69. {
  70. VERIFY(command_buffer.size() <= NumericLimits<u32>::max());
  71. VirGLCommandBuffer command_buffer_descriptor {
  72. .data = command_buffer.data(),
  73. .num_elems = (u32)command_buffer.size(),
  74. };
  75. VERIFY(ioctl(gpu_fd, VIRGL_IOCTL_SUBMIT_CMD, &command_buffer_descriptor) >= 0);
  76. }
  77. static ResourceID create_virgl_resource(VirGL3DResourceSpec& spec)
  78. {
  79. VERIFY(ioctl(gpu_fd, VIRGL_IOCTL_CREATE_RESOURCE, &spec) >= 0);
  80. return spec.created_resource_id;
  81. }
  82. static Vector<VertexData> gen_vertex_data()
  83. {
  84. Vector<VertexData> data;
  85. static const VertexData vertices[8] = {
  86. VertexData { .r = 0, .g = 0, .b = 0, .x = -0.5, .y = -0.5, .z = -0.5 },
  87. VertexData { .r = 0, .g = 0, .b = 0, .x = 0.5, .y = -0.5, .z = -0.5 },
  88. VertexData { .r = 0, .g = 0, .b = 0, .x = -0.5, .y = 0.5, .z = -0.5 },
  89. VertexData { .r = 0, .g = 0, .b = 0, .x = 0.5, .y = 0.5, .z = -0.5 },
  90. VertexData { .r = 0, .g = 0, .b = 0, .x = -0.5, .y = -0.5, .z = 0.5 },
  91. VertexData { .r = 0, .g = 0, .b = 0, .x = 0.5, .y = -0.5, .z = 0.5 },
  92. VertexData { .r = 0, .g = 0, .b = 0, .x = -0.5, .y = 0.5, .z = 0.5 },
  93. VertexData { .r = 0, .g = 0, .b = 0, .x = 0.5, .y = 0.5, .z = 0.5 },
  94. };
  95. size_t tris[36] = {
  96. 0, 1, 2, 1, 3, 2, // Top
  97. 4, 0, 6, 0, 2, 6, // Left
  98. 4, 5, 0, 5, 1, 0, // Up
  99. 1, 5, 3, 5, 7, 3, // Right
  100. 2, 3, 6, 3, 7, 6, // Down
  101. 5, 4, 7, 4, 6, 7, // Bottom
  102. };
  103. for (auto index : tris) {
  104. data.append(vertices[index]);
  105. }
  106. // Choose random colors for each face of the cube
  107. for (auto i = 0; i < 6; ++i) {
  108. float red = (rand() % 256) / 255.f;
  109. float green = (rand() % 256) / 255.f;
  110. float blue = (rand() % 256) / 255.f;
  111. for (auto j = 0; j < 6; ++j) {
  112. auto& vertex = data[i * 6 + j];
  113. vertex.r = red;
  114. vertex.g = green;
  115. vertex.b = blue;
  116. }
  117. }
  118. return data;
  119. }
  120. static void init()
  121. {
  122. // Open the device
  123. gpu_fd = open("/dev/gpu0", O_RDWR);
  124. VERIFY(gpu_fd >= 0);
  125. // Create a virgl context for this file descriptor
  126. VERIFY(ioctl(gpu_fd, VIRGL_IOCTL_CREATE_CONTEXT) >= 0);
  127. // Create a VertexElements resource
  128. VirGL3DResourceSpec vbo_spec {
  129. .target = to_underlying(Gallium::PipeTextureTarget::BUFFER), // pipe_texture_target
  130. .format = 45, // pipe_to_virgl_format
  131. .bind = VIRGL_BIND_VERTEX_BUFFER,
  132. .width = PAGE_SIZE,
  133. .height = 1,
  134. .depth = 1,
  135. .array_size = 1,
  136. .last_level = 0,
  137. .nr_samples = 0,
  138. .flags = 0,
  139. .created_resource_id = 0,
  140. };
  141. vbo_resource_id = create_virgl_resource(vbo_spec);
  142. // Create a texture to draw to
  143. VirGL3DResourceSpec drawtarget_spec {
  144. .target = to_underlying(Gallium::PipeTextureTarget::TEXTURE_RECT), // pipe_texture_target
  145. .format = to_underlying(Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM), // pipe_to_virgl_format
  146. .bind = VIRGL_BIND_RENDER_TARGET,
  147. .width = DRAWTARGET_WIDTH,
  148. .height = DRAWTARGET_HEIGHT,
  149. .depth = 1,
  150. .array_size = 1,
  151. .last_level = 0,
  152. .nr_samples = 0,
  153. .flags = 0,
  154. .created_resource_id = 0,
  155. };
  156. drawtarget = create_virgl_resource(drawtarget_spec);
  157. // Create a depthbuffer surface
  158. VirGL3DResourceSpec depthbuffer_surface_spec {
  159. .target = to_underlying(Gallium::PipeTextureTarget::TEXTURE_RECT), // pipe_texture_target
  160. .format = to_underlying(Protocol::TextureFormat::VIRTIO_GPU_FORMAT_Z32_FLOAT), // pipe_to_virgl_format
  161. .bind = VIRGL_BIND_RENDER_TARGET | VIRGL_BIND_DEPTH_STENCIL,
  162. .width = DRAWTARGET_WIDTH,
  163. .height = DRAWTARGET_HEIGHT,
  164. .depth = 1,
  165. .array_size = 1,
  166. .last_level = 0,
  167. .nr_samples = 0,
  168. .flags = 0,
  169. .created_resource_id = 0,
  170. };
  171. depthbuffer_surface = create_virgl_resource(depthbuffer_surface_spec);
  172. // Initialize all required state
  173. CommandBufferBuilder builder;
  174. // Create and set the blend, to control the color mask
  175. blend_handle = allocate_handle();
  176. builder.append_create_blend(blend_handle);
  177. builder.append_bind_blend(blend_handle);
  178. // Create drawtarget surface
  179. drawtarget_surface_handle = allocate_handle();
  180. builder.append_create_surface(drawtarget, drawtarget_surface_handle, Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM);
  181. // Create depthbuffer surface
  182. depthbuffer_surface_handle = allocate_handle();
  183. builder.append_create_surface(depthbuffer_surface, depthbuffer_surface_handle, Protocol::TextureFormat::VIRTIO_GPU_FORMAT_Z32_FLOAT);
  184. // Set some framebuffer state (attached handle, framebuffer size, etc)
  185. builder.append_set_framebuffer_state(drawtarget_surface_handle, depthbuffer_surface_handle);
  186. builder.append_set_framebuffer_state_no_attach();
  187. // Set the vertex buffer
  188. builder.append_set_vertex_buffers(sizeof(VertexData), 0, vbo_resource_id);
  189. // Create and bind fragment shader
  190. frag_shader_handle = allocate_handle();
  191. builder.append_create_shader(frag_shader_handle, Gallium::ShaderType::SHADER_FRAGMENT, frag_shader);
  192. builder.append_bind_shader(frag_shader_handle, Gallium::ShaderType::SHADER_FRAGMENT);
  193. // Create and bind vertex shader
  194. vert_shader_handle = allocate_handle();
  195. builder.append_create_shader(vert_shader_handle, Gallium::ShaderType::SHADER_VERTEX, vert_shader);
  196. builder.append_bind_shader(vert_shader_handle, Gallium::ShaderType::SHADER_VERTEX);
  197. // Create a VertexElements object (used to specify layout of vertex data)
  198. ve_handle = allocate_handle();
  199. builder.append_create_vertex_elements(ve_handle);
  200. builder.append_bind_vertex_elements(ve_handle);
  201. // Create a DepthStencilAlpha (DSA) object
  202. dsa_handle = allocate_handle();
  203. builder.append_create_dsa(dsa_handle);
  204. builder.append_bind_dsa(dsa_handle);
  205. // Create a Rasterizer object
  206. rasterizer_handle = allocate_handle();
  207. builder.append_create_rasterizer(rasterizer_handle);
  208. builder.append_bind_rasterizer(rasterizer_handle);
  209. // Set the Viewport
  210. builder.append_gl_viewport();
  211. // Upload buffer
  212. upload_command_buffer(builder.build());
  213. // Setup the vertex data
  214. g_vertices = gen_vertex_data();
  215. }
  216. static Gfx::FloatMatrix4x4 get_transform_matrix(unsigned step_num)
  217. {
  218. auto mat = Gfx::FloatMatrix4x4::identity();
  219. float angle = step_num * 0.02;
  220. mat = mat * Gfx::rotation_matrix(FloatVector3(1, 0, 0), angle * 1.17356641f);
  221. mat = mat * Gfx::rotation_matrix(FloatVector3(0, 1, 0), angle * 0.90533273f);
  222. mat = mat * Gfx::rotation_matrix(FloatVector3(0, 0, 1), angle);
  223. return mat;
  224. }
  225. static Vector<float> encode_constant_buffer(Gfx::FloatMatrix4x4 const& mat)
  226. {
  227. // Flip the y axis. This is done because OpenGLs coordinate space has a Y-axis of
  228. // Opposite direction to that of LibGfx
  229. Gfx::FloatMatrix4x4 flip_y = Gfx::FloatMatrix4x4::identity();
  230. flip_y.elements()[1][1] = -1;
  231. auto real_mat = mat * flip_y;
  232. Vector<float> values;
  233. for (int i = 0; i < 4; ++i) {
  234. for (int j = 0; j < 4; ++j) {
  235. values.append(real_mat.elements()[i][j]);
  236. }
  237. }
  238. return values;
  239. }
  240. static void draw_frame(unsigned step_num)
  241. {
  242. // Get model matrix
  243. auto model_matrix = get_transform_matrix(step_num);
  244. VirGLTransferDescriptor descriptor {
  245. .data = (void*)g_vertices.data(),
  246. .offset_in_region = 0,
  247. .num_bytes = sizeof(VertexData) * g_vertices.size(),
  248. .direction = VIRGL_DATA_DIR_GUEST_TO_HOST,
  249. };
  250. // Transfer data from vertices array to kernel virgl transfer region
  251. VERIFY(ioctl(gpu_fd, VIRGL_IOCTL_TRANSFER_DATA, &descriptor) >= 0);
  252. // Create command buffer
  253. CommandBufferBuilder builder;
  254. // Transfer data from kernel virgl transfer region to host resource
  255. builder.append_transfer3d(vbo_resource_id, sizeof(VertexData) * g_vertices.size(), 1, 1, VIRGL_DATA_DIR_GUEST_TO_HOST);
  256. builder.append_end_transfers_3d();
  257. // Set the constant buffer to the identity matrix
  258. builder.append_set_constant_buffer(encode_constant_buffer(model_matrix));
  259. // Clear the framebuffer
  260. builder.append_gl_clear(0, 0, 0);
  261. // Draw the vbo
  262. builder.append_draw_vbo(g_vertices.size());
  263. // Upload the buffer
  264. upload_command_buffer(builder.build());
  265. }
  266. void update_frame(RefPtr<Gfx::Bitmap> target, unsigned num_cycles)
  267. {
  268. VERIFY(target->width() == DRAWTARGET_WIDTH);
  269. VERIFY(target->height() == DRAWTARGET_HEIGHT);
  270. // Run logic to draw the frame
  271. draw_frame(num_cycles);
  272. // Transfer data back from hypervisor to kernel transfer region
  273. CommandBufferBuilder builder;
  274. builder.append_transfer3d(drawtarget, DRAWTARGET_WIDTH, DRAWTARGET_HEIGHT, 1, VIRGL_DATA_DIR_HOST_TO_GUEST);
  275. builder.append_end_transfers_3d();
  276. upload_command_buffer(builder.build());
  277. // Copy from kernel transfer region to userspace
  278. VirGLTransferDescriptor descriptor {
  279. .data = (void*)target->scanline_u8(0),
  280. .offset_in_region = 0,
  281. .num_bytes = DRAWTARGET_WIDTH * DRAWTARGET_HEIGHT * sizeof(u32),
  282. .direction = VIRGL_DATA_DIR_HOST_TO_GUEST,
  283. };
  284. VERIFY(ioctl(gpu_fd, VIRGL_IOCTL_TRANSFER_DATA, &descriptor) >= 0);
  285. }
  286. ErrorOr<int> serenity_main(Main::Arguments arguments)
  287. {
  288. auto app = TRY(GUI::Application::try_create(arguments));
  289. auto window = TRY(GUI::Window::try_create());
  290. window->set_double_buffering_enabled(true);
  291. window->set_title("VirGLDemo");
  292. window->set_resizable(false);
  293. window->resize(DRAWTARGET_WIDTH, DRAWTARGET_HEIGHT);
  294. window->set_has_alpha_channel(false);
  295. window->set_alpha_hit_threshold(1);
  296. auto demo = TRY(window->try_set_main_widget<Demo>());
  297. auto app_icon = GUI::Icon::default_icon("app-cube");
  298. window->set_icon(app_icon.bitmap_for_size(16));
  299. init();
  300. window->show();
  301. return app->exec();
  302. }