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:
parent
966989afe8
commit
fd6a536c60
7 changed files with 503 additions and 8 deletions
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
97
Kernel/Graphics/VirtIOGPU/GPU3DDevice.cpp
Normal file
97
Kernel/Graphics/VirtIOGPU/GPU3DDevice.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
111
Kernel/Graphics/VirtIOGPU/GPU3DDevice.h
Normal file
111
Kernel/Graphics/VirtIOGPU/GPU3DDevice.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue