From 1ec791fcd0f42b5e47ebded8b37ce1ed3a7f3eac Mon Sep 17 00:00:00 2001 From: Stephan Unverwerth Date: Thu, 22 Dec 2022 15:11:32 +0100 Subject: LibVirtGPU: Adopt device initialization code from VirGLDemo --- Userland/Libraries/LibVirtGPU/Device.cpp | 167 ++++++++++++++++++++++++++++++- Userland/Libraries/LibVirtGPU/Device.h | 32 ++++++ 2 files changed, 198 insertions(+), 1 deletion(-) (limited to 'Userland/Libraries/LibVirtGPU') diff --git a/Userland/Libraries/LibVirtGPU/Device.cpp b/Userland/Libraries/LibVirtGPU/Device.cpp index 7709f1390d..37e58f6bd9 100644 --- a/Userland/Libraries/LibVirtGPU/Device.cpp +++ b/Userland/Libraries/LibVirtGPU/Device.cpp @@ -1,28 +1,170 @@ /* * Copyright (c) 2022, Stephan Unverwerth + * Copyright (c) 2022, Sahan Fernando + * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #include +#include +#include +#include #include #include #include +#include namespace VirtGPU { +static constexpr auto 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"sv; + +static constexpr auto 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"sv; + Device::Device(NonnullRefPtr gpu_file) : m_gpu_file { gpu_file } { } -ErrorOr> Device::create(Gfx::IntSize) +ErrorOr> Device::create(Gfx::IntSize min_size) { auto file = TRY(Core::File::open("/dev/gpu/render0", Core::OpenMode::ReadWrite)); auto device = make(file); + TRY(device->initialize_context(min_size)); return device; } +ErrorOr Device::initialize_context(Gfx::IntSize min_size) +{ + // Create a virgl context for this file descriptor + TRY(Core::System::ioctl(m_gpu_file->fd(), VIRGL_IOCTL_CREATE_CONTEXT)); + + // Create a VertexElements resource + VirGL3DResourceSpec vbo_spec { + .target = to_underlying(Gallium::PipeTextureTarget::BUFFER), // pipe_texture_target + .format = 0, // untyped buffer + .bind = to_underlying(Protocol::BindTarget::VIRGL_BIND_VERTEX_BUFFER), + .width = PAGE_SIZE * 256, + .height = 1, + .depth = 1, + .array_size = 1, + .last_level = 0, + .nr_samples = 0, + .flags = 0, + .created_resource_id = 0, + }; + m_vbo_resource_id = TRY(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 = to_underlying(Protocol::BindTarget::VIRGL_BIND_RENDER_TARGET), + .width = static_cast(min_size.width()), + .height = static_cast(min_size.height()), + .depth = 1, + .array_size = 1, + .last_level = 0, + .nr_samples = 0, + .flags = 0, + .created_resource_id = 0, + }; + m_drawtarget = TRY(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 = to_underlying(Protocol::BindTarget::VIRGL_BIND_RENDER_TARGET) | to_underlying(Protocol::BindTarget::VIRGL_BIND_DEPTH_STENCIL), + .width = static_cast(min_size.width()), + .height = static_cast(min_size.height()), + .depth = 1, + .array_size = 1, + .last_level = 0, + .nr_samples = 0, + .flags = 0, + .created_resource_id = 0, + }; + m_depthbuffer_surface = TRY(create_virgl_resource(depthbuffer_surface_spec)); + + // Initialize all required state + CommandBufferBuilder builder; + + // Create and set the blend, to control the color mask + m_blend_handle = allocate_handle(); + builder.append_create_blend(m_blend_handle); + builder.append_bind_blend(m_blend_handle); + + // Create drawtarget surface + m_drawtarget_surface_handle = allocate_handle(); + builder.append_create_surface(m_drawtarget, m_drawtarget_surface_handle, Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM); + + // Create depthbuffer surface + m_depthbuffer_surface_handle = allocate_handle(); + builder.append_create_surface(m_depthbuffer_surface, m_depthbuffer_surface_handle, Protocol::TextureFormat::VIRTIO_GPU_FORMAT_Z32_FLOAT); + + // Set some framebuffer state (attached handle, framebuffer size, etc) + builder.append_set_framebuffer_state(m_drawtarget_surface_handle, m_depthbuffer_surface_handle); + builder.append_set_framebuffer_state_no_attach(min_size); + + // Set the vertex buffer + builder.append_set_vertex_buffers(sizeof(VertexData), 0, m_vbo_resource_id); + + // Create and bind fragment shader + m_frag_shader_handle = allocate_handle(); + builder.append_create_shader(m_frag_shader_handle, Gallium::ShaderType::SHADER_FRAGMENT, frag_shader); + builder.append_bind_shader(m_frag_shader_handle, Gallium::ShaderType::SHADER_FRAGMENT); + + // Create and bind vertex shader + m_vert_shader_handle = allocate_handle(); + builder.append_create_shader(m_vert_shader_handle, Gallium::ShaderType::SHADER_VERTEX, vert_shader); + builder.append_bind_shader(m_vert_shader_handle, Gallium::ShaderType::SHADER_VERTEX); + + // Create a VertexElements object (used to specify layout of vertex data) + m_ve_handle = allocate_handle(); + Vector element_bindings { + { .offset = 12, .divisor = 0, .vertex_buffer_index = 0, .format = Gallium::PipeFormat::R32G32B32_FLOAT }, + { .offset = 0, .divisor = 0, .vertex_buffer_index = 0, .format = Gallium::PipeFormat::R32G32B32_FLOAT }, + }; + builder.append_create_vertex_elements(m_ve_handle, element_bindings); + builder.append_bind_vertex_elements(m_ve_handle); + + // Create a DepthStencilAlpha (DSA) object + m_dsa_handle = allocate_handle(); + builder.append_create_dsa(m_dsa_handle); + builder.append_bind_dsa(m_dsa_handle); + + // Create a Rasterizer object + m_rasterizer_handle = allocate_handle(); + builder.append_create_rasterizer(m_rasterizer_handle); + builder.append_bind_rasterizer(m_rasterizer_handle); + + // Set the Viewport + builder.append_viewport(min_size); + + // Upload buffer + TRY(upload_command_buffer(builder.build())); + + return {}; +} + GPU::DeviceInfo Device::info() const { return { @@ -185,6 +327,29 @@ void Device::bind_fragment_shader(RefPtr) dbgln("VirtGPU::Device::bind_fragment_shader(): unimplemented"); } +Protocol::ObjectHandle Device::allocate_handle() +{ + return { ++m_last_allocated_handle }; +} + +ErrorOr Device::upload_command_buffer(Vector const& command_buffer) +{ + VERIFY(command_buffer.size() <= NumericLimits::max()); + VirGLCommandBuffer command_buffer_descriptor { + .data = command_buffer.data(), + .num_elems = static_cast(command_buffer.size()), + }; + TRY(Core::System::ioctl(m_gpu_file->fd(), VIRGL_IOCTL_SUBMIT_CMD, &command_buffer_descriptor)); + + return {}; +} + +ErrorOr Device::create_virgl_resource(VirGL3DResourceSpec& spec) +{ + TRY(Core::System::ioctl(m_gpu_file->fd(), VIRGL_IOCTL_CREATE_RESOURCE, &spec)); + return Protocol::ResourceID { spec.created_resource_id }; +} + } extern "C" GPU::Device* serenity_gpu_create_device(Gfx::IntSize size) diff --git a/Userland/Libraries/LibVirtGPU/Device.h b/Userland/Libraries/LibVirtGPU/Device.h index 7084f6c126..ec2e207d51 100644 --- a/Userland/Libraries/LibVirtGPU/Device.h +++ b/Userland/Libraries/LibVirtGPU/Device.h @@ -8,8 +8,11 @@ #include #include +#include +#include #include #include +#include namespace VirtGPU { @@ -19,6 +22,9 @@ public: static ErrorOr> create(Gfx::IntSize min_size); + // FIXME: Once the kernel driver supports destroying contexts we need to add this functionality here + ErrorOr initialize_context(Gfx::IntSize min_size); + virtual GPU::DeviceInfo info() const override; virtual void draw_primitives(GPU::PrimitiveType, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform, Vector& vertices) override; @@ -55,7 +61,33 @@ public: virtual void bind_fragment_shader(RefPtr) override; private: + Protocol::ObjectHandle allocate_handle(); + ErrorOr create_virgl_resource(VirGL3DResourceSpec&); + ErrorOr upload_command_buffer(Vector const&); + NonnullRefPtr m_gpu_file; + + Protocol::ResourceID m_vbo_resource_id { 0 }; + Protocol::ResourceID m_drawtarget { 0 }; + Protocol::ResourceID m_depthbuffer_surface { 0 }; + Protocol::ObjectHandle m_blend_handle { 0 }; + Protocol::ObjectHandle m_drawtarget_surface_handle { 0 }; + Protocol::ObjectHandle m_depthbuffer_surface_handle { 0 }; + Protocol::ObjectHandle m_ve_handle { 0 }; + Protocol::ObjectHandle m_frag_shader_handle { 0 }; + Protocol::ObjectHandle m_vert_shader_handle { 0 }; + Protocol::ObjectHandle m_rasterizer_handle { 0 }; + Protocol::ObjectHandle m_dsa_handle { 0 }; + u32 m_last_allocated_handle { 0 }; + + struct VertexData { + float r; + float g; + float b; + float x; + float y; + float z; + }; }; } -- cgit v1.2.3