mirror of
https://github.com/RGBCube/serenity
synced 2025-07-28 06:57:45 +00:00
Kernel: Move all Graphics-related code into Devices/GPU directory
Like the HID, Audio and Storage subsystem, the Graphics subsystem (which handles GPUs technically) exposes unix device files (typically in /dev). To ensure consistency across the repository, move all related files to a new directory under Kernel/Devices called "GPU". Also remove the redundant "GPU" word from the VirtIO driver directory, and the word "Graphics" from GraphicsManagement.{h,cpp} filenames.
This commit is contained in:
parent
31a7dabf02
commit
9ee098b119
69 changed files with 167 additions and 167 deletions
103
Kernel/Devices/GPU/VirtIO/Console.cpp
Normal file
103
Kernel/Devices/GPU/VirtIO/Console.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Devices/GPU/VirtIO/Console.h>
|
||||
#include <Kernel/TTY/ConsoleManagement.h>
|
||||
#include <Kernel/Tasks/WorkQueue.h>
|
||||
|
||||
namespace Kernel::Graphics::VirtIOGPU {
|
||||
|
||||
constexpr static AK::Duration refresh_interval = AK::Duration::from_milliseconds(16);
|
||||
|
||||
NonnullLockRefPtr<Console> Console::initialize(VirtIODisplayConnector& parent_display_connector)
|
||||
{
|
||||
auto current_resolution = parent_display_connector.current_mode_setting();
|
||||
return adopt_lock_ref(*new Console(parent_display_connector, current_resolution));
|
||||
}
|
||||
|
||||
Console::Console(VirtIODisplayConnector const& parent_display_connector, DisplayConnector::ModeSetting current_resolution)
|
||||
: GenericFramebufferConsole(current_resolution.horizontal_active, current_resolution.vertical_active, current_resolution.horizontal_stride)
|
||||
, m_parent_display_connector(parent_display_connector)
|
||||
{
|
||||
// NOTE: Clear the framebuffer, in case it's left with some garbage.
|
||||
memset(framebuffer_data(), 0, current_resolution.horizontal_stride * current_resolution.vertical_active);
|
||||
enqueue_refresh_timer();
|
||||
}
|
||||
|
||||
void Console::set_resolution(size_t width, size_t height, size_t pitch)
|
||||
{
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_pitch = pitch;
|
||||
|
||||
// Just to start cleanly, we clean the entire framebuffer
|
||||
memset(framebuffer_data(), 0, pitch * height);
|
||||
|
||||
ConsoleManagement::the().resolution_was_changed();
|
||||
}
|
||||
|
||||
void Console::set_cursor(size_t x, size_t y)
|
||||
{
|
||||
GenericFramebufferConsole::hide_cursor();
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
GenericFramebufferConsole::show_cursor();
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void Console::hide_cursor()
|
||||
{
|
||||
GenericFramebufferConsole::hide_cursor();
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void Console::show_cursor()
|
||||
{
|
||||
GenericFramebufferConsole::show_cursor();
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void Console::flush(size_t, size_t, size_t, size_t)
|
||||
{
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void Console::enqueue_refresh_timer()
|
||||
{
|
||||
auto refresh_timer = adopt_nonnull_ref_or_enomem(new (nothrow) Timer()).release_value_but_fixme_should_propagate_errors();
|
||||
refresh_timer->setup(CLOCK_MONOTONIC, refresh_interval, [this]() {
|
||||
if (m_enabled.load() && m_dirty) {
|
||||
MUST(g_io_work->try_queue([this]() {
|
||||
{
|
||||
MutexLocker locker(m_parent_display_connector->m_flushing_lock);
|
||||
MUST(m_parent_display_connector->flush_first_surface());
|
||||
}
|
||||
m_dirty = false;
|
||||
}));
|
||||
}
|
||||
enqueue_refresh_timer();
|
||||
});
|
||||
TimerQueue::the().add_timer(move(refresh_timer));
|
||||
}
|
||||
|
||||
void Console::enable()
|
||||
{
|
||||
// FIXME: Do we need some locking here to ensure the resolution doesn't change
|
||||
// while we enable the console?
|
||||
auto current_resolution = m_parent_display_connector->current_mode_setting();
|
||||
m_width = current_resolution.horizontal_active;
|
||||
m_height = current_resolution.vertical_active;
|
||||
m_pitch = current_resolution.horizontal_stride;
|
||||
GenericFramebufferConsole::enable();
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
u8* Console::framebuffer_data()
|
||||
{
|
||||
return m_parent_display_connector->framebuffer_data();
|
||||
}
|
||||
|
||||
}
|
37
Kernel/Devices/GPU/VirtIO/Console.h
Normal file
37
Kernel/Devices/GPU/VirtIO/Console.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Kernel/Devices/GPU/Console/GenericFramebufferConsole.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/DisplayConnector.h>
|
||||
#include <Kernel/Time/TimerQueue.h>
|
||||
|
||||
namespace Kernel::Graphics::VirtIOGPU {
|
||||
|
||||
class Console final : public GenericFramebufferConsole {
|
||||
public:
|
||||
static NonnullLockRefPtr<Console> initialize(VirtIODisplayConnector& parent_display_connector);
|
||||
|
||||
virtual void set_resolution(size_t width, size_t height, size_t pitch) override;
|
||||
virtual void flush(size_t x, size_t y, size_t width, size_t height) override;
|
||||
virtual void enable() override;
|
||||
|
||||
virtual void set_cursor(size_t x, size_t y) override;
|
||||
|
||||
private:
|
||||
void enqueue_refresh_timer();
|
||||
virtual u8* framebuffer_data() override;
|
||||
|
||||
virtual void hide_cursor() override;
|
||||
virtual void show_cursor() override;
|
||||
|
||||
Console(VirtIODisplayConnector const& parent_display_connector, DisplayConnector::ModeSetting current_resolution);
|
||||
NonnullLockRefPtr<VirtIODisplayConnector> m_parent_display_connector;
|
||||
bool m_dirty { false };
|
||||
};
|
||||
|
||||
}
|
201
Kernel/Devices/GPU/VirtIO/DisplayConnector.cpp
Normal file
201
Kernel/Devices/GPU/VirtIO/DisplayConnector.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/API/VirGL.h>
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Devices/GPU/Management.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/Console.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/DisplayConnector.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/GraphicsAdapter.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/Protocol.h>
|
||||
#include <Kernel/Security/Random.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
NonnullLockRefPtr<VirtIODisplayConnector> VirtIODisplayConnector::must_create(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id)
|
||||
{
|
||||
auto device_or_error = DeviceManagement::try_create_device<VirtIODisplayConnector>(graphics_adapter, scanout_id);
|
||||
VERIFY(!device_or_error.is_error());
|
||||
auto connector = device_or_error.release_value();
|
||||
return connector;
|
||||
}
|
||||
|
||||
static_assert((MAX_VIRTIOGPU_RESOLUTION_WIDTH * MAX_VIRTIOGPU_RESOLUTION_HEIGHT * sizeof(u32) * 2) % PAGE_SIZE == 0);
|
||||
|
||||
VirtIODisplayConnector::VirtIODisplayConnector(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id)
|
||||
: DisplayConnector((MAX_VIRTIOGPU_RESOLUTION_WIDTH * MAX_VIRTIOGPU_RESOLUTION_HEIGHT * sizeof(u32) * 2), false)
|
||||
, m_graphics_adapter(graphics_adapter)
|
||||
, m_scanout_id(scanout_id)
|
||||
{
|
||||
}
|
||||
|
||||
void VirtIODisplayConnector::initialize_console(Badge<VirtIOGraphicsAdapter>)
|
||||
{
|
||||
m_console = Kernel::Graphics::VirtIOGPU::Console::initialize(*this);
|
||||
GraphicsManagement::the().set_console(*m_console);
|
||||
}
|
||||
|
||||
void VirtIODisplayConnector::set_safe_mode_setting_after_initialization(Badge<VirtIOGraphicsAdapter>)
|
||||
{
|
||||
MUST(set_safe_mode_setting());
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIODisplayConnector::set_mode_setting(ModeSetting const& mode_setting)
|
||||
{
|
||||
SpinlockLocker locker(m_modeset_lock);
|
||||
if (mode_setting.horizontal_active > MAX_VIRTIOGPU_RESOLUTION_WIDTH || mode_setting.vertical_active > MAX_VIRTIOGPU_RESOLUTION_HEIGHT)
|
||||
return Error::from_errno(ENOTSUP);
|
||||
|
||||
auto& info = m_display_info;
|
||||
info.rect = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = (u32)mode_setting.horizontal_active,
|
||||
.height = (u32)mode_setting.vertical_active,
|
||||
};
|
||||
|
||||
TRY(m_graphics_adapter->mode_set_resolution({}, *this, mode_setting.horizontal_active, mode_setting.vertical_active));
|
||||
|
||||
if (m_console)
|
||||
m_console->set_resolution(info.rect.width, info.rect.height, info.rect.width * sizeof(u32));
|
||||
DisplayConnector::ModeSetting mode_set {
|
||||
.horizontal_stride = info.rect.width * sizeof(u32),
|
||||
.pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware
|
||||
.horizontal_active = info.rect.width,
|
||||
.horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware
|
||||
.horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware
|
||||
.horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware
|
||||
.vertical_active = info.rect.height,
|
||||
.vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware
|
||||
.vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware
|
||||
.vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware
|
||||
.horizontal_offset = 0,
|
||||
.vertical_offset = 0,
|
||||
};
|
||||
m_current_mode_setting = mode_set;
|
||||
|
||||
m_display_info.enabled = 1;
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> VirtIODisplayConnector::set_safe_mode_setting()
|
||||
{
|
||||
DisplayConnector::ModeSetting safe_mode_setting {
|
||||
.horizontal_stride = 1024 * sizeof(u32),
|
||||
.pixel_clock_in_khz = 0, // Note: There's no pixel clock in paravirtualized hardware
|
||||
.horizontal_active = 1024,
|
||||
.horizontal_front_porch_pixels = 0, // Note: There's no horizontal_front_porch_pixels in paravirtualized hardware
|
||||
.horizontal_sync_time_pixels = 0, // Note: There's no horizontal_sync_time_pixels in paravirtualized hardware
|
||||
.horizontal_blank_pixels = 0, // Note: There's no horizontal_blank_pixels in paravirtualized hardware
|
||||
.vertical_active = 768,
|
||||
.vertical_front_porch_lines = 0, // Note: There's no vertical_front_porch_lines in paravirtualized hardware
|
||||
.vertical_sync_time_lines = 0, // Note: There's no vertical_sync_time_lines in paravirtualized hardware
|
||||
.vertical_blank_lines = 0, // Note: There's no vertical_blank_lines in paravirtualized hardware
|
||||
.horizontal_offset = 0,
|
||||
.vertical_offset = 0,
|
||||
};
|
||||
return set_mode_setting(safe_mode_setting);
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIODisplayConnector::set_y_offset(size_t)
|
||||
{
|
||||
// NOTE (FIXME?): We don't do double buffering because when using double buffering,
|
||||
// perfomance visually looks terrible (everything look sluggish) compared to not using it,
|
||||
// so until we figure out why (and we might not figure this and double buffering is simply not needed)
|
||||
// this happens, we simply don't support it.
|
||||
return Error::from_errno(ENOTSUP);
|
||||
}
|
||||
ErrorOr<void> VirtIODisplayConnector::unblank()
|
||||
{
|
||||
return Error::from_errno(ENOTIMPL);
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIODisplayConnector::flush_rectangle(size_t buffer_index, FBRect const& rect)
|
||||
{
|
||||
VERIFY(m_flushing_lock.is_locked());
|
||||
if (!is_valid_buffer_index(buffer_index))
|
||||
return Error::from_errno(EINVAL);
|
||||
SpinlockLocker locker(m_graphics_adapter->operation_lock());
|
||||
Graphics::VirtIOGPU::Protocol::Rect dirty_rect {
|
||||
.x = rect.x,
|
||||
.y = rect.y,
|
||||
.width = rect.width,
|
||||
.height = rect.height
|
||||
};
|
||||
|
||||
TRY(m_graphics_adapter->transfer_framebuffer_data_to_host({}, *this, dirty_rect, true));
|
||||
// Flushing directly to screen
|
||||
TRY(flush_displayed_image(dirty_rect, true));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIODisplayConnector::flush_first_surface()
|
||||
{
|
||||
VERIFY(m_flushing_lock.is_locked());
|
||||
SpinlockLocker locker(m_graphics_adapter->operation_lock());
|
||||
Graphics::VirtIOGPU::Protocol::Rect dirty_rect {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = m_display_info.rect.width,
|
||||
.height = m_display_info.rect.height
|
||||
};
|
||||
|
||||
TRY(m_graphics_adapter->transfer_framebuffer_data_to_host({}, *this, dirty_rect, true));
|
||||
// Flushing directly to screen
|
||||
TRY(flush_displayed_image(dirty_rect, true));
|
||||
return {};
|
||||
}
|
||||
|
||||
void VirtIODisplayConnector::enable_console()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_console);
|
||||
m_console->enable();
|
||||
}
|
||||
|
||||
void VirtIODisplayConnector::disable_console()
|
||||
{
|
||||
VERIFY(m_control_lock.is_locked());
|
||||
VERIFY(m_console);
|
||||
m_console->disable();
|
||||
}
|
||||
|
||||
void VirtIODisplayConnector::set_edid_bytes(Badge<VirtIOGraphicsAdapter>, Array<u8, 128> const& edid_bytes)
|
||||
{
|
||||
DisplayConnector::set_edid_bytes(edid_bytes);
|
||||
}
|
||||
|
||||
Graphics::VirtIOGPU::Protocol::DisplayInfoResponse::Display VirtIODisplayConnector::display_information(Badge<VirtIOGraphicsAdapter>) const
|
||||
{
|
||||
return m_display_info;
|
||||
}
|
||||
|
||||
void VirtIODisplayConnector::clear_to_black()
|
||||
{
|
||||
size_t width = m_display_info.rect.width;
|
||||
size_t height = m_display_info.rect.height;
|
||||
u8* data = framebuffer_data();
|
||||
for (size_t i = 0; i < width * height; ++i) {
|
||||
data[4 * i + 0] = 0x00;
|
||||
data[4 * i + 1] = 0x00;
|
||||
data[4 * i + 2] = 0x00;
|
||||
data[4 * i + 3] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIODisplayConnector::flush_displayed_image(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer)
|
||||
{
|
||||
VERIFY(m_graphics_adapter->operation_lock().is_locked());
|
||||
TRY(m_graphics_adapter->flush_displayed_image({}, *this, dirty_rect, main_buffer));
|
||||
return {};
|
||||
}
|
||||
|
||||
void VirtIODisplayConnector::set_dirty_displayed_rect(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer)
|
||||
{
|
||||
VERIFY(m_graphics_adapter->operation_lock().is_locked());
|
||||
m_graphics_adapter->set_dirty_displayed_rect({}, *this, dirty_rect, main_buffer);
|
||||
}
|
||||
|
||||
}
|
87
Kernel/Devices/GPU/VirtIO/DisplayConnector.h
Normal file
87
Kernel/Devices/GPU/VirtIO/DisplayConnector.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/BinaryBufferWriter.h>
|
||||
#include <AK/DistinctNumeric.h>
|
||||
#include <Kernel/Devices/CharacterDevice.h>
|
||||
#include <Kernel/Devices/GPU/Console/Console.h>
|
||||
#include <Kernel/Devices/GPU/DisplayConnector.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/GraphicsAdapter.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/Protocol.h>
|
||||
#include <Kernel/Memory/Region.h>
|
||||
#include <LibEDID/EDID.h>
|
||||
|
||||
namespace Kernel::Graphics::VirtIOGPU {
|
||||
|
||||
class Console;
|
||||
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class VirtIOGraphicsAdapter;
|
||||
class VirtIODisplayConnector final : public DisplayConnector {
|
||||
friend class Graphics::VirtIOGPU::Console;
|
||||
friend class DeviceManagement;
|
||||
|
||||
public:
|
||||
static NonnullLockRefPtr<VirtIODisplayConnector> must_create(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id);
|
||||
|
||||
void set_edid_bytes(Badge<VirtIOGraphicsAdapter>, Array<u8, 128> const& edid_bytes);
|
||||
void set_safe_mode_setting_after_initialization(Badge<VirtIOGraphicsAdapter>);
|
||||
Graphics::VirtIOGPU::ScanoutID scanout_id() const { return m_scanout_id; }
|
||||
Graphics::VirtIOGPU::Protocol::DisplayInfoResponse::Display display_information(Badge<VirtIOGraphicsAdapter>) const;
|
||||
|
||||
void initialize_console(Badge<VirtIOGraphicsAdapter>);
|
||||
|
||||
private:
|
||||
virtual bool mutable_mode_setting_capable() const override { return true; }
|
||||
virtual bool double_framebuffering_capable() const override { return false; }
|
||||
virtual bool partial_flush_support() const override { return true; }
|
||||
virtual ErrorOr<void> set_mode_setting(ModeSetting const&) override;
|
||||
virtual ErrorOr<void> set_safe_mode_setting() override;
|
||||
virtual ErrorOr<void> set_y_offset(size_t y) override;
|
||||
virtual ErrorOr<void> unblank() override;
|
||||
|
||||
// Note: VirtIO hardware requires a constant refresh to keep the screen in sync to the user.
|
||||
virtual bool flush_support() const override { return true; }
|
||||
// Note: Paravirtualized hardware doesn't require a defined refresh rate for modesetting.
|
||||
virtual bool refresh_rate_support() const override { return false; }
|
||||
|
||||
virtual ErrorOr<void> flush_first_surface() override;
|
||||
virtual ErrorOr<void> flush_rectangle(size_t buffer_index, FBRect const& rect) override;
|
||||
|
||||
virtual void enable_console() override;
|
||||
virtual void disable_console() override;
|
||||
|
||||
static bool is_valid_buffer_index(size_t buffer_index)
|
||||
{
|
||||
return buffer_index == 0 || buffer_index == 1;
|
||||
}
|
||||
|
||||
private:
|
||||
VirtIODisplayConnector(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id);
|
||||
|
||||
ErrorOr<void> flush_displayed_image(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer);
|
||||
void set_dirty_displayed_rect(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer);
|
||||
|
||||
void clear_to_black();
|
||||
|
||||
// Member data
|
||||
// Context used for kernel operations (e.g. flushing resources to scanout)
|
||||
Graphics::VirtIOGPU::ContextID m_kernel_context_id;
|
||||
|
||||
NonnullLockRefPtr<VirtIOGraphicsAdapter> m_graphics_adapter;
|
||||
LockRefPtr<Graphics::VirtIOGPU::Console> m_console;
|
||||
Graphics::VirtIOGPU::Protocol::DisplayInfoResponse::Display m_display_info {};
|
||||
Graphics::VirtIOGPU::ScanoutID m_scanout_id;
|
||||
|
||||
constexpr static size_t NUM_TRANSFER_REGION_PAGES = 256;
|
||||
};
|
||||
|
||||
}
|
158
Kernel/Devices/GPU/VirtIO/GPU3DDevice.cpp
Normal file
158
Kernel/Devices/GPU/VirtIO/GPU3DDevice.cpp
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/API/Ioctl.h>
|
||||
#include <Kernel/API/VirGL.h>
|
||||
#include <Kernel/Devices/GPU/Management.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/Console.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/GPU3DDevice.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/GraphicsAdapter.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/Protocol.h>
|
||||
#include <Kernel/Security/Random.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
VirtIOGPU3DDevice::PerContextState::PerContextState(OpenFileDescription& description, Graphics::VirtIOGPU::ContextID context_id, OwnPtr<Memory::Region> transfer_buffer_region)
|
||||
: m_context_id(context_id)
|
||||
, m_transfer_buffer_region(move(transfer_buffer_region))
|
||||
, m_attached_file_description(description)
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<NonnullLockRefPtr<VirtIOGPU3DDevice>> VirtIOGPU3DDevice::try_create(VirtIOGraphicsAdapter& adapter)
|
||||
{
|
||||
// Setup memory transfer region
|
||||
auto region_result = TRY(MM.allocate_kernel_region(
|
||||
NUM_TRANSFER_REGION_PAGES * PAGE_SIZE,
|
||||
"VIRGL3D kernel upload buffer"sv,
|
||||
Memory::Region::Access::ReadWrite,
|
||||
AllocationStrategy::AllocateNow));
|
||||
auto kernel_context_id = TRY(adapter.create_context());
|
||||
return TRY(DeviceManagement::try_create_device<VirtIOGPU3DDevice>(adapter, move(region_result), kernel_context_id));
|
||||
}
|
||||
|
||||
VirtIOGPU3DDevice::VirtIOGPU3DDevice(VirtIOGraphicsAdapter const& graphics_adapter, NonnullOwnPtr<Memory::Region> transfer_buffer_region, Graphics::VirtIOGPU::ContextID kernel_context_id)
|
||||
: CharacterDevice(28, 0)
|
||||
, m_graphics_adapter(graphics_adapter)
|
||||
, m_kernel_context_id(kernel_context_id)
|
||||
, m_transfer_buffer_region(move(transfer_buffer_region))
|
||||
{
|
||||
}
|
||||
|
||||
void VirtIOGPU3DDevice::detach(OpenFileDescription& description)
|
||||
{
|
||||
m_context_state_list.with([&description](auto& list) {
|
||||
for (auto& context : list) {
|
||||
if (&context.description() == &description) {
|
||||
context.m_list_node.remove();
|
||||
// NOTE: We break here because there shouldn't be another context
|
||||
// being attached to this OpenFileDescription.
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
CharacterDevice::detach(description);
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGPU3DDevice::ioctl(OpenFileDescription& description, unsigned request, Userspace<void*> arg)
|
||||
{
|
||||
|
||||
auto get_context_for_description = [](ContextList& list, OpenFileDescription& description) -> ErrorOr<NonnullRefPtr<PerContextState>> {
|
||||
for (auto& context : list) {
|
||||
if (&context.description() == &description)
|
||||
return context;
|
||||
}
|
||||
return EBADF;
|
||||
};
|
||||
|
||||
// TODO: We really should have ioctls for destroying resources as well
|
||||
switch (request) {
|
||||
case VIRGL_IOCTL_CREATE_CONTEXT: {
|
||||
return m_context_state_list.with([get_context_for_description, &description, this](auto& list) -> ErrorOr<void> {
|
||||
if (auto result = get_context_for_description(list, description); !result.is_error())
|
||||
return EEXIST;
|
||||
SpinlockLocker locker(m_graphics_adapter->operation_lock());
|
||||
// TODO: Delete the context if it fails to be set in m_context_state_lookup
|
||||
auto context_id = TRY(m_graphics_adapter->create_context());
|
||||
auto per_context_state = TRY(PerContextState::try_create(description, context_id));
|
||||
list.append(per_context_state);
|
||||
return {};
|
||||
});
|
||||
}
|
||||
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 (Checked<size_t>::addition_would_overflow(transfer_descriptor.offset_in_region, transfer_descriptor.num_bytes)) {
|
||||
return EOVERFLOW;
|
||||
}
|
||||
if (transfer_descriptor.offset_in_region + transfer_descriptor.num_bytes > NUM_TRANSFER_REGION_PAGES * PAGE_SIZE) {
|
||||
return EOVERFLOW;
|
||||
}
|
||||
|
||||
return m_context_state_list.with([get_context_for_description, &description, &transfer_descriptor](auto& list) -> ErrorOr<void> {
|
||||
auto context = TRY(get_context_for_description(list, description));
|
||||
auto& transfer_buffer_region = context->transfer_buffer_region();
|
||||
if (transfer_descriptor.direction == VIRGL_DATA_DIR_GUEST_TO_HOST) {
|
||||
auto target = 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) {
|
||||
auto source = 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: {
|
||||
auto user_command_buffer = static_ptr_cast<VirGLCommandBuffer const*>(arg);
|
||||
auto command_buffer = TRY(copy_typed_from_user(user_command_buffer));
|
||||
return m_context_state_list.with([get_context_for_description, &description, &command_buffer, this](auto& list) -> ErrorOr<void> {
|
||||
auto context = TRY(get_context_for_description(list, description));
|
||||
auto context_id = context->context_id();
|
||||
SpinlockLocker locker(m_graphics_adapter->operation_lock());
|
||||
TRY(m_graphics_adapter->submit_command_buffer(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));
|
||||
Graphics::VirtIOGPU::Protocol::Resource3DSpecification const resource_spec = {
|
||||
.target = static_cast<Graphics::VirtIOGPU::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,
|
||||
.padding = 0,
|
||||
};
|
||||
return m_context_state_list.with([get_context_for_description, &description, &resource_spec, &spec, &arg, this](auto& list) -> ErrorOr<void> {
|
||||
auto context = TRY(get_context_for_description(list, description));
|
||||
SpinlockLocker locker(m_graphics_adapter->operation_lock());
|
||||
// FIXME: What would be an appropriate resource free-ing mechanism to use in case anything
|
||||
// after this fails?
|
||||
auto resource_id = TRY(m_graphics_adapter->create_3d_resource(resource_spec));
|
||||
TRY(m_graphics_adapter->attach_resource_to_context(resource_id, context->context_id()));
|
||||
TRY(m_graphics_adapter->ensure_backing_storage(resource_id, context->transfer_buffer_region(), 0, NUM_TRANSFER_REGION_PAGES * PAGE_SIZE));
|
||||
spec.created_resource_id = resource_id.value();
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
158
Kernel/Devices/GPU/VirtIO/GPU3DDevice.h
Normal file
158
Kernel/Devices/GPU/VirtIO/GPU3DDevice.h
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/DistinctNumeric.h>
|
||||
#include <AK/IntrusiveList.h>
|
||||
#include <Kernel/Devices/CharacterDevice.h>
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/Protocol.h>
|
||||
#include <Kernel/Locking/SpinlockProtected.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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class VirtIOGraphicsAdapter;
|
||||
class VirtIOGPU3DDevice : public CharacterDevice {
|
||||
friend class DeviceManagement;
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullLockRefPtr<VirtIOGPU3DDevice>> try_create(VirtIOGraphicsAdapter&);
|
||||
|
||||
private:
|
||||
VirtIOGPU3DDevice(VirtIOGraphicsAdapter const& graphics_adapter, NonnullOwnPtr<Memory::Region> transfer_buffer_region, Graphics::VirtIOGPU::ContextID kernel_context_id);
|
||||
|
||||
class PerContextState final : public AtomicRefCounted<PerContextState> {
|
||||
friend class VirtIOGPU3DDevice;
|
||||
|
||||
public:
|
||||
static ErrorOr<NonnullRefPtr<PerContextState>> try_create(OpenFileDescription& description, Graphics::VirtIOGPU::ContextID context_id)
|
||||
{
|
||||
auto region_result = TRY(MM.allocate_kernel_region(
|
||||
NUM_TRANSFER_REGION_PAGES * PAGE_SIZE,
|
||||
"VIRGL3D userspace upload buffer"sv,
|
||||
Memory::Region::Access::ReadWrite,
|
||||
AllocationStrategy::AllocateNow));
|
||||
return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PerContextState(description, context_id, move(region_result))));
|
||||
}
|
||||
Graphics::VirtIOGPU::ContextID context_id() { return m_context_id; }
|
||||
Memory::Region& transfer_buffer_region() { return *m_transfer_buffer_region; }
|
||||
|
||||
OpenFileDescription& description() { return m_attached_file_description; }
|
||||
|
||||
private:
|
||||
PerContextState() = delete;
|
||||
PerContextState(OpenFileDescription&, Graphics::VirtIOGPU::ContextID context_id, OwnPtr<Memory::Region> transfer_buffer_region);
|
||||
Graphics::VirtIOGPU::ContextID m_context_id;
|
||||
OwnPtr<Memory::Region> m_transfer_buffer_region;
|
||||
|
||||
// NOTE: We clean this whole object when the file description is closed, therefore we need to hold
|
||||
// a raw reference here instead of a strong reference pointer (e.g. RefPtr, which will make it
|
||||
// possible to leak the attached OpenFileDescription for a context in this device).
|
||||
OpenFileDescription& m_attached_file_description;
|
||||
|
||||
IntrusiveListNode<PerContextState, NonnullRefPtr<PerContextState>> m_list_node;
|
||||
};
|
||||
|
||||
virtual bool can_read(OpenFileDescription const&, u64) const override { return true; }
|
||||
virtual bool can_write(OpenFileDescription const&, 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, UserOrKernelBuffer const&, size_t) override { return ENOTSUP; }
|
||||
virtual StringView class_name() const override { return "virgl3d"sv; }
|
||||
|
||||
virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override;
|
||||
virtual void detach(OpenFileDescription&) override;
|
||||
|
||||
using ContextList = IntrusiveListRelaxedConst<&PerContextState::m_list_node>;
|
||||
|
||||
private:
|
||||
NonnullLockRefPtr<VirtIOGraphicsAdapter> m_graphics_adapter;
|
||||
// Context used for kernel operations (e.g. flushing resources to scanout)
|
||||
Graphics::VirtIOGPU::ContextID m_kernel_context_id;
|
||||
SpinlockProtected<ContextList, LockRank::None> m_context_state_list;
|
||||
// Memory management for backing buffers
|
||||
NonnullOwnPtr<Memory::Region> m_transfer_buffer_region;
|
||||
constexpr static size_t NUM_TRANSFER_REGION_PAGES = 1024;
|
||||
};
|
||||
|
||||
}
|
557
Kernel/Devices/GPU/VirtIO/GraphicsAdapter.cpp
Normal file
557
Kernel/Devices/GPU/VirtIO/GraphicsAdapter.cpp
Normal file
|
@ -0,0 +1,557 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/BinaryBufferWriter.h>
|
||||
#include <Kernel/Arch/Delay.h>
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/Bus/PCI/IDs.h>
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Devices/GPU/Console/GenericFramebufferConsole.h>
|
||||
#include <Kernel/Devices/GPU/Management.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/Console.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/DisplayConnector.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/GPU3DDevice.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/GraphicsAdapter.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
#define DEVICE_EVENTS_READ 0x0
|
||||
#define DEVICE_EVENTS_CLEAR 0x4
|
||||
#define DEVICE_NUM_SCANOUTS 0x8
|
||||
|
||||
ErrorOr<bool> VirtIOGraphicsAdapter::probe(PCI::DeviceIdentifier const& device_identifier)
|
||||
{
|
||||
return device_identifier.hardware_id().vendor_id == PCI::VendorID::VirtIO;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullLockRefPtr<GenericGraphicsAdapter>> VirtIOGraphicsAdapter::create(PCI::DeviceIdentifier const& device_identifier)
|
||||
{
|
||||
// Setup memory transfer region
|
||||
auto scratch_space_region = TRY(MM.allocate_contiguous_kernel_region(
|
||||
32 * PAGE_SIZE,
|
||||
"VirtGPU Scratch Space"sv,
|
||||
Memory::Region::Access::ReadWrite));
|
||||
|
||||
auto active_context_ids = TRY(Bitmap::create(VREND_MAX_CTX, false));
|
||||
auto adapter = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) VirtIOGraphicsAdapter(device_identifier, move(active_context_ids), move(scratch_space_region))));
|
||||
TRY(adapter->initialize_virtio_resources());
|
||||
TRY(adapter->initialize_adapter());
|
||||
return adapter;
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::initialize_adapter()
|
||||
{
|
||||
VERIFY(m_num_scanouts <= VIRTIO_GPU_MAX_SCANOUTS);
|
||||
TRY(initialize_3d_device());
|
||||
for (size_t index = 0; index < m_num_scanouts; index++) {
|
||||
auto display_connector = VirtIODisplayConnector::must_create(*this, index);
|
||||
m_scanouts[index].display_connector = display_connector;
|
||||
TRY(query_and_set_edid(index, *display_connector));
|
||||
display_connector->set_safe_mode_setting_after_initialization({});
|
||||
display_connector->initialize_console({});
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::mode_set_resolution(Badge<VirtIODisplayConnector>, VirtIODisplayConnector& connector, size_t width, size_t height)
|
||||
{
|
||||
SpinlockLocker locker(m_operation_lock);
|
||||
VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS);
|
||||
auto rounded_buffer_size = TRY(calculate_framebuffer_size(width, height));
|
||||
TRY(attach_physical_range_to_framebuffer(connector, true, 0, rounded_buffer_size));
|
||||
return {};
|
||||
}
|
||||
|
||||
void VirtIOGraphicsAdapter::set_dirty_displayed_rect(Badge<VirtIODisplayConnector>, VirtIODisplayConnector& connector, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS);
|
||||
Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer;
|
||||
if (buffer.dirty_rect.width == 0 || buffer.dirty_rect.height == 0) {
|
||||
buffer.dirty_rect = dirty_rect;
|
||||
} else {
|
||||
auto current_dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width;
|
||||
auto current_dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height;
|
||||
buffer.dirty_rect.x = min(buffer.dirty_rect.x, dirty_rect.x);
|
||||
buffer.dirty_rect.y = min(buffer.dirty_rect.y, dirty_rect.y);
|
||||
buffer.dirty_rect.width = max(current_dirty_right, dirty_rect.x + dirty_rect.width) - buffer.dirty_rect.x;
|
||||
buffer.dirty_rect.height = max(current_dirty_bottom, dirty_rect.y + dirty_rect.height) - buffer.dirty_rect.y;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::flush_displayed_image(Badge<VirtIODisplayConnector>, VirtIODisplayConnector& connector, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS);
|
||||
Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer;
|
||||
TRY(flush_displayed_image(buffer.resource_id, dirty_rect));
|
||||
buffer.dirty_rect = {};
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::transfer_framebuffer_data_to_host(Badge<VirtIODisplayConnector>, VirtIODisplayConnector& connector, Graphics::VirtIOGPU::Protocol::Rect const& rect, bool main_buffer)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
VERIFY(connector.scanout_id() < VIRTIO_GPU_MAX_SCANOUTS);
|
||||
Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer;
|
||||
TRY(transfer_framebuffer_data_to_host(connector.scanout_id(), buffer.resource_id, rect));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::attach_physical_range_to_framebuffer(VirtIODisplayConnector& connector, bool main_buffer, size_t framebuffer_offset, size_t framebuffer_size)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
Scanout::PhysicalBuffer& buffer = main_buffer ? m_scanouts[connector.scanout_id().value()].main_buffer : m_scanouts[connector.scanout_id().value()].back_buffer;
|
||||
buffer.framebuffer_offset = framebuffer_offset;
|
||||
|
||||
// 1. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D
|
||||
if (buffer.resource_id.value() != 0) {
|
||||
// FIXME: Do we need to remove the resource regardless of this condition?
|
||||
// Do we need to remove it if any of the code below fails for some reason?
|
||||
TRY(delete_resource(buffer.resource_id));
|
||||
}
|
||||
|
||||
auto display_info = connector.display_information({});
|
||||
buffer.resource_id = TRY(create_2d_resource(display_info.rect));
|
||||
|
||||
// 2. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING
|
||||
TRY(ensure_backing_storage(buffer.resource_id, connector.framebuffer_region(), buffer.framebuffer_offset, framebuffer_size));
|
||||
// 3. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout.
|
||||
TRY(set_scanout_resource(connector.scanout_id(), buffer.resource_id, display_info.rect));
|
||||
|
||||
// Make sure we constrain the existing dirty rect (if any)
|
||||
if (buffer.dirty_rect.width != 0 || buffer.dirty_rect.height != 0) {
|
||||
auto dirty_right = buffer.dirty_rect.x + buffer.dirty_rect.width;
|
||||
auto dirty_bottom = buffer.dirty_rect.y + buffer.dirty_rect.height;
|
||||
buffer.dirty_rect.width = min(dirty_right, display_info.rect.x + display_info.rect.width) - buffer.dirty_rect.x;
|
||||
buffer.dirty_rect.height = min(dirty_bottom, display_info.rect.y + display_info.rect.height) - buffer.dirty_rect.y;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
VirtIOGraphicsAdapter::VirtIOGraphicsAdapter(PCI::DeviceIdentifier const& device_identifier, Bitmap&& active_context_ids, NonnullOwnPtr<Memory::Region> scratch_space_region)
|
||||
: VirtIO::Device(device_identifier)
|
||||
, m_scratch_space(move(scratch_space_region))
|
||||
{
|
||||
m_active_context_ids.with([&](Bitmap& my_active_context_ids) {
|
||||
my_active_context_ids = move(active_context_ids);
|
||||
// Note: Context ID 0 is invalid, so mark it as in use.
|
||||
my_active_context_ids.set(0, true);
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::initialize_virtio_resources()
|
||||
{
|
||||
TRY(VirtIO::Device::initialize_virtio_resources());
|
||||
auto* config = TRY(get_config(VirtIO::ConfigurationType::Device));
|
||||
m_device_configuration = config;
|
||||
bool success = negotiate_features([&](u64 supported_features) {
|
||||
u64 negotiated = 0;
|
||||
if (is_feature_set(supported_features, VIRTIO_GPU_F_VIRGL)) {
|
||||
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))
|
||||
negotiated |= VIRTIO_GPU_F_EDID;
|
||||
return negotiated;
|
||||
});
|
||||
if (success) {
|
||||
read_config_atomic([&]() {
|
||||
m_num_scanouts = config_read32(*config, DEVICE_NUM_SCANOUTS);
|
||||
});
|
||||
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: num_scanouts: {}", m_num_scanouts);
|
||||
success = setup_queues(2); // CONTROLQ + CURSORQ
|
||||
}
|
||||
if (!success)
|
||||
return Error::from_errno(EIO);
|
||||
finish_init();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool VirtIOGraphicsAdapter::handle_device_config_change()
|
||||
{
|
||||
auto events = get_pending_events();
|
||||
if (events & VIRTIO_GPU_EVENT_DISPLAY) {
|
||||
// The host window was resized, in SerenityOS we completely ignore this event
|
||||
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Ignoring virtio gpu display resize event");
|
||||
clear_pending_events(VIRTIO_GPU_EVENT_DISPLAY);
|
||||
}
|
||||
if (events & ~VIRTIO_GPU_EVENT_DISPLAY) {
|
||||
dbgln("VirtIO::GraphicsAdapter: Got unknown device config change event: {:#x}", events);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VirtIOGraphicsAdapter::handle_queue_update(u16)
|
||||
{
|
||||
}
|
||||
|
||||
u32 VirtIOGraphicsAdapter::get_pending_events()
|
||||
{
|
||||
return config_read32(*m_device_configuration, DEVICE_EVENTS_READ);
|
||||
}
|
||||
|
||||
void VirtIOGraphicsAdapter::clear_pending_events(u32 event_bitmask)
|
||||
{
|
||||
config_write32(*m_device_configuration, DEVICE_EVENTS_CLEAR, event_bitmask);
|
||||
}
|
||||
|
||||
static void populate_virtio_gpu_request_header(Graphics::VirtIOGPU::Protocol::ControlHeader& header, Graphics::VirtIOGPU::Protocol::CommandType ctrl_type, u32 flags)
|
||||
{
|
||||
header.type = to_underlying(ctrl_type);
|
||||
header.flags = flags;
|
||||
header.fence_id = 0;
|
||||
header.context_id = 0;
|
||||
header.padding = 0;
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::query_and_set_edid(u32 scanout_id, VirtIODisplayConnector& display_connector)
|
||||
{
|
||||
SpinlockLocker locker(m_operation_lock);
|
||||
if (!is_feature_accepted(VIRTIO_GPU_F_EDID))
|
||||
return Error::from_errno(ENOTSUP);
|
||||
|
||||
auto writer = create_scratchspace_writer();
|
||||
auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::GetEDID>();
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::GetEDIDResponse>();
|
||||
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_GET_EDID, 0);
|
||||
|
||||
request.scanout_id = scanout_id;
|
||||
request.padding = 0;
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response)));
|
||||
|
||||
if (response.header.type != to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_EDID)) {
|
||||
dmesgln("VirtIO::GraphicsAdapter: Failed to get EDID");
|
||||
return Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
if (response.size == 0) {
|
||||
dmesgln("VirtIO::GraphicsAdapter: Failed to get EDID, empty buffer");
|
||||
return Error::from_errno(EIO);
|
||||
}
|
||||
|
||||
Array<u8, 128> raw_edid;
|
||||
memcpy(raw_edid.data(), response.edid, min(sizeof(raw_edid), response.size));
|
||||
display_connector.set_edid_bytes({}, raw_edid);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<Graphics::VirtIOGPU::ResourceID> VirtIOGraphicsAdapter::create_2d_resource(Graphics::VirtIOGPU::Protocol::Rect rect)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
auto writer = create_scratchspace_writer();
|
||||
auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::ResourceCreate2D>();
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>();
|
||||
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, 0);
|
||||
|
||||
auto resource_id = allocate_resource_id();
|
||||
request.resource_id = resource_id.value();
|
||||
request.width = rect.width;
|
||||
request.height = rect.height;
|
||||
request.format = to_underlying(Graphics::VirtIOGPU::Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM);
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response)));
|
||||
|
||||
if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) {
|
||||
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Allocated 2d resource with id {}", resource_id.value());
|
||||
return resource_id;
|
||||
}
|
||||
return EIO;
|
||||
}
|
||||
|
||||
ErrorOr<Graphics::VirtIOGPU::ResourceID> VirtIOGraphicsAdapter::create_3d_resource(Graphics::VirtIOGPU::Protocol::Resource3DSpecification const& resource_3d_specification)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
auto writer = create_scratchspace_writer();
|
||||
auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::ResourceCreate3D>();
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>();
|
||||
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_CREATE_3D, 0);
|
||||
|
||||
// FIXME: What would be an appropriate resource free-ing mechanism to use in case anything
|
||||
// after this fails?
|
||||
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;
|
||||
|
||||
// Validate that the sub copy from the resource_3d_specification to the offset of the request fits.
|
||||
static_assert((sizeof(request) - offsetof(Graphics::VirtIOGPU::Protocol::ResourceCreate3D, target) == sizeof(resource_3d_specification)));
|
||||
memcpy(start_of_copied_fields, &resource_3d_specification, sizeof(resource_3d_specification));
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response)));
|
||||
|
||||
if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) {
|
||||
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Allocated 3d resource with id {}", resource_id.value());
|
||||
return resource_id;
|
||||
}
|
||||
return EIO;
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::ensure_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
|
||||
VERIFY(buffer_offset % PAGE_SIZE == 0);
|
||||
VERIFY(buffer_length % PAGE_SIZE == 0);
|
||||
auto first_page_index = buffer_offset / PAGE_SIZE;
|
||||
size_t num_mem_regions = buffer_length / PAGE_SIZE;
|
||||
|
||||
auto writer = create_scratchspace_writer();
|
||||
auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::ResourceAttachBacking>();
|
||||
const size_t header_block_size = sizeof(request) + num_mem_regions * sizeof(Graphics::VirtIOGPU::Protocol::MemoryEntry);
|
||||
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, 0);
|
||||
request.resource_id = resource_id.value();
|
||||
request.num_entries = num_mem_regions;
|
||||
for (size_t i = 0; i < num_mem_regions; ++i) {
|
||||
auto& memory_entry = writer.append_structure<Graphics::VirtIOGPU::Protocol::MemoryEntry>();
|
||||
memory_entry.address = region.physical_page(first_page_index + i)->paddr().get();
|
||||
memory_entry.length = PAGE_SIZE;
|
||||
}
|
||||
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>();
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), header_block_size, sizeof(response)));
|
||||
|
||||
if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) {
|
||||
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Allocated backing storage");
|
||||
return {};
|
||||
}
|
||||
return EIO;
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::detach_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
auto writer = create_scratchspace_writer();
|
||||
auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::ResourceDetachBacking>();
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>();
|
||||
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, 0);
|
||||
request.resource_id = resource_id.value();
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response)));
|
||||
|
||||
if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) {
|
||||
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Detached backing storage");
|
||||
return {};
|
||||
}
|
||||
return EIO;
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::set_scanout_resource(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect rect)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
// We need to scope the request/response here so that we can query display information later on
|
||||
auto writer = create_scratchspace_writer();
|
||||
auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::SetScanOut>();
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>();
|
||||
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_SET_SCANOUT, 0);
|
||||
request.resource_id = resource_id.value();
|
||||
request.scanout_id = scanout.value();
|
||||
request.rect = rect;
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response)));
|
||||
|
||||
if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) {
|
||||
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Set backing scanout");
|
||||
return {};
|
||||
}
|
||||
return EIO;
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::transfer_framebuffer_data_to_host(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
auto writer = create_scratchspace_writer();
|
||||
auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::TransferToHost2D>();
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>();
|
||||
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, 0);
|
||||
request.offset = (dirty_rect.x + (dirty_rect.y * m_scanouts[scanout.value()].display_connector->display_information({}).rect.width)) * sizeof(u32);
|
||||
request.resource_id = resource_id.value();
|
||||
request.rect = dirty_rect;
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response)));
|
||||
|
||||
if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA))
|
||||
return {};
|
||||
return EIO;
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::flush_displayed_image(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
auto writer = create_scratchspace_writer();
|
||||
auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::ResourceFlush>();
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>();
|
||||
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_FLUSH, 0);
|
||||
request.resource_id = resource_id.value();
|
||||
request.rect = dirty_rect;
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response)));
|
||||
|
||||
if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA))
|
||||
return {};
|
||||
return EIO;
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::synchronous_virtio_gpu_command(size_t microseconds_timeout, PhysicalAddress buffer_start, size_t request_size, size_t response_size)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
VERIFY(microseconds_timeout > 10);
|
||||
VERIFY(microseconds_timeout < 100000);
|
||||
auto& queue = get_queue(CONTROLQ);
|
||||
queue.disable_interrupts();
|
||||
SpinlockLocker lock(queue.lock());
|
||||
VirtIO::QueueChain chain { queue };
|
||||
chain.add_buffer_to_chain(buffer_start, request_size, VirtIO::BufferType::DeviceReadable);
|
||||
chain.add_buffer_to_chain(buffer_start.offset(request_size), response_size, VirtIO::BufferType::DeviceWritable);
|
||||
supply_chain_and_notify(CONTROLQ, chain);
|
||||
full_memory_barrier();
|
||||
size_t current_time = 0;
|
||||
ScopeGuard clear_used_buffers([&] {
|
||||
queue.discard_used_buffers();
|
||||
});
|
||||
while (current_time < microseconds_timeout) {
|
||||
if (queue.new_data_available())
|
||||
return {};
|
||||
microseconds_delay(1);
|
||||
current_time++;
|
||||
}
|
||||
return Error::from_errno(EBUSY);
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::flush_dirty_rectangle(Graphics::VirtIOGPU::ScanoutID scanout_id, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
TRY(transfer_framebuffer_data_to_host(scanout_id, resource_id, dirty_rect));
|
||||
TRY(flush_displayed_image(resource_id, dirty_rect));
|
||||
return {};
|
||||
}
|
||||
|
||||
Graphics::VirtIOGPU::ResourceID VirtIOGraphicsAdapter::allocate_resource_id()
|
||||
{
|
||||
return m_resource_id_counter++;
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::delete_resource(Graphics::VirtIOGPU::ResourceID resource_id)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
auto writer = create_scratchspace_writer();
|
||||
auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::ResourceUnref>();
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>();
|
||||
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_UNREF, 0);
|
||||
request.resource_id = resource_id.value();
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response)));
|
||||
|
||||
if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA))
|
||||
return {};
|
||||
return EIO;
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::initialize_3d_device()
|
||||
{
|
||||
if (m_has_virgl_support) {
|
||||
SpinlockLocker locker(m_operation_lock);
|
||||
m_3d_device = TRY(VirtIOGPU3DDevice::try_create(*this));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<Graphics::VirtIOGPU::ContextID> VirtIOGraphicsAdapter::create_context()
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
return m_active_context_ids.with([&](Bitmap& active_context_ids) -> ErrorOr<Graphics::VirtIOGPU::ContextID> {
|
||||
auto maybe_available_id = active_context_ids.find_first_unset();
|
||||
if (!maybe_available_id.has_value()) {
|
||||
dmesgln("VirtIO::GraphicsAdapter: No available context IDs.");
|
||||
return Error::from_errno(ENXIO);
|
||||
}
|
||||
auto new_context_id = static_cast<u32>(maybe_available_id.value());
|
||||
|
||||
auto writer = create_scratchspace_writer();
|
||||
auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::ContextCreate>();
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>();
|
||||
|
||||
constexpr char const* region_name = "Serenity VirGL3D Context";
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_CTX_CREATE, 0);
|
||||
request.header.context_id = new_context_id;
|
||||
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);
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response)));
|
||||
|
||||
if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)) {
|
||||
active_context_ids.set(maybe_available_id.value(), true);
|
||||
return static_cast<Graphics::VirtIOGPU::ContextID>(new_context_id);
|
||||
}
|
||||
return Error::from_errno(EIO);
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::submit_command_buffer(Graphics::VirtIOGPU::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<Graphics::VirtIOGPU::Protocol::CommandSubmit>();
|
||||
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_SUBMIT_3D, 0);
|
||||
request.header.context_id = context_id.value();
|
||||
|
||||
auto max_command_buffer_length = m_scratch_space->size() - sizeof(request) - sizeof(Graphics::VirtIOGPU::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(Graphics::VirtIOGPU::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(Graphics::VirtIOGPU::Protocol::ControlHeader) != 0) {
|
||||
VERIFY((writer.current_offset() % alignof(Graphics::VirtIOGPU::Protocol::ControlHeader)) % sizeof(u32) == 0);
|
||||
writer.append_structure<u32>() = to_underlying(Graphics::VirtIOGPU::VirGLCommand::NOP);
|
||||
request.size += 4;
|
||||
}
|
||||
dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Sending command buffer of length {}", request.size);
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>();
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request) + request.size, sizeof(response)));
|
||||
|
||||
if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA))
|
||||
return {};
|
||||
return EIO;
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtIOGraphicsAdapter::attach_resource_to_context(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::ContextID context_id)
|
||||
{
|
||||
VERIFY(m_operation_lock.is_locked());
|
||||
auto writer = create_scratchspace_writer();
|
||||
auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::ContextAttachResource>();
|
||||
auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>();
|
||||
populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, 0);
|
||||
request.header.context_id = context_id.value();
|
||||
request.resource_id = resource_id.value();
|
||||
|
||||
TRY(synchronous_virtio_gpu_command(10000, start_of_scratch_space(), sizeof(request), sizeof(response)));
|
||||
|
||||
if (response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA))
|
||||
return {};
|
||||
return EIO;
|
||||
}
|
||||
|
||||
}
|
125
Kernel/Devices/GPU/VirtIO/GraphicsAdapter.h
Normal file
125
Kernel/Devices/GPU/VirtIO/GraphicsAdapter.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/BinaryBufferWriter.h>
|
||||
#include <AK/DistinctNumeric.h>
|
||||
#include <Kernel/Bus/VirtIO/Device.h>
|
||||
#include <Kernel/Bus/VirtIO/Queue.h>
|
||||
#include <Kernel/Devices/GPU/GenericGraphicsAdapter.h>
|
||||
#include <Kernel/Devices/GPU/VirtIO/Protocol.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
#define VIRTIO_GPU_F_VIRGL (1 << 0)
|
||||
#define VIRTIO_GPU_F_EDID (1 << 1)
|
||||
|
||||
#define VIRTIO_GPU_FLAG_FENCE (1 << 0)
|
||||
|
||||
#define CONTROLQ 0
|
||||
#define CURSORQ 1
|
||||
|
||||
#define MAX_VIRTIOGPU_RESOLUTION_WIDTH 3840
|
||||
#define MAX_VIRTIOGPU_RESOLUTION_HEIGHT 2160
|
||||
|
||||
#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
|
||||
|
||||
class VirtIODisplayConnector;
|
||||
class VirtIOGPU3DDevice;
|
||||
class VirtIOGraphicsAdapter final
|
||||
: public GenericGraphicsAdapter
|
||||
, public VirtIO::Device {
|
||||
friend class VirtIODisplayConnector;
|
||||
friend class VirtIOGPU3DDevice;
|
||||
|
||||
public:
|
||||
static ErrorOr<bool> probe(PCI::DeviceIdentifier const&);
|
||||
static ErrorOr<NonnullLockRefPtr<GenericGraphicsAdapter>> create(PCI::DeviceIdentifier const&);
|
||||
|
||||
virtual ErrorOr<void> initialize_virtio_resources() override;
|
||||
|
||||
virtual StringView device_name() const override { return "VirtIOGraphicsAdapter"sv; }
|
||||
|
||||
ErrorOr<void> mode_set_resolution(Badge<VirtIODisplayConnector>, VirtIODisplayConnector&, size_t width, size_t height);
|
||||
void set_dirty_displayed_rect(Badge<VirtIODisplayConnector>, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer);
|
||||
ErrorOr<void> flush_displayed_image(Badge<VirtIODisplayConnector>, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, bool main_buffer);
|
||||
ErrorOr<void> transfer_framebuffer_data_to_host(Badge<VirtIODisplayConnector>, VirtIODisplayConnector&, Graphics::VirtIOGPU::Protocol::Rect const& rect, bool main_buffer);
|
||||
|
||||
private:
|
||||
ErrorOr<void> attach_physical_range_to_framebuffer(VirtIODisplayConnector& connector, bool main_buffer, size_t framebuffer_offset, size_t framebuffer_size);
|
||||
|
||||
ErrorOr<void> initialize_3d_device();
|
||||
|
||||
ErrorOr<void> flush_dirty_rectangle(Graphics::VirtIOGPU::ScanoutID, Graphics::VirtIOGPU::ResourceID, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect);
|
||||
struct Scanout {
|
||||
struct PhysicalBuffer {
|
||||
size_t framebuffer_offset { 0 };
|
||||
Graphics::VirtIOGPU::Protocol::Rect dirty_rect {};
|
||||
Graphics::VirtIOGPU::ResourceID resource_id { 0 };
|
||||
};
|
||||
|
||||
LockRefPtr<VirtIODisplayConnector> display_connector;
|
||||
PhysicalBuffer main_buffer;
|
||||
PhysicalBuffer back_buffer;
|
||||
};
|
||||
|
||||
VirtIOGraphicsAdapter(PCI::DeviceIdentifier const&, Bitmap&& active_context_ids, NonnullOwnPtr<Memory::Region> scratch_space_region);
|
||||
|
||||
ErrorOr<void> initialize_adapter();
|
||||
|
||||
virtual bool handle_device_config_change() override;
|
||||
virtual void handle_queue_update(u16 queue_index) override;
|
||||
u32 get_pending_events();
|
||||
void clear_pending_events(u32 event_bitmask);
|
||||
|
||||
// 2D framebuffer stuff
|
||||
static ErrorOr<FlatPtr> calculate_framebuffer_size(size_t width, size_t height)
|
||||
{
|
||||
// VirtIO resources can only map on page boundaries!
|
||||
return Memory::page_round_up(sizeof(u32) * width * height);
|
||||
}
|
||||
|
||||
// 3D Command stuff
|
||||
ErrorOr<Graphics::VirtIOGPU::ContextID> create_context();
|
||||
ErrorOr<void> attach_resource_to_context(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::ContextID context_id);
|
||||
ErrorOr<void> submit_command_buffer(Graphics::VirtIOGPU::ContextID, Function<size_t(Bytes)> buffer_writer);
|
||||
Graphics::VirtIOGPU::Protocol::TextureFormat get_framebuffer_format() const { return Graphics::VirtIOGPU::Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; }
|
||||
|
||||
auto& operation_lock() { return m_operation_lock; }
|
||||
Graphics::VirtIOGPU::ResourceID allocate_resource_id();
|
||||
|
||||
PhysicalAddress start_of_scratch_space() const { return m_scratch_space->physical_page(0)->paddr(); }
|
||||
AK::BinaryBufferWriter create_scratchspace_writer()
|
||||
{
|
||||
return { Bytes(m_scratch_space->vaddr().as_ptr(), m_scratch_space->size()) };
|
||||
}
|
||||
ErrorOr<void> synchronous_virtio_gpu_command(size_t microseconds_timeout, PhysicalAddress buffer_start, size_t request_size, size_t response_size);
|
||||
|
||||
ErrorOr<Graphics::VirtIOGPU::ResourceID> create_2d_resource(Graphics::VirtIOGPU::Protocol::Rect rect);
|
||||
ErrorOr<Graphics::VirtIOGPU::ResourceID> create_3d_resource(Graphics::VirtIOGPU::Protocol::Resource3DSpecification const& resource_3d_specification);
|
||||
ErrorOr<void> delete_resource(Graphics::VirtIOGPU::ResourceID resource_id);
|
||||
ErrorOr<void> ensure_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length);
|
||||
ErrorOr<void> detach_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id);
|
||||
ErrorOr<void> set_scanout_resource(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect rect);
|
||||
ErrorOr<void> transfer_framebuffer_data_to_host(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& rect);
|
||||
ErrorOr<void> flush_displayed_image(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect);
|
||||
ErrorOr<void> query_and_set_edid(u32 scanout_id, VirtIODisplayConnector& display_connector);
|
||||
|
||||
size_t m_num_scanouts { 0 };
|
||||
Scanout m_scanouts[VIRTIO_GPU_MAX_SCANOUTS];
|
||||
|
||||
VirtIO::Configuration const* m_device_configuration { nullptr };
|
||||
// Note: Resource ID 0 is invalid, and we must not allocate 0 as the first resource ID.
|
||||
Atomic<u32> m_resource_id_counter { 1 };
|
||||
SpinlockProtected<Bitmap, LockRank::None> m_active_context_ids {};
|
||||
LockRefPtr<VirtIOGPU3DDevice> m_3d_device;
|
||||
bool m_has_virgl_support { false };
|
||||
|
||||
Spinlock<LockRank::None> m_operation_lock {};
|
||||
NonnullOwnPtr<Memory::Region> m_scratch_space;
|
||||
};
|
||||
}
|
334
Kernel/Devices/GPU/VirtIO/Protocol.h
Normal file
334
Kernel/Devices/GPU/VirtIO/Protocol.h
Normal file
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Types.h>
|
||||
|
||||
#define VIRTIO_GPU_MAX_SCANOUTS 16
|
||||
|
||||
namespace Kernel::Graphics::VirtIOGPU {
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, ContextID);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u32, ResourceID);
|
||||
AK_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 {
|
||||
|
||||
// Specification equivalent: enum virtio_gpu_ctrl_type
|
||||
enum class CommandType : u32 {
|
||||
/* 2d commands */
|
||||
VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
|
||||
VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
|
||||
VIRTIO_GPU_CMD_RESOURCE_UNREF,
|
||||
VIRTIO_GPU_CMD_SET_SCANOUT,
|
||||
VIRTIO_GPU_CMD_RESOURCE_FLUSH,
|
||||
VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
|
||||
VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
|
||||
VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
|
||||
VIRTIO_GPU_CMD_GET_CAPSET_INFO,
|
||||
VIRTIO_GPU_CMD_GET_CAPSET,
|
||||
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 */
|
||||
VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
|
||||
VIRTIO_GPU_CMD_MOVE_CURSOR,
|
||||
|
||||
/* success responses */
|
||||
VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
|
||||
VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
|
||||
VIRTIO_GPU_RESP_OK_CAPSET_INFO,
|
||||
VIRTIO_GPU_RESP_OK_CAPSET,
|
||||
VIRTIO_GPU_RESP_OK_EDID,
|
||||
|
||||
/* error responses */
|
||||
VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
|
||||
VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
|
||||
VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
|
||||
VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
|
||||
VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
|
||||
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
|
||||
struct ControlHeader {
|
||||
u32 type;
|
||||
u32 flags;
|
||||
u64 fence_id;
|
||||
u32 context_id;
|
||||
u32 padding;
|
||||
};
|
||||
|
||||
// Specification equivalent: struct virtio_gpu_rect
|
||||
struct Rect {
|
||||
u32 x;
|
||||
u32 y;
|
||||
u32 width;
|
||||
u32 height;
|
||||
};
|
||||
|
||||
// Specification equivalent: struct virtio_gpu_resp_display_info
|
||||
struct DisplayInfoResponse {
|
||||
ControlHeader header;
|
||||
// Specification equivalent: struct virtio_gpu_display_one
|
||||
struct Display {
|
||||
Rect rect;
|
||||
u32 enabled;
|
||||
u32 flags;
|
||||
} scanout_modes[VIRTIO_GPU_MAX_SCANOUTS];
|
||||
};
|
||||
|
||||
// Specification equivalent: enum virtio_gpu_formats
|
||||
enum class TextureFormat : u32 {
|
||||
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,
|
||||
};
|
||||
|
||||
// Specification equivalent: struct virtio_gpu_resource_create_2d
|
||||
struct ResourceCreate2D {
|
||||
ControlHeader header;
|
||||
u32 resource_id;
|
||||
u32 format;
|
||||
u32 width;
|
||||
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
|
||||
struct ResourceUnref {
|
||||
ControlHeader header;
|
||||
u32 resource_id;
|
||||
u32 padding;
|
||||
};
|
||||
|
||||
// Specification equivalent: struct virtio_gpu_set_scanout
|
||||
struct SetScanOut {
|
||||
ControlHeader header;
|
||||
Rect rect;
|
||||
u32 scanout_id;
|
||||
u32 resource_id;
|
||||
};
|
||||
|
||||
// Specification equivalent: struct virtio_gpu_mem_entry
|
||||
struct MemoryEntry {
|
||||
u64 address;
|
||||
u32 length;
|
||||
u32 padding;
|
||||
};
|
||||
|
||||
// Specification equivalent: struct virtio_gpu_resource_attach_backing
|
||||
struct ResourceAttachBacking {
|
||||
ControlHeader header;
|
||||
u32 resource_id;
|
||||
u32 num_entries;
|
||||
};
|
||||
|
||||
// Specification equivalent: struct virtio_gpu_resource_detach_backing
|
||||
struct ResourceDetachBacking {
|
||||
ControlHeader header;
|
||||
u32 resource_id;
|
||||
u32 padding;
|
||||
};
|
||||
|
||||
// Specification equivalent: struct virtio_gpu_transfer_to_host_2d
|
||||
struct TransferToHost2D {
|
||||
ControlHeader header;
|
||||
Rect rect;
|
||||
u64 offset;
|
||||
u32 resource_id;
|
||||
u32 padding;
|
||||
};
|
||||
|
||||
// Specification equivalent: struct virtio_gpu_resource_flush
|
||||
struct ResourceFlush {
|
||||
ControlHeader header;
|
||||
Rect rect;
|
||||
u32 resource_id;
|
||||
u32 padding;
|
||||
};
|
||||
|
||||
// Specification equivalent: struct virtio_gpu_get_edid
|
||||
struct GetEDID {
|
||||
ControlHeader header;
|
||||
u32 scanout_id;
|
||||
u32 padding;
|
||||
};
|
||||
|
||||
// Specification equivalent: struct virtio_gpu_resp_edid
|
||||
struct GetEDIDResponse {
|
||||
ControlHeader header;
|
||||
u32 size;
|
||||
u32 padding;
|
||||
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;
|
||||
u32 padding;
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue