diff options
author | Sahan Fernando <sahan.h.fernando@gmail.com> | 2022-02-18 13:27:19 +1100 |
---|---|---|
committer | Ali Mohammad Pur <Ali.mpfard@gmail.com> | 2022-03-09 14:58:48 +0330 |
commit | fd5eaf6494a920ba76bc9629b553bae6fd12ee19 (patch) | |
tree | 2c2e0acf05931de52f6c67f54414b49be2805540 | |
parent | 2939f65753bf4cfaaf5605dc545898018ea99785 (diff) | |
download | serenity-fd5eaf6494a920ba76bc9629b553bae6fd12ee19.zip |
Demos: Create demo for VirGL gpu device
This is a simple demo for VirGL, that does the bare minimum required to
create and render a spinning cube (with one color per face).
-rw-r--r-- | Userland/Demos/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Demos/VirGLDemo/CMakeLists.txt | 13 | ||||
-rw-r--r-- | Userland/Demos/VirGLDemo/CommandBufferBuilder.cpp | 291 | ||||
-rw-r--r-- | Userland/Demos/VirGLDemo/CommandBufferBuilder.h | 41 | ||||
-rw-r--r-- | Userland/Demos/VirGLDemo/VirGLDemo.cpp | 323 | ||||
-rw-r--r-- | Userland/Demos/VirGLDemo/VirGLProtocol.h | 210 | ||||
-rw-r--r-- | Userland/Demos/VirGLDemo/Widget.cpp | 37 | ||||
-rw-r--r-- | Userland/Demos/VirGLDemo/Widget.h | 41 |
8 files changed, 957 insertions, 0 deletions
diff --git a/Userland/Demos/CMakeLists.txt b/Userland/Demos/CMakeLists.txt index f75aa4adc7..cb9b1e2f00 100644 --- a/Userland/Demos/CMakeLists.txt +++ b/Userland/Demos/CMakeLists.txt @@ -9,4 +9,5 @@ add_subdirectory(ModelGallery) add_subdirectory(Mouse) add_subdirectory(Screensaver) add_subdirectory(Starfield) +add_subdirectory(VirGLDemo) add_subdirectory(WidgetGallery) diff --git a/Userland/Demos/VirGLDemo/CMakeLists.txt b/Userland/Demos/VirGLDemo/CMakeLists.txt new file mode 100644 index 0000000000..8b11dec1a8 --- /dev/null +++ b/Userland/Demos/VirGLDemo/CMakeLists.txt @@ -0,0 +1,13 @@ +serenity_component( + VirGLDemo + TARGETS VirGLDemo +) + +set(SOURCES + CommandBufferBuilder.cpp + VirGLDemo.cpp + Widget.cpp +) + +serenity_app(VirGLDemo ICON app-cube) +target_link_libraries(VirGLDemo LibMain LibGUI) diff --git a/Userland/Demos/VirGLDemo/CommandBufferBuilder.cpp b/Userland/Demos/VirGLDemo/CommandBufferBuilder.cpp new file mode 100644 index 0000000000..8d7b8627db --- /dev/null +++ b/Userland/Demos/VirGLDemo/CommandBufferBuilder.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2022, Sahan Fernando <sahan.h.fernando@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <Kernel/API/VirGL.h> +#include <sys/ioctl_numbers.h> + +#include "CommandBufferBuilder.h" +#include "VirGLProtocol.h" +#include "Widget.h" + +static u32 encode_command(u32 length, u32 mid, Protocol::VirGLCommand command) +{ + u32 command_value = to_underlying(command); + return (length << 16) | ((mid & 0xff) << 8) | (command_value & 0xff); +}; + +class CommandBuilder { +public: + CommandBuilder() = delete; + CommandBuilder(Vector<u32>& buffer, Protocol::VirGLCommand command, u32 mid) + : m_buffer(buffer) + , m_start_offset(buffer.size()) + , m_command(command) + , m_command_mid(mid) + { + m_buffer.append(0); + } + void appendu32(u32 value) + { + VERIFY(!m_finalized); + m_buffer.append(value); + } + void appendf32(float value) + { + VERIFY(!m_finalized); + m_buffer.append(bit_cast<u32>(value)); + } + void appendf64(double value) + { + VERIFY(!m_finalized); + m_buffer.append(0); + m_buffer.append(0); + auto* depth = (u64*)(&m_buffer[m_buffer.size() - 2]); + *depth = bit_cast<u64>(value); + } + void append_string_null_padded(StringView string) + { + VERIFY(!m_finalized); + // Remember to have at least one null terminator byte + auto length = string.length() + 1; + auto num_required_words = (length + sizeof(u32) - 1) / sizeof(u32); + m_buffer.resize(m_buffer.size() + num_required_words); + char* dest = (char*)&m_buffer[m_buffer.size() - num_required_words]; + memcpy(dest, string.characters_without_null_termination(), string.length()); + // Pad end with null bytes + memset(&dest[string.length()], 0, 4 * num_required_words - string.length()); + } + void finalize() + { + if (!m_finalized) { + m_finalized = true; + size_t num_elems = m_buffer.size() - m_start_offset - 1; + m_buffer[m_start_offset] = encode_command(num_elems, m_command_mid, m_command); + } + } + ~CommandBuilder() + { + if (!m_finalized) + finalize(); + } + +private: + Vector<u32>& m_buffer; + size_t m_start_offset; + Protocol::VirGLCommand m_command; + u32 m_command_mid; + bool m_finalized { false }; +}; + +void CommandBufferBuilder::append_set_tweaks(u32 id, u32 value) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_TWEAKS, 0); + builder.appendu32(id); + builder.appendu32(value); +} + +void CommandBufferBuilder::append_transfer3d(ResourceID resource, size_t width, size_t height, size_t depth, size_t direction) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::TRANSFER3D, 0); + builder.appendu32(resource.value()); // res_handle + builder.appendu32(0); // level + builder.appendu32(242); // usage + builder.appendu32(0); // stride + builder.appendu32(0); // layer_stride + builder.appendu32(0); // x + builder.appendu32(0); // y + builder.appendu32(0); // z + builder.appendu32(width); // width + builder.appendu32(height); // height + builder.appendu32(depth); // depth + builder.appendu32(0); // data_offset + builder.appendu32(direction); // direction +} + +void CommandBufferBuilder::append_end_transfers_3d() +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::END_TRANSFERS, 0); +} + +void CommandBufferBuilder::append_draw_vbo(u32 count) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::DRAW_VBO, 0); + builder.appendu32(0); // start + builder.appendu32(count); // count + builder.appendu32(to_underlying(Protocol::PipePrimitiveTypes::TRIANGLES)); // mode + builder.appendu32(0); // indexed + builder.appendu32(1); // instance_count + builder.appendu32(0); // index_bias + builder.appendu32(0); // start_instance + builder.appendu32(0); // primitive_restart + builder.appendu32(0); // restart_index + builder.appendu32(0); // min_index + builder.appendu32(0xffffffff); // max_index + builder.appendu32(0); // cso +} + +void CommandBufferBuilder::append_gl_clear(float r, float g, float b) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CLEAR, 0); + Protocol::ClearType clear_flags {}; + clear_flags.flags.depth = 1; + clear_flags.flags.color0 = 1; + builder.appendu32(clear_flags.value); + builder.appendf32(r); + builder.appendf32(g); + builder.appendf32(b); + builder.appendf32(1.0f); // Alpha + builder.appendf64(1.0); // Depth + builder.appendu32(0); // Stencil +} + +void CommandBufferBuilder::append_set_vertex_buffers(u32 stride, u32 offset, ResourceID resource) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_VERTEX_BUFFERS, 0); + builder.appendu32(stride); + builder.appendu32(offset); + builder.appendu32(resource.value()); +} + +void CommandBufferBuilder::append_create_blend(ObjectHandle handle) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, to_underlying(Protocol::ObjectType::BLEND)); + builder.appendu32(handle.value()); + builder.appendu32(4); // Enable dither flag, and nothing else + builder.appendu32(0); + builder.appendu32(0x78000000); // Enable all bits of color mask for color buffer 0, and nothing else + for (size_t i = 1; i < 8; ++i) { + builder.appendu32(0); // Explicitly disable all flags for other color buffers + } +} + +void CommandBufferBuilder::append_bind_blend(ObjectHandle handle) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::BIND_OBJECT, to_underlying(Protocol::ObjectType::BLEND)); + builder.appendu32(handle.value()); // VIRGL_OBJ_BIND_HANDLE +} + +void CommandBufferBuilder::append_create_vertex_elements(ObjectHandle handle) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, to_underlying(Protocol::ObjectType::VERTEX_ELEMENTS)); + builder.appendu32(handle.value()); + builder.appendu32(12); // src_offset_0 + builder.appendu32(0); // instance_divisor_0 + builder.appendu32(0); // vertex_buffer_index_0 + builder.appendu32(30); // src_format_0 (PIPE_FORMAT_R32G32B32_FLOAT = 30) + builder.appendu32(0); // src_offset_1 + builder.appendu32(0); // instance_divisor_1 + builder.appendu32(0); // vertex_buffer_index_1 + builder.appendu32(30); // src_format_1 (PIPE_FORMAT_R32G32B32_FLOAT = 30) +} + +void CommandBufferBuilder::append_bind_vertex_elements(ObjectHandle handle) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::BIND_OBJECT, to_underlying(Protocol::ObjectType::VERTEX_ELEMENTS)); + builder.appendu32(handle.value()); // VIRGL_OBJ_BIND_HANDLE +} + +void CommandBufferBuilder::append_create_surface(ResourceID drawtarget_resource, ObjectHandle drawtarget_handle, Protocol::TextureFormat format) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, to_underlying(Protocol::ObjectType::SURFACE)); + builder.appendu32(drawtarget_handle.value()); + builder.appendu32(drawtarget_resource.value()); + builder.appendu32(to_underlying(format)); + builder.appendu32(0); // First element / Texture Level + builder.appendu32(0); // Last element / Texture Element +} + +void CommandBufferBuilder::append_set_framebuffer_state(ObjectHandle drawtarget, ObjectHandle depthbuffer) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_FRAMEBUFFER_STATE, 0); + builder.appendu32(1); // nr_cbufs + builder.appendu32(depthbuffer.value()); // zsurf_handle + builder.appendu32(drawtarget.value()); // surf_handle +} + +void CommandBufferBuilder::append_gl_viewport() +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_VIEWPORT_STATE, 0); + builder.appendu32(0); + builder.appendf32(DRAWTARGET_WIDTH / 2); // scale_x + builder.appendf32((DRAWTARGET_HEIGHT / 2)); // scale_y (flipped, due to VirGL being different from our coordinate space) + builder.appendf32(0.5f); // scale_z + builder.appendf32(DRAWTARGET_WIDTH / 2); // translate_x + builder.appendf32(DRAWTARGET_HEIGHT / 2); // translate_y + builder.appendf32(0.5f); // translate_z +} + +void CommandBufferBuilder::append_set_framebuffer_state_no_attach() +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_FRAMEBUFFER_STATE_NO_ATTACH, 0); + builder.appendu32((DRAWTARGET_HEIGHT << 16) | DRAWTARGET_WIDTH); // (height << 16) | width + builder.appendu32(0); // (samples << 16) | layers +} + +void CommandBufferBuilder::append_set_constant_buffer(Vector<float> const& constant_buffer) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::SET_CONSTANT_BUFFER, 0); + builder.appendu32(to_underlying(Gallium::ShaderType::SHADER_VERTEX)); + builder.appendu32(0); // index (currently unused according to virglrenderer source code) + for (auto v : constant_buffer) { + builder.appendf32(v); + } +} + +void CommandBufferBuilder::append_create_shader(ObjectHandle handle, Gallium::ShaderType shader_type, const char* shader_data) +{ + size_t shader_len = strlen(shader_data) + 1; // Need to remember to copy null terminator as well if needed + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, to_underlying(Protocol::ObjectType::SHADER)); + builder.appendu32(handle.value()); // VIRGL_OBJ_CREATE_HANDLE + builder.appendu32(to_underlying(shader_type)); + builder.appendu32(0); // VIRGL_OBJ_SHADER_OFFSET + builder.appendu32(shader_len); + builder.appendu32(0); // VIRGL_OBJ_SHADER_NUM_TOKENS + builder.append_string_null_padded(shader_data); +} + +void CommandBufferBuilder::append_bind_shader(ObjectHandle handle, Gallium::ShaderType shader_type) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::BIND_SHADER, 0); + builder.appendu32(handle.value()); // VIRGL_OBJ_BIND_HANDLE + builder.appendu32(to_underlying(shader_type)); +} + +void CommandBufferBuilder::append_create_rasterizer(ObjectHandle handle) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, to_underlying(Protocol::ObjectType::RASTERIZER)); + builder.appendu32(handle.value()); // Handle + builder.appendu32(0x00000002); // S0 (bitfield of state bits) + builder.appendf32(1.0); // Point size + builder.appendu32(0); // Sprite coord enable + builder.appendu32(0x00000000); // S3 (bitfield of state bits) + builder.appendf32(0.1); // Line width + builder.appendf32(0.0); // Offset units + builder.appendf32(0.0); // offset scale + builder.appendf32(0.0); // Offset clamp +} + +void CommandBufferBuilder::append_bind_rasterizer(ObjectHandle handle) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::BIND_OBJECT, to_underlying(Protocol::ObjectType::RASTERIZER)); + builder.appendu32(handle.value()); // VIRGL_OBJ_BIND_HANDLE +} + +void CommandBufferBuilder::append_create_dsa(ObjectHandle handle) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::CREATE_OBJECT, to_underlying(Protocol::ObjectType::DSA)); + builder.appendu32(handle.value()); // Handle + builder.appendu32(0x00000007); // S0 (bitset: (v >> 0) & 1 = depth.enabled, (v >> 1) & 1 = depth.writemask, (v >> 2) & 7 = depth.func) + builder.appendu32(0x00000000); // S1 (bitset for 1st stencil buffer) + builder.appendu32(0x00000000); // S2 (bitset for 2nd stencil buffer) + builder.appendf32(1.0); // Alpha Ref +} + +void CommandBufferBuilder::append_bind_dsa(ObjectHandle handle) +{ + CommandBuilder builder(m_buffer, Protocol::VirGLCommand::BIND_OBJECT, to_underlying(Protocol::ObjectType::DSA)); + builder.appendu32(handle.value()); // VIRGL_OBJ_BIND_HANDLE +} diff --git a/Userland/Demos/VirGLDemo/CommandBufferBuilder.h b/Userland/Demos/VirGLDemo/CommandBufferBuilder.h new file mode 100644 index 0000000000..8be91f9f35 --- /dev/null +++ b/Userland/Demos/VirGLDemo/CommandBufferBuilder.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022, Sahan Fernando <sahan.h.fernando@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Vector.h> +#include <sys/ioctl_numbers.h> + +#include "VirGLProtocol.h" + +class CommandBufferBuilder { +public: + void append_set_tweaks(u32 id, u32 value); + void append_transfer3d(ResourceID resource, size_t width, size_t height = 1, size_t depth = 1, size_t direction = VIRGL_DATA_DIR_GUEST_TO_HOST); + void append_end_transfers_3d(); + void append_draw_vbo(u32 count); + void append_gl_clear(float r, float g, float b); + void append_set_vertex_buffers(u32 stride, u32 offset, ResourceID resource); + void append_create_blend(ObjectHandle handle); + void append_bind_blend(ObjectHandle handle); + void append_create_surface(ResourceID drawtarget_resource, ObjectHandle drawtarget_handle, Protocol::TextureFormat format); + void append_set_framebuffer_state(ObjectHandle drawtarget, ObjectHandle depthbuffer = 0); + void append_create_vertex_elements(ObjectHandle handle); + void append_bind_vertex_elements(ObjectHandle handle); + void append_gl_viewport(); + void append_set_framebuffer_state_no_attach(); + void append_set_constant_buffer(Vector<float> const& constant_buffer); + void append_create_shader(ObjectHandle handle, Gallium::ShaderType shader_type, const char* shader_data); + void append_bind_shader(ObjectHandle handle, Gallium::ShaderType shader_type); + void append_create_rasterizer(ObjectHandle handle); + void append_bind_rasterizer(ObjectHandle handle); + void append_create_dsa(ObjectHandle handle); + void append_bind_dsa(ObjectHandle handle); + Vector<u32> const& build() { return m_buffer; } + +private: + Vector<u32> m_buffer; +}; diff --git a/Userland/Demos/VirGLDemo/VirGLDemo.cpp b/Userland/Demos/VirGLDemo/VirGLDemo.cpp new file mode 100644 index 0000000000..f3763a54e1 --- /dev/null +++ b/Userland/Demos/VirGLDemo/VirGLDemo.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2022, Sahan Fernando <sahan.h.fernando@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/String.h> +#include <AK/Vector.h> +#include <Kernel/API/VirGL.h> +#include <LibGUI/Application.h> +#include <LibGUI/Icon.h> +#include <LibGUI/Window.h> +#include <LibGfx/Matrix4x4.h> +#include <LibMain/Main.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/ioctl_numbers.h> +#include <unistd.h> + +#include "CommandBufferBuilder.h" +#include "VirGLProtocol.h" +#include "Widget.h" + +static const char* frag_shader = "FRAG\n" + "PROPERTY FS_COLOR0_WRITES_ALL_CBUFS 1\n" + "DCL IN[0], COLOR, COLOR\n" + "DCL OUT[0], COLOR\n" + " 0: MOV OUT[0], IN[0]\n" + " 1: END\n"; + +static const char* vert_shader = "VERT\n" + "DCL IN[0]\n" + "DCL IN[1]\n" + "DCL OUT[0], POSITION\n" + "DCL OUT[1], COLOR\n" + "DCL CONST[0..3]\n" + "DCL TEMP[0..1]\n" + " 0: MUL TEMP[0], IN[0].xxxx, CONST[0]\n" + " 1: MAD TEMP[1], IN[0].yyyy, CONST[1], TEMP[0]\n" + " 2: MAD TEMP[0], IN[0].zzzz, CONST[2], TEMP[1]\n" + " 3: MAD OUT[0], IN[0].wwww, CONST[3], TEMP[0]\n" + " 4: MOV_SAT OUT[1], IN[1]\n" + " 5: END\n"; + +struct VertexData { + float r; + float g; + float b; + float x; + float y; + float z; +}; + +int gpu_fd; +ResourceID vbo_resource_id; +ResourceID drawtarget; +ResourceID depthbuffer_surface; +ObjectHandle blend_handle; +ObjectHandle drawtarget_surface_handle; +ObjectHandle depthbuffer_surface_handle; +ObjectHandle ve_handle; +ObjectHandle frag_shader_handle; +ObjectHandle vert_shader_handle; +ObjectHandle rasterizer_handle; +ObjectHandle dsa_handle; +Vector<VertexData> g_vertices; + +static ObjectHandle allocate_handle() +{ + static u32 last_allocated_handle = 0; + return { ++last_allocated_handle }; +} + +static void upload_command_buffer(Vector<u32> const& command_buffer) +{ + VERIFY(command_buffer.size() <= NumericLimits<u32>::max()); + VirGLCommandBuffer command_buffer_descriptor { + .data = command_buffer.data(), + .num_elems = (u32)command_buffer.size(), + }; + VERIFY(ioctl(gpu_fd, VIRGL_IOCTL_SUBMIT_CMD, &command_buffer_descriptor) >= 0); +} + +static ResourceID create_virgl_resource(VirGL3DResourceSpec& spec) +{ + VERIFY(ioctl(gpu_fd, VIRGL_IOCTL_CREATE_RESOURCE, &spec) >= 0); + return spec.created_resource_id; +} + +static Vector<VertexData> gen_vertex_data() +{ + Vector<VertexData> data; + static const VertexData vertices[8] = { + VertexData { .r = 0, .g = 0, .b = 0, .x = -0.5, .y = -0.5, .z = -0.5 }, + VertexData { .r = 0, .g = 0, .b = 0, .x = 0.5, .y = -0.5, .z = -0.5 }, + VertexData { .r = 0, .g = 0, .b = 0, .x = -0.5, .y = 0.5, .z = -0.5 }, + VertexData { .r = 0, .g = 0, .b = 0, .x = 0.5, .y = 0.5, .z = -0.5 }, + VertexData { .r = 0, .g = 0, .b = 0, .x = -0.5, .y = -0.5, .z = 0.5 }, + VertexData { .r = 0, .g = 0, .b = 0, .x = 0.5, .y = -0.5, .z = 0.5 }, + VertexData { .r = 0, .g = 0, .b = 0, .x = -0.5, .y = 0.5, .z = 0.5 }, + VertexData { .r = 0, .g = 0, .b = 0, .x = 0.5, .y = 0.5, .z = 0.5 }, + }; + size_t tris[36] = { + 0, 1, 2, 1, 3, 2, // Top + 4, 0, 6, 0, 2, 6, // Left + 4, 5, 0, 5, 1, 0, // Up + 1, 5, 3, 5, 7, 3, // Right + 2, 3, 6, 3, 7, 6, // Down + 5, 4, 7, 4, 6, 7, // Bottom + }; + for (auto index : tris) { + data.append(vertices[index]); + } + // Choose random colors for each face of the cube + for (auto i = 0; i < 6; ++i) { + float red = (rand() % 256) / 255.f; + float green = (rand() % 256) / 255.f; + float blue = (rand() % 256) / 255.f; + for (auto j = 0; j < 6; ++j) { + auto& vertex = data[i * 6 + j]; + vertex.r = red; + vertex.g = green; + vertex.b = blue; + } + } + return data; +} + +static void init() +{ + // Open the device + gpu_fd = open("/dev/gpu0", O_RDWR); + VERIFY(gpu_fd >= 0); + // Create a VertexElements resource + VirGL3DResourceSpec vbo_spec { + .target = to_underlying(Gallium::PipeTextureTarget::BUFFER), // pipe_texture_target + .format = 45, // pipe_to_virgl_format + .bind = VIRGL_BIND_VERTEX_BUFFER, + .width = PAGE_SIZE, + .height = 1, + .depth = 1, + .array_size = 1, + .last_level = 0, + .nr_samples = 0, + .flags = 0, + .created_resource_id = 0, + }; + vbo_resource_id = create_virgl_resource(vbo_spec); + // Create a texture to draw to + VirGL3DResourceSpec drawtarget_spec { + .target = to_underlying(Gallium::PipeTextureTarget::TEXTURE_RECT), // pipe_texture_target + .format = to_underlying(Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM), // pipe_to_virgl_format + .bind = VIRGL_BIND_RENDER_TARGET, + .width = DRAWTARGET_WIDTH, + .height = DRAWTARGET_HEIGHT, + .depth = 1, + .array_size = 1, + .last_level = 0, + .nr_samples = 0, + .flags = 0, + .created_resource_id = 0, + }; + drawtarget = create_virgl_resource(drawtarget_spec); + // Create a depthbuffer surface + VirGL3DResourceSpec depthbuffer_surface_spec { + .target = to_underlying(Gallium::PipeTextureTarget::TEXTURE_RECT), // pipe_texture_target + .format = to_underlying(Protocol::TextureFormat::VIRTIO_GPU_FORMAT_Z32_FLOAT), // pipe_to_virgl_format + .bind = VIRGL_BIND_RENDER_TARGET | VIRGL_BIND_DEPTH_STENCIL, + .width = DRAWTARGET_WIDTH, + .height = DRAWTARGET_HEIGHT, + .depth = 1, + .array_size = 1, + .last_level = 0, + .nr_samples = 0, + .flags = 0, + .created_resource_id = 0, + }; + depthbuffer_surface = create_virgl_resource(depthbuffer_surface_spec); + + // Initialize all required state + CommandBufferBuilder builder; + // Create and set the blend, to control the color mask + blend_handle = allocate_handle(); + builder.append_create_blend(blend_handle); + builder.append_bind_blend(blend_handle); + // Create drawtarget surface + drawtarget_surface_handle = allocate_handle(); + builder.append_create_surface(drawtarget, drawtarget_surface_handle, Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM); + // Create depthbuffer surface + depthbuffer_surface_handle = allocate_handle(); + builder.append_create_surface(depthbuffer_surface, depthbuffer_surface_handle, Protocol::TextureFormat::VIRTIO_GPU_FORMAT_Z32_FLOAT); + // Set some framebuffer state (attached handle, framebuffer size, etc) + builder.append_set_framebuffer_state(drawtarget_surface_handle, depthbuffer_surface_handle); + builder.append_set_framebuffer_state_no_attach(); + // Set the vertex buffer + builder.append_set_vertex_buffers(sizeof(VertexData), 0, vbo_resource_id); + // Create and bind fragment shader + frag_shader_handle = allocate_handle(); + builder.append_create_shader(frag_shader_handle, Gallium::ShaderType::SHADER_FRAGMENT, frag_shader); + builder.append_bind_shader(frag_shader_handle, Gallium::ShaderType::SHADER_FRAGMENT); + // Create and bind vertex shader + vert_shader_handle = allocate_handle(); + builder.append_create_shader(vert_shader_handle, Gallium::ShaderType::SHADER_VERTEX, vert_shader); + builder.append_bind_shader(vert_shader_handle, Gallium::ShaderType::SHADER_VERTEX); + // Create a VertexElements object (used to specify layout of vertex data) + ve_handle = allocate_handle(); + builder.append_create_vertex_elements(ve_handle); + builder.append_bind_vertex_elements(ve_handle); + // Create a DepthStencilAlpha (DSA) object + dsa_handle = allocate_handle(); + builder.append_create_dsa(dsa_handle); + builder.append_bind_dsa(dsa_handle); + // Create a Rasterizer object + rasterizer_handle = allocate_handle(); + builder.append_create_rasterizer(rasterizer_handle); + builder.append_bind_rasterizer(rasterizer_handle); + // Set the Viewport + builder.append_gl_viewport(); + // Upload buffer + upload_command_buffer(builder.build()); + + // Setup the vertex data + g_vertices = gen_vertex_data(); +} + +static Gfx::FloatMatrix4x4 get_transform_matrix(unsigned step_num) +{ + auto mat = Gfx::FloatMatrix4x4::identity(); + float angle = step_num * 0.02; + mat = mat * Gfx::rotation_matrix(FloatVector3(1, 0, 0), angle * 1.17356641f); + mat = mat * Gfx::rotation_matrix(FloatVector3(0, 1, 0), angle * 0.90533273f); + mat = mat * Gfx::rotation_matrix(FloatVector3(0, 0, 1), angle); + return mat; +} + +static Vector<float> encode_constant_buffer(Gfx::FloatMatrix4x4 const& mat) +{ + // Flip the y axis. This is done because OpenGLs coordinate space has a Y-axis of + // Opposite direction to that of LibGfx + Gfx::FloatMatrix4x4 flip_y = Gfx::FloatMatrix4x4::identity(); + flip_y.elements()[1][1] = -1; + auto real_mat = mat * flip_y; + Vector<float> values; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + values.append(real_mat.elements()[i][j]); + } + } + return values; +} + +static void draw_frame(unsigned step_num) +{ + // Get model matrix + auto model_matrix = get_transform_matrix(step_num); + VirGLTransferDescriptor descriptor { + .data = (void*)g_vertices.data(), + .offset_in_region = 0, + .num_bytes = sizeof(VertexData) * g_vertices.size(), + .direction = VIRGL_DATA_DIR_GUEST_TO_HOST, + }; + // Transfer data from vertices array to kernel virgl transfer region + VERIFY(ioctl(gpu_fd, VIRGL_IOCTL_TRANSFER_DATA, &descriptor) >= 0); + // Create command buffer + CommandBufferBuilder builder; + // Transfer data from kernel virgl transfer region to host resource + builder.append_transfer3d(vbo_resource_id, sizeof(VertexData) * g_vertices.size(), 1, 1, VIRGL_DATA_DIR_GUEST_TO_HOST); + builder.append_end_transfers_3d(); + // Set the constant buffer to the identity matrix + builder.append_set_constant_buffer(encode_constant_buffer(model_matrix)); + // Clear the framebuffer + builder.append_gl_clear(0, 0, 0); + // Draw the vbo + builder.append_draw_vbo(g_vertices.size()); + // Upload the buffer + upload_command_buffer(builder.build()); +} + +void update_frame(RefPtr<Gfx::Bitmap> target, unsigned num_cycles) +{ + VERIFY(target->width() == DRAWTARGET_WIDTH); + VERIFY(target->height() == DRAWTARGET_HEIGHT); + // Run logic to draw the frame + draw_frame(num_cycles); + // Transfer data back from hypervisor to kernel transfer region + CommandBufferBuilder builder; + builder.append_transfer3d(drawtarget, DRAWTARGET_WIDTH, DRAWTARGET_HEIGHT, 1, VIRGL_DATA_DIR_HOST_TO_GUEST); + builder.append_end_transfers_3d(); + upload_command_buffer(builder.build()); + // Copy from kernel transfer region to userspace + VirGLTransferDescriptor descriptor { + .data = (void*)target->scanline_u8(0), + .offset_in_region = 0, + .num_bytes = DRAWTARGET_WIDTH * DRAWTARGET_HEIGHT * sizeof(u32), + .direction = VIRGL_DATA_DIR_HOST_TO_GUEST, + }; + VERIFY(ioctl(gpu_fd, VIRGL_IOCTL_TRANSFER_DATA, &descriptor) >= 0); +} + +ErrorOr<int> serenity_main(Main::Arguments arguments) +{ + auto app = TRY(GUI::Application::try_create(arguments)); + + auto window = TRY(GUI::Window::try_create()); + window->set_double_buffering_enabled(true); + window->set_title("VirGLDemo"); + window->set_resizable(false); + window->resize(DRAWTARGET_WIDTH, DRAWTARGET_HEIGHT); + window->set_has_alpha_channel(false); + window->set_alpha_hit_threshold(1); + + auto demo = TRY(window->try_set_main_widget<Demo>()); + + auto app_icon = GUI::Icon::default_icon("app-cube"); + window->set_icon(app_icon.bitmap_for_size(16)); + + init(); + window->show(); + + return app->exec(); +} diff --git a/Userland/Demos/VirGLDemo/VirGLProtocol.h b/Userland/Demos/VirGLDemo/VirGLProtocol.h new file mode 100644 index 0000000000..21d9da96bc --- /dev/null +++ b/Userland/Demos/VirGLDemo/VirGLProtocol.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2022, Sahan Fernando <sahan.h.fernando@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/DistinctNumeric.h> + +TYPEDEF_DISTINCT_ORDERED_ID(u32, ObjectHandle); +TYPEDEF_DISTINCT_ORDERED_ID(u32, ResourceID); + +#define VIRGL_BIND_DEPTH_STENCIL (1 << 0) +#define VIRGL_BIND_RENDER_TARGET (1 << 1) +#define VIRGL_BIND_SAMPLER_VIEW (1 << 3) +#define VIRGL_BIND_VERTEX_BUFFER (1 << 4) +#define VIRGL_BIND_INDEX_BUFFER (1 << 5) +#define VIRGL_BIND_CONSTANT_BUFFER (1 << 6) +#define VIRGL_BIND_DISPLAY_TARGET (1 << 7) +#define VIRGL_BIND_COMMAND_ARGS (1 << 8) +#define VIRGL_BIND_STREAM_OUTPUT (1 << 11) +#define VIRGL_BIND_SHADER_BUFFER (1 << 14) +#define VIRGL_BIND_QUERY_BUFFER (1 << 15) +#define VIRGL_BIND_CURSOR (1 << 16) +#define VIRGL_BIND_CUSTOM (1 << 17) +#define VIRGL_BIND_SCANOUT (1 << 18) +#define VIRGL_BIND_STAGING (1 << 19) +#define VIRGL_BIND_SHARED (1 << 20) + +namespace Protocol { + +enum class TextureFormat : u32 { + // RGBA Formats + VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1, + VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2, + VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3, + VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4, + + VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67, + VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68, + + VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121, + VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134, + + // Stencil-Depth Formats + VIRTIO_GPU_FORMAT_Z16_UNORM = 16, + VIRTIO_GPU_FORMAT_Z32_UNORM = 17, + VIRTIO_GPU_FORMAT_Z32_FLOAT = 18, + VIRTIO_GPU_FORMAT_Z24_UNORM_S8_UINT = 19, + VIRTIO_GPU_FORMAT_S8_UINT_Z24_UNORM = 20, + VIRTIO_GPU_FORMAT_Z24X8_UNORM = 21, + VIRTIO_GPU_FORMAT_X8Z24_UNORM = 22, + VIRTIO_GPU_FORMAT_S8_UINT = 23, + VIRTIO_GPU_FORMAT_Z32_FLOAT_S8X24_UINT = 128, + VIRTIO_GPU_FORMAT_X24S8_UINT = 136, + VIRTIO_GPU_FORMAT_S8X24_UINT = 137, + VIRTIO_GPU_FORMAT_X32_S8X24_UINT = 138, + +}; + +enum class VirGLCommand : u32 { + NOP = 0, + CREATE_OBJECT = 1, + BIND_OBJECT, + DESTROY_OBJECT, + SET_VIEWPORT_STATE, + SET_FRAMEBUFFER_STATE, + SET_VERTEX_BUFFERS, + CLEAR, + DRAW_VBO, + RESOURCE_INLINE_WRITE, + SET_SAMPLER_VIEWS, + SET_INDEX_BUFFER, + SET_CONSTANT_BUFFER, + SET_STENCIL_REF, + SET_BLEND_COLOR, + SET_SCISSOR_STATE, + BLIT, + RESOURCE_COPY_REGION, + BIND_SAMPLER_STATES, + BEGIN_QUERY, + END_QUERY, + GET_QUERY_RESULT, + SET_POLYGON_STIPPLE, + SET_CLIP_STATE, + SET_SAMPLE_MASK, + SET_STREAMOUT_TARGETS, + SET_RENDER_CONDITION, + SET_UNIFORM_BUFFER, + + SET_SUB_CTX, + CREATE_SUB_CTX, + DESTROY_SUB_CTX, + BIND_SHADER, + SET_TESS_STATE, + SET_MIN_SAMPLES, + SET_SHADER_BUFFERS, + SET_SHADER_IMAGES, + MEMORY_BARRIER, + LAUNCH_GRID, + SET_FRAMEBUFFER_STATE_NO_ATTACH, + TEXTURE_BARRIER, + SET_ATOMIC_BUFFERS, + SET_DBG_FLAGS, + GET_QUERY_RESULT_QBO, + TRANSFER3D, + END_TRANSFERS, + COPY_TRANSFER3D, + SET_TWEAKS, + CLEAR_TEXTURE, + PIPE_RESOURCE_CREATE, + PIPE_RESOURCE_SET_TYPE, + GET_MEMORY_INFO, + SEND_STRING_MARKER, + MAX_COMMANDS +}; + +union ClearType { + struct { + u32 depth : 1; + u32 stencil : 1; + u32 color0 : 1; + u32 color1 : 1; + u32 color2 : 1; + u32 color3 : 1; + u32 color4 : 1; + u32 color5 : 1; + u32 color6 : 1; + u32 color7 : 1; + } flags; + u32 value; +}; + +enum class ObjectType : u32 { + NONE, + BLEND, + RASTERIZER, + DSA, + SHADER, + VERTEX_ELEMENTS, + SAMPLER_VIEW, + SAMPLER_STATE, + SURFACE, + QUERY, + STREAMOUT_TARGET, + MSAA_SURFACE, + MAX_OBJECTS, +}; + +enum class PipeTextureTarget : u32 { + BUFFER = 0, + TEXTURE_1D, + TEXTURE_2D, + TEXTURE_3D, + TEXTURE_CUBE, + TEXTURE_RECT, + TEXTURE_1D_ARRAY, + TEXTURE_2D_ARRAY, + TEXTURE_CUBE_ARRAY, + MAX +}; + +enum class PipePrimitiveTypes : u32 { + POINTS = 0, + LINES, + LINE_LOOP, + LINE_STRIP, + TRIANGLES, + TRIANGLE_STRIP, + TRIANGLE_FAN, + QUADS, + QUAD_STRIP, + POLYGON, + LINES_ADJACENCY, + LINE_STRIP_ADJACENCY, + TRIANGLES_ADJACENCY, + TRIANGLE_STRIP_ADJACENCY, + PATCHES, + MAX +}; + +} + +namespace Gallium { + +enum class PipeTextureTarget : u32 { + BUFFER, + TEXTURE_1D, + TEXTURE_2D, + TEXTURE_3D, + TEXTURE_CUBE, + TEXTURE_RECT, + TEXTURE_1D_ARRAY, + TEXTURE_2D_ARRAY, + TEXTURE_CUBE_ARRAY, + MAX_TEXTURE_TYPES, +}; + +enum class ShaderType : u32 { + SHADER_VERTEX = 0, + SHADER_FRAGMENT, + SHADER_GEOMETRY, + SHADER_TESS_CTRL, + SHADER_TESS_EVAL, + SHADER_COMPUTE, + SHADER_TYPES +}; + +} diff --git a/Userland/Demos/VirGLDemo/Widget.cpp b/Userland/Demos/VirGLDemo/Widget.cpp new file mode 100644 index 0000000000..41f640961f --- /dev/null +++ b/Userland/Demos/VirGLDemo/Widget.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022, Sahan Fernando <sahan.h.fernando@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibGUI/Label.h> +#include <LibGUI/Painter.h> + +#include "Widget.h" + +Demo::Demo() +{ + m_bitmap = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRx8888, { DRAWTARGET_WIDTH, DRAWTARGET_HEIGHT }).release_value_but_fixme_should_propagate_errors(); + m_bitmap->fill(Gfx::Color::Black); + + m_cycles = 0; + + stop_timer(); + start_timer(16); +} + +Demo::~Demo() { } + +void Demo::paint_event(GUI::PaintEvent& event) +{ + GUI::Painter painter(*this); + painter.add_clip_rect(event.rect()); + painter.draw_scaled_bitmap(rect(), *m_bitmap, m_bitmap->rect()); +} + +void Demo::timer_event(Core::TimerEvent&) +{ + m_cycles += 1; + update_frame(m_bitmap, m_cycles); + update(); +} diff --git a/Userland/Demos/VirGLDemo/Widget.h b/Userland/Demos/VirGLDemo/Widget.h new file mode 100644 index 0000000000..bf51ce7e2c --- /dev/null +++ b/Userland/Demos/VirGLDemo/Widget.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022, Sahan Fernando <sahan.h.fernando@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibGUI/Widget.h> + +void update_frame(RefPtr<Gfx::Bitmap>, unsigned num_cycles); + +constexpr size_t DRAWTARGET_WIDTH = 500; +constexpr size_t DRAWTARGET_HEIGHT = 500; + +class Demo final : public GUI::Widget { + C_OBJECT(Demo) +public: + virtual ~Demo() override; + bool show_window_frame() const { return m_show_window_frame; } + + Function<void(GUI::ContextMenuEvent&)> on_context_menu_request; + +protected: + virtual void context_menu_event(GUI::ContextMenuEvent& event) override + { + if (on_context_menu_request) + on_context_menu_request(event); + } + +private: + Demo(); + + RefPtr<Gfx::Bitmap> m_bitmap; + + virtual void paint_event(GUI::PaintEvent&) override; + virtual void timer_event(Core::TimerEvent&) override; + + int m_cycles; + bool m_show_window_frame { true }; +}; |