1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 01:27:43 +00:00

Kernel: Implement basic VirGL device

This commit flips VirtIOGPU back to using a Mutex for its operation
lock (instead of a spinlock). This is necessary for avoiding a few
system hangs when queuing actions on the driver from multiple
processes, which becomes much more of an issue when using VirGL from
multiple userspace process.

This does result in a few code paths where we inevitably have to grab
a mutex from inside a spinlock, the only way to fix both issues is to
move to issuing asynchronous virtio gpu commands.
This commit is contained in:
Sahan Fernando 2022-02-13 16:45:30 +11:00 committed by Ali Mohammad Pur
parent 966989afe8
commit fd6a536c60
7 changed files with 503 additions and 8 deletions

View file

@ -86,6 +86,7 @@ set(KERNEL_SOURCES
Graphics/VGA/PCIAdapter.cpp Graphics/VGA/PCIAdapter.cpp
Graphics/VirtIOGPU/FramebufferDevice.cpp Graphics/VirtIOGPU/FramebufferDevice.cpp
Graphics/VirtIOGPU/Console.cpp Graphics/VirtIOGPU/Console.cpp
Graphics/VirtIOGPU/GPU3DDevice.cpp
Graphics/VirtIOGPU/GraphicsAdapter.cpp Graphics/VirtIOGPU/GraphicsAdapter.cpp
Graphics/GenericFramebufferDevice.cpp Graphics/GenericFramebufferDevice.cpp
SanCov.cpp SanCov.cpp

View file

@ -108,7 +108,7 @@ ErrorOr<void> FramebufferDevice::flush_head_buffer(size_t)
} }
ErrorOr<void> FramebufferDevice::flush_rectangle(size_t buffer_index, FBRect const& rect) ErrorOr<void> FramebufferDevice::flush_rectangle(size_t buffer_index, FBRect const& rect)
{ {
SpinlockLocker locker(adapter()->operation_lock()); MutexLocker locker(adapter()->operation_lock());
Protocol::Rect dirty_rect { Protocol::Rect dirty_rect {
.x = rect.x, .x = rect.x,
.y = rect.y, .y = rect.y,
@ -165,7 +165,7 @@ FramebufferDevice::~FramebufferDevice()
ErrorOr<void> FramebufferDevice::create_framebuffer() ErrorOr<void> FramebufferDevice::create_framebuffer()
{ {
SpinlockLocker locker(adapter()->operation_lock()); MutexLocker locker(adapter()->operation_lock());
// First delete any existing framebuffers to free the memory first // First delete any existing framebuffers to free the memory first
m_framebuffer = nullptr; m_framebuffer = nullptr;
m_framebuffer_sink_vmobject = nullptr; m_framebuffer_sink_vmobject = nullptr;
@ -255,7 +255,7 @@ void FramebufferDevice::flush_displayed_image(Protocol::Rect const& dirty_rect,
void FramebufferDevice::set_buffer(int buffer_index) void FramebufferDevice::set_buffer(int buffer_index)
{ {
auto& buffer = buffer_index == 0 ? m_main_buffer : m_back_buffer; auto& buffer = buffer_index == 0 ? m_main_buffer : m_back_buffer;
SpinlockLocker locker(adapter()->operation_lock()); MutexLocker locker(adapter()->operation_lock());
if (&buffer == m_current_buffer) if (&buffer == m_current_buffer)
return; return;
m_current_buffer = &buffer; m_current_buffer = &buffer;

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/API/VirGL.h>
#include <Kernel/Graphics/GraphicsManagement.h>
#include <Kernel/Graphics/VirtIOGPU/Console.h>
#include <Kernel/Graphics/VirtIOGPU/GPU3DDevice.h>
#include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h>
#include <Kernel/Graphics/VirtIOGPU/Protocol.h>
#include <Kernel/Random.h>
#include <LibC/sys/ioctl_numbers.h>
namespace Kernel::Graphics::VirtIOGPU {
GPU3DDevice::GPU3DDevice(GraphicsAdapter& graphics_adapter)
: CharacterDevice(28, 0)
, m_graphics_adapter(graphics_adapter)
{
m_kernel_context_id = m_graphics_adapter.create_context();
// Setup memory transfer region
auto region_result = MM.allocate_kernel_region(
NUM_TRANSFER_REGION_PAGES * PAGE_SIZE,
"VIRGL3D upload buffer",
Memory::Region::Access::ReadWrite,
AllocationStrategy::AllocateNow);
VERIFY(!region_result.is_error());
m_transfer_buffer_region = region_result.release_value();
}
ErrorOr<void> GPU3DDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
{
// TODO: We really should have ioctls for destroying resources as well
switch (request) {
case VIRGL_IOCTL_TRANSFER_DATA: {
auto user_transfer_descriptor = static_ptr_cast<VirGLTransferDescriptor const*>(arg);
auto transfer_descriptor = TRY(copy_typed_from_user(user_transfer_descriptor));
if (transfer_descriptor.direction == VIRGL_DATA_DIR_GUEST_TO_HOST) {
if (transfer_descriptor.offset_in_region + transfer_descriptor.num_bytes > NUM_TRANSFER_REGION_PAGES * PAGE_SIZE) {
return EOVERFLOW;
}
auto target = m_transfer_buffer_region->vaddr().offset(transfer_descriptor.offset_in_region).as_ptr();
return copy_from_user(target, transfer_descriptor.data, transfer_descriptor.num_bytes);
} else if (transfer_descriptor.direction == VIRGL_DATA_DIR_HOST_TO_GUEST) {
if (transfer_descriptor.offset_in_region + transfer_descriptor.num_bytes > NUM_TRANSFER_REGION_PAGES * PAGE_SIZE) {
return EOVERFLOW;
}
auto source = m_transfer_buffer_region->vaddr().offset(transfer_descriptor.offset_in_region).as_ptr();
return copy_to_user(transfer_descriptor.data, source, transfer_descriptor.num_bytes);
} else {
return EINVAL;
}
}
case VIRGL_IOCTL_SUBMIT_CMD: {
MutexLocker locker(m_graphics_adapter.operation_lock());
auto user_command_buffer = static_ptr_cast<VirGLCommandBuffer const*>(arg);
auto command_buffer = TRY(copy_typed_from_user(user_command_buffer));
m_graphics_adapter.submit_command_buffer(m_kernel_context_id, [&](Bytes buffer) {
auto num_bytes = command_buffer.num_elems * sizeof(u32);
VERIFY(num_bytes <= buffer.size());
MUST(copy_from_user(buffer.data(), command_buffer.data, num_bytes));
return num_bytes;
});
return {};
}
case VIRGL_IOCTL_CREATE_RESOURCE: {
auto user_spec = static_ptr_cast<VirGL3DResourceSpec const*>(arg);
VirGL3DResourceSpec spec = TRY(copy_typed_from_user(user_spec));
Protocol::Resource3DSpecification const resource_spec = {
.target = static_cast<Protocol::Gallium::PipeTextureTarget>(spec.target),
.format = spec.format,
.bind = spec.bind,
.width = spec.width,
.height = spec.height,
.depth = spec.depth,
.array_size = spec.array_size,
.last_level = spec.last_level,
.nr_samples = spec.nr_samples,
.flags = spec.flags
};
MutexLocker locker(m_graphics_adapter.operation_lock());
auto resource_id = m_graphics_adapter.create_3d_resource(resource_spec).value();
m_graphics_adapter.attach_resource_to_context(resource_id, m_kernel_context_id);
m_graphics_adapter.ensure_backing_storage(resource_id, *m_transfer_buffer_region, 0, NUM_TRANSFER_REGION_PAGES * PAGE_SIZE);
spec.created_resource_id = resource_id;
// FIXME: We should delete the resource we just created if we fail to copy the resource id out
return copy_to_user(static_ptr_cast<VirGL3DResourceSpec*>(arg), &spec);
}
}
return EINVAL;
}
}

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/DistinctNumeric.h>
#include <Kernel/Devices/CharacterDevice.h>
#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h>
#include <Kernel/Graphics/VirtIOGPU/Protocol.h>
namespace Kernel::Graphics::VirtIOGPU {
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;
};
class GPU3DDevice : public CharacterDevice {
public:
GPU3DDevice() = delete;
explicit GPU3DDevice(GraphicsAdapter& graphics_adapter);
virtual bool can_read(const OpenFileDescription&, u64) const override { return true; }
virtual bool can_write(const OpenFileDescription&, u64) const override { return true; }
virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override { return ENOTSUP; }
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, const UserOrKernelBuffer&, size_t) override { return ENOTSUP; }
virtual StringView class_name() const override { return "virgl3d"; }
virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override;
private:
Kernel::Graphics::VirtIOGPU::GraphicsAdapter& m_graphics_adapter;
// Context used for kernel operations (e.g. flushing resources to scanout)
ContextID m_kernel_context_id;
// Memory management for backing buffers
OwnPtr<Memory::Region> m_transfer_buffer_region;
constexpr static size_t NUM_TRANSFER_REGION_PAGES = 256;
};
}

View file

@ -7,10 +7,12 @@
#include <AK/BinaryBufferWriter.h> #include <AK/BinaryBufferWriter.h>
#include <Kernel/Bus/PCI/API.h> #include <Kernel/Bus/PCI/API.h>
#include <Kernel/Bus/PCI/IDs.h> #include <Kernel/Bus/PCI/IDs.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Graphics/Console/GenericFramebufferConsole.h> #include <Kernel/Graphics/Console/GenericFramebufferConsole.h>
#include <Kernel/Graphics/GraphicsManagement.h> #include <Kernel/Graphics/GraphicsManagement.h>
#include <Kernel/Graphics/VirtIOGPU/Console.h> #include <Kernel/Graphics/VirtIOGPU/Console.h>
#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> #include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h>
#include <Kernel/Graphics/VirtIOGPU/GPU3DDevice.h>
#include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h> #include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h>
namespace Kernel::Graphics::VirtIOGPU { namespace Kernel::Graphics::VirtIOGPU {
@ -74,8 +76,11 @@ void GraphicsAdapter::initialize()
m_device_configuration = config; m_device_configuration = config;
bool success = negotiate_features([&](u64 supported_features) { bool success = negotiate_features([&](u64 supported_features) {
u64 negotiated = 0; u64 negotiated = 0;
if (is_feature_set(supported_features, VIRTIO_GPU_F_VIRGL)) if (is_feature_set(supported_features, VIRTIO_GPU_F_VIRGL)) {
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: VIRGL is not yet supported!"); dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: VirGL is available, enabling");
negotiated |= VIRTIO_GPU_F_VIRGL;
m_has_virgl_support = true;
}
if (is_feature_set(supported_features, VIRTIO_GPU_F_EDID)) if (is_feature_set(supported_features, VIRTIO_GPU_F_EDID))
negotiated |= VIRTIO_GPU_F_EDID; negotiated |= VIRTIO_GPU_F_EDID;
return negotiated; return negotiated;
@ -89,7 +94,8 @@ void GraphicsAdapter::initialize()
} }
VERIFY(success); VERIFY(success);
finish_init(); finish_init();
SpinlockLocker locker(m_operation_lock); initialize_3d_device();
MutexLocker locker(m_operation_lock);
// Get display information using VIRTIO_GPU_CMD_GET_DISPLAY_INFO // Get display information using VIRTIO_GPU_CMD_GET_DISPLAY_INFO
query_display_information(); query_display_information();
query_display_edid({}); query_display_edid({});
@ -265,6 +271,28 @@ ResourceID GraphicsAdapter::create_2d_resource(Protocol::Rect rect)
return resource_id; return resource_id;
} }
ResourceID GraphicsAdapter::create_3d_resource(Protocol::Resource3DSpecification const& resource_3d_specification)
{
VERIFY(m_operation_lock.is_locked());
auto writer = create_scratchspace_writer();
auto& request = writer.append_structure<Protocol::ResourceCreate3D>();
auto& response = writer.append_structure<Protocol::ControlHeader>();
populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_CREATE_3D, VIRTIO_GPU_FLAG_FENCE);
auto resource_id = allocate_resource_id();
request.resource_id = resource_id.value();
// TODO: Abstract this out a bit more
u32* start_of_copied_fields = &request.target;
memcpy(start_of_copied_fields, &resource_3d_specification, sizeof(Protocol::Resource3DSpecification));
synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response));
VERIFY(response.type == static_cast<u32>(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA));
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Allocated 3d resource with id {}", resource_id.value());
return resource_id;
}
void GraphicsAdapter::ensure_backing_storage(ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length) void GraphicsAdapter::ensure_backing_storage(ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length)
{ {
VERIFY(m_operation_lock.is_locked()); VERIFY(m_operation_lock.is_locked());
@ -395,7 +423,7 @@ void GraphicsAdapter::populate_virtio_gpu_request_header(Protocol::ControlHeader
void GraphicsAdapter::flush_dirty_rectangle(ScanoutID scanout_id, ResourceID resource_id, Protocol::Rect const& dirty_rect) void GraphicsAdapter::flush_dirty_rectangle(ScanoutID scanout_id, ResourceID resource_id, Protocol::Rect const& dirty_rect)
{ {
SpinlockLocker locker(m_operation_lock); MutexLocker locker(m_operation_lock);
transfer_framebuffer_data_to_host(scanout_id, resource_id, dirty_rect); transfer_framebuffer_data_to_host(scanout_id, resource_id, dirty_rect);
flush_displayed_image(resource_id, dirty_rect); flush_displayed_image(resource_id, dirty_rect);
} }
@ -407,6 +435,14 @@ ResourceID GraphicsAdapter::allocate_resource_id()
return m_resource_id_counter; return m_resource_id_counter;
} }
ContextID GraphicsAdapter::allocate_context_id()
{
// FIXME: This should really be tracked using a bitmap, instead of an atomic counter
VERIFY(m_operation_lock.is_locked());
m_context_id_counter = m_context_id_counter.value() + 1;
return m_context_id_counter;
}
void GraphicsAdapter::delete_resource(ResourceID resource_id) void GraphicsAdapter::delete_resource(ResourceID resource_id)
{ {
VERIFY(m_operation_lock.is_locked()); VERIFY(m_operation_lock.is_locked());
@ -422,4 +458,79 @@ void GraphicsAdapter::delete_resource(ResourceID resource_id)
VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA));
} }
void GraphicsAdapter::initialize_3d_device()
{
if (m_has_virgl_support) {
MutexLocker locker(m_operation_lock);
m_3d_device = MUST(DeviceManagement::try_create_device<VirtIOGPU::GPU3DDevice>(*this));
}
}
ContextID GraphicsAdapter::create_context()
{
VERIFY(m_operation_lock.is_locked());
auto ctx_id = allocate_context_id();
auto writer = create_scratchspace_writer();
auto& request = writer.append_structure<Protocol::ContextCreate>();
auto& response = writer.append_structure<Protocol::ControlHeader>();
constexpr char const* region_name = "Serenity VirGL3D Context";
populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_CTX_CREATE, VIRTIO_GPU_FLAG_FENCE);
request.header.context_id = ctx_id.value();
request.name_length = strlen(region_name);
memset(request.debug_name.data(), 0, 64);
VERIFY(request.name_length <= 64);
memcpy(request.debug_name.data(), region_name, request.name_length);
synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response));
VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA));
return ctx_id;
}
void GraphicsAdapter::submit_command_buffer(ContextID context_id, Function<size_t(Bytes)> buffer_writer)
{
VERIFY(m_operation_lock.is_locked());
auto writer = create_scratchspace_writer();
auto& request = writer.append_structure<Protocol::CommandSubmit>();
populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_SUBMIT_3D, VIRTIO_GPU_FLAG_FENCE);
request.header.context_id = context_id.value();
auto max_command_buffer_length = m_scratch_space->size() - sizeof(request) - sizeof(Protocol::ControlHeader);
// Truncate to nearest multiple of alignment, to ensure padding loop doesn't exhaust allocated space
max_command_buffer_length -= max_command_buffer_length % alignof(Protocol::ControlHeader);
Bytes command_buffer_buffer(m_scratch_space->vaddr().offset(sizeof(request)).as_ptr(), max_command_buffer_length);
request.size = buffer_writer(command_buffer_buffer);
writer.skip_bytes(request.size);
// The alignment of a ControlHeader may be a few words larger than the length of a command buffer, so
// we pad with no-ops until we reach the correct alignment
while (writer.current_offset() % alignof(Protocol::ControlHeader) != 0) {
VERIFY((writer.current_offset() % alignof(Protocol::ControlHeader)) % sizeof(u32) == 0);
writer.append_structure<u32>() = to_underlying(VirGLCommand::NOP);
request.size += 4;
}
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Sending command buffer of length {}", request.size);
auto& response = writer.append_structure<Protocol::ControlHeader>();
synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request) + request.size, sizeof(response));
VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA));
}
void GraphicsAdapter::attach_resource_to_context(ResourceID resource_id, ContextID context_id)
{
VERIFY(m_operation_lock.is_locked());
auto writer = create_scratchspace_writer();
auto& request = writer.append_structure<Protocol::ContextAttachResource>();
auto& response = writer.append_structure<Protocol::ControlHeader>();
populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_FLAG_FENCE);
request.header.context_id = context_id.value();
request.resource_id = resource_id.value();
synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response));
VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA));
}
} }

View file

@ -19,6 +19,8 @@
namespace Kernel::Graphics::VirtIOGPU { namespace Kernel::Graphics::VirtIOGPU {
class GPU3DDevice;
#define VIRTIO_GPU_F_VIRGL (1 << 0) #define VIRTIO_GPU_F_VIRGL (1 << 0)
#define VIRTIO_GPU_F_EDID (1 << 1) #define VIRTIO_GPU_F_EDID (1 << 1)
@ -47,6 +49,7 @@ public:
virtual bool vga_compatible() const override { return false; } virtual bool vga_compatible() const override { return false; }
virtual void initialize() override; virtual void initialize() override;
void initialize_3d_device();
ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override; ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override;
@ -109,8 +112,15 @@ private:
u32 get_pending_events(); u32 get_pending_events();
void clear_pending_events(u32 event_bitmask); void clear_pending_events(u32 event_bitmask);
// 3D Command stuff
ContextID create_context();
void attach_resource_to_context(ResourceID resource_id, ContextID context_id);
void submit_command_buffer(ContextID, Function<size_t(Bytes)> buffer_writer);
Protocol::TextureFormat get_framebuffer_format() const { return Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; }
auto& operation_lock() { return m_operation_lock; } auto& operation_lock() { return m_operation_lock; }
ResourceID allocate_resource_id(); ResourceID allocate_resource_id();
ContextID allocate_context_id();
PhysicalAddress start_of_scratch_space() const { return m_scratch_space->physical_page(0)->paddr(); } PhysicalAddress start_of_scratch_space() const { return m_scratch_space->physical_page(0)->paddr(); }
AK::BinaryBufferWriter create_scratchspace_writer() AK::BinaryBufferWriter create_scratchspace_writer()
@ -123,6 +133,7 @@ private:
void query_display_information(); void query_display_information();
void query_display_edid(Optional<ScanoutID>); void query_display_edid(Optional<ScanoutID>);
ResourceID create_2d_resource(Protocol::Rect rect); ResourceID create_2d_resource(Protocol::Rect rect);
ResourceID create_3d_resource(Protocol::Resource3DSpecification const& resource_3d_specification);
void delete_resource(ResourceID resource_id); void delete_resource(ResourceID resource_id);
void ensure_backing_storage(ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length); void ensure_backing_storage(ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length);
void detach_backing_storage(ResourceID resource_id); void detach_backing_storage(ResourceID resource_id);
@ -138,10 +149,15 @@ private:
VirtIO::Configuration const* m_device_configuration { nullptr }; VirtIO::Configuration const* m_device_configuration { nullptr };
ResourceID m_resource_id_counter { 0 }; ResourceID m_resource_id_counter { 0 };
ContextID m_context_id_counter { 0 };
RefPtr<GPU3DDevice> m_3d_device;
bool m_has_virgl_support { false };
// Synchronous commands // Synchronous commands
WaitQueue m_outstanding_request; WaitQueue m_outstanding_request;
Spinlock m_operation_lock; Mutex m_operation_lock;
OwnPtr<Memory::Region> m_scratch_space; OwnPtr<Memory::Region> m_scratch_space;
friend class Kernel::Graphics::VirtIOGPU::GPU3DDevice;
}; };
} }

View file

@ -11,10 +11,28 @@
#define VIRTIO_GPU_MAX_SCANOUTS 16 #define VIRTIO_GPU_MAX_SCANOUTS 16
namespace Kernel::Graphics::VirtIOGPU { namespace Kernel::Graphics::VirtIOGPU {
TYPEDEF_DISTINCT_ORDERED_ID(u32, ContextID);
TYPEDEF_DISTINCT_ORDERED_ID(u32, ResourceID); TYPEDEF_DISTINCT_ORDERED_ID(u32, ResourceID);
TYPEDEF_DISTINCT_ORDERED_ID(u32, ScanoutID); TYPEDEF_DISTINCT_ORDERED_ID(u32, ScanoutID);
}; };
#define VREND_MAX_CTX 64
#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)
namespace Kernel::Graphics::VirtIOGPU::Protocol { namespace Kernel::Graphics::VirtIOGPU::Protocol {
// Specification equivalent: enum virtio_gpu_ctrl_type // Specification equivalent: enum virtio_gpu_ctrl_type
@ -32,6 +50,18 @@ enum class CommandType : u32 {
VIRTIO_GPU_CMD_GET_CAPSET, VIRTIO_GPU_CMD_GET_CAPSET,
VIRTIO_GPU_CMD_GET_EDID, VIRTIO_GPU_CMD_GET_EDID,
/* 3d commands */
VIRTIO_GPU_CMD_CTX_CREATE = 0x0200,
VIRTIO_GPU_CMD_CTX_DESTROY,
VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE,
VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE,
VIRTIO_GPU_CMD_RESOURCE_CREATE_3D,
VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D,
VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D,
VIRTIO_GPU_CMD_SUBMIT_3D,
VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB,
VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB,
/* cursor commands */ /* cursor commands */
VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300, VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR,
@ -52,6 +82,54 @@ enum class CommandType : u32 {
VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
}; };
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
};
// Specification equivalent: struct virtio_gpu_ctrl_hdr // Specification equivalent: struct virtio_gpu_ctrl_hdr
struct ControlHeader { struct ControlHeader {
u32 type; u32 type;
@ -103,6 +181,23 @@ struct ResourceCreate2D {
u32 height; u32 height;
}; };
// Specification equivalent: struct virtio_gpu_resource_create_3d
struct ResourceCreate3D {
ControlHeader header;
u32 resource_id;
u32 target;
u32 format;
u32 bind;
u32 width;
u32 height;
u32 depth;
u32 array_size;
u32 last_level;
u32 nr_samples;
u32 flags;
u32 padding;
};
// Specification equivalent: struct virtio_gpu_resource_unref // Specification equivalent: struct virtio_gpu_resource_unref
struct ResourceUnref { struct ResourceUnref {
ControlHeader header; ControlHeader header;
@ -171,4 +266,68 @@ struct GetEDIDResponse {
u8 edid[1024]; u8 edid[1024];
}; };
// No equivalent in specification
struct ContextCreate {
ControlHeader header;
u32 name_length;
u32 padding;
AK::Array<char, 64> debug_name;
};
static_assert(sizeof(ContextCreate::debug_name) == 64);
// No equivalent in specification
struct ContextAttachResource {
ControlHeader header;
u32 resource_id;
u32 padding;
};
// No equivalent in specification
struct CommandSubmit {
ControlHeader header;
u32 size;
u32 padding;
};
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
};
}
struct Resource3DSpecification {
Gallium::PipeTextureTarget target;
u32 format;
u32 bind;
u32 width;
u32 height;
u32 depth;
u32 array_size;
u32 last_level;
u32 nr_samples;
u32 flags;
};
} }