diff --git a/Userland/Libraries/LibVirtGPU/CMakeLists.txt b/Userland/Libraries/LibVirtGPU/CMakeLists.txt index 6d709b8f51e..0831a5b2f9b 100644 --- a/Userland/Libraries/LibVirtGPU/CMakeLists.txt +++ b/Userland/Libraries/LibVirtGPU/CMakeLists.txt @@ -1,4 +1,5 @@ set(SOURCES + CommandBufferBuilder.cpp Device.cpp Image.cpp Shader.cpp diff --git a/Userland/Libraries/LibVirtGPU/CommandBufferBuilder.cpp b/Userland/Libraries/LibVirtGPU/CommandBufferBuilder.cpp new file mode 100644 index 00000000000..536000efecc --- /dev/null +++ b/Userland/Libraries/LibVirtGPU/CommandBufferBuilder.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2022, Sahan Fernando + * Copyright (c) 2022, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +#include "CommandBufferBuilder.h" +#include "VirGLProtocol.h" + +constexpr int DRAWTARGET_WIDTH = 500; +constexpr int DRAWTARGET_HEIGHT = 500; + +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& 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(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(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& 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 + // FIXME: It is not clear what this magic 242 value does. + // According to https://gitlab.freedesktop.org/virgl/virglrenderer/-/blob/master/src/vrend_decode.c#L1398 it is unused + // But ccapitalK had to specifically set it to prevent rendering failures. + 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 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, StringView shader_data) +{ + size_t shader_len = shader_data.length() + 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/Libraries/LibVirtGPU/CommandBufferBuilder.h b/Userland/Libraries/LibVirtGPU/CommandBufferBuilder.h new file mode 100644 index 00000000000..34250d7f5b4 --- /dev/null +++ b/Userland/Libraries/LibVirtGPU/CommandBufferBuilder.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022, Sahan Fernando + * Copyright (c) 2022, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +#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 const& constant_buffer); + void append_create_shader(ObjectHandle handle, Gallium::ShaderType shader_type, StringView 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 const& build() { return m_buffer; } + +private: + Vector m_buffer; +}; diff --git a/Userland/Libraries/LibVirtGPU/VirGLProtocol.h b/Userland/Libraries/LibVirtGPU/VirGLProtocol.h new file mode 100644 index 00000000000..15d4afa5831 --- /dev/null +++ b/Userland/Libraries/LibVirtGPU/VirGLProtocol.h @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2022, Sahan Fernando + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, ObjectHandle); +AK_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 +}; + +}