diff options
author | Liav A <liavalb@gmail.com> | 2022-04-30 13:56:29 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-05-05 20:55:57 +0200 |
commit | c27c414ed1728a16b7ee3c1ebf1353127a700569 (patch) | |
tree | ead2735f9b9797a87f03c28d3213b6ec69f1ac3b /Kernel | |
parent | 4ff6150f1b760b0674f32b44bbc58e5c2971ffa4 (diff) | |
download | serenity-c27c414ed1728a16b7ee3c1ebf1353127a700569.zip |
Kernel/Graphics: Apply DisplayConnector design on the VirtIO driver
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Kernel/Graphics/DisplayConnector.cpp | 8 | ||||
-rw-r--r-- | Kernel/Graphics/GraphicsManagement.cpp | 2 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/Console.cpp | 42 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/Console.h | 8 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/DisplayConnector.cpp | 365 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/DisplayConnector.h | 123 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp | 402 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/FramebufferDevice.h | 110 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/GPU3DDevice.cpp | 38 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/GPU3DDevice.h | 26 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp | 344 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h | 145 |
13 files changed, 729 insertions, 886 deletions
diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 24c02e621c..c2418966b6 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -89,7 +89,7 @@ set(KERNEL_SOURCES Graphics/Intel/NativeGraphicsAdapter.cpp Graphics/VGA/ISAAdapter.cpp Graphics/VGA/PCIAdapter.cpp - Graphics/VirtIOGPU/FramebufferDevice.cpp + Graphics/VirtIOGPU/DisplayConnector.cpp Graphics/VirtIOGPU/Console.cpp Graphics/VirtIOGPU/GPU3DDevice.cpp Graphics/VirtIOGPU/GraphicsAdapter.cpp diff --git a/Kernel/Graphics/DisplayConnector.cpp b/Kernel/Graphics/DisplayConnector.cpp index facf9b7ea9..44409baecb 100644 --- a/Kernel/Graphics/DisplayConnector.cpp +++ b/Kernel/Graphics/DisplayConnector.cpp @@ -306,8 +306,9 @@ ErrorOr<void> DisplayConnector::ioctl(OpenFileDescription&, unsigned request, Us TRY(copy_from_user(&user_dirty_rect, &flush_rects.rects[i])); { SpinlockLocker control_locker(m_control_lock); - if (console_mode()) + if (console_mode()) { return {}; + } TRY(flush_rectangle(flush_rects.buffer_index, user_dirty_rect)); } } @@ -319,10 +320,13 @@ ErrorOr<void> DisplayConnector::ioctl(OpenFileDescription&, unsigned request, Us // WindowServer is not ready yet to handle errors such as EBUSY currently. MutexLocker locker(m_flushing_lock); SpinlockLocker control_locker(m_control_lock); - if (console_mode()) + if (console_mode()) { return {}; + } + if (!flush_support()) return Error::from_errno(ENOTSUP); + TRY(flush_first_surface()); return {}; } diff --git a/Kernel/Graphics/GraphicsManagement.cpp b/Kernel/Graphics/GraphicsManagement.cpp index d5fb78d185..f8c526f496 100644 --- a/Kernel/Graphics/GraphicsManagement.cpp +++ b/Kernel/Graphics/GraphicsManagement.cpp @@ -198,7 +198,7 @@ UNMAP_AFTER_INIT bool GraphicsManagement::determine_and_initialize_graphics_devi break; case PCI::VendorID::VirtIO: dmesgln("Graphics: Using VirtIO console"); - adapter = Graphics::VirtIOGPU::GraphicsAdapter::initialize(device_identifier); + adapter = VirtIOGraphicsAdapter::initialize(device_identifier); break; default: if (!is_vga_compatible_pci_device(device_identifier)) diff --git a/Kernel/Graphics/VirtIOGPU/Console.cpp b/Kernel/Graphics/VirtIOGPU/Console.cpp index 15f97206d1..234fd2ba47 100644 --- a/Kernel/Graphics/VirtIOGPU/Console.cpp +++ b/Kernel/Graphics/VirtIOGPU/Console.cpp @@ -5,7 +5,6 @@ */ #include <Kernel/Graphics/VirtIOGPU/Console.h> -#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> #include <Kernel/WorkQueue.h> namespace Kernel::Graphics::VirtIOGPU { @@ -30,22 +29,22 @@ void DirtyRect::union_rect(size_t x, size_t y, size_t width, size_t height) } } -NonnullRefPtr<Console> Console::initialize(RefPtr<FramebufferDevice> const& framebuffer_device) +NonnullRefPtr<Console> Console::initialize(VirtIODisplayConnector& parent_display_connector) { - return adopt_ref(*new Console(framebuffer_device)); + auto current_resolution = parent_display_connector.current_mode_setting(); + return adopt_ref(*new Console(parent_display_connector, current_resolution)); } -Console::Console(RefPtr<FramebufferDevice> const& framebuffer_device) - : GenericFramebufferConsole(framebuffer_device->width(), framebuffer_device->height(), framebuffer_device->pitch()) - , m_framebuffer_device(framebuffer_device) +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) { enqueue_refresh_timer(); } -void Console::set_resolution(size_t width, size_t height, size_t pitch) +void Console::set_resolution(size_t, size_t, size_t) { - auto did_set_resolution = m_framebuffer_device->set_head_resolution(0, width, height, pitch); - VERIFY(!did_set_resolution.is_error()); + // FIXME: Update some values here? } void Console::flush(size_t x, size_t y, size_t width, size_t height) @@ -58,16 +57,12 @@ void Console::enqueue_refresh_timer() NonnullRefPtr<Timer> refresh_timer = adopt_ref(*new Timer()); refresh_timer->setup(CLOCK_MONOTONIC, refresh_interval, [this]() { auto rect = m_dirty_rect; - if (rect.is_dirty()) { - Protocol::Rect dirty_rect { - .x = (u32)rect.x(), - .y = (u32)rect.y(), - .width = (u32)rect.width(), - .height = (u32)rect.height(), - }; - // FIXME: Do something sanely here if we can't allocate a work queue? - MUST(g_io_work->try_queue([this, dirty_rect]() { - m_framebuffer_device->flush_dirty_window(dirty_rect, m_framebuffer_device->current_buffer()); + if (m_enabled.load() && rect.is_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_rect.clear(); })); } @@ -78,16 +73,17 @@ void Console::enqueue_refresh_timer() void Console::enable() { + auto current_resolution = m_parent_display_connector->current_mode_setting(); GenericFramebufferConsole::enable(); - m_width = m_framebuffer_device->width(); - m_height = m_framebuffer_device->height(); - m_pitch = m_framebuffer_device->pitch(); + m_width = current_resolution.horizontal_active; + m_height = current_resolution.vertical_active; + m_pitch = current_resolution.horizontal_stride; m_dirty_rect.union_rect(0, 0, m_width, m_height); } u8* Console::framebuffer_data() { - return m_framebuffer_device->framebuffer_data(); + return m_parent_display_connector->framebuffer_data(); } } diff --git a/Kernel/Graphics/VirtIOGPU/Console.h b/Kernel/Graphics/VirtIOGPU/Console.h index d046663205..ab9d9076b6 100644 --- a/Kernel/Graphics/VirtIOGPU/Console.h +++ b/Kernel/Graphics/VirtIOGPU/Console.h @@ -7,7 +7,7 @@ #pragma once #include <Kernel/Graphics/Console/GenericFramebufferConsole.h> -#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> +#include <Kernel/Graphics/VirtIOGPU/DisplayConnector.h> #include <Kernel/TimerQueue.h> namespace Kernel::Graphics::VirtIOGPU { @@ -32,7 +32,7 @@ private: class Console final : public GenericFramebufferConsole { public: - static NonnullRefPtr<Console> initialize(RefPtr<FramebufferDevice> const&); + static NonnullRefPtr<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; @@ -42,8 +42,8 @@ private: void enqueue_refresh_timer(); virtual u8* framebuffer_data() override; - Console(RefPtr<FramebufferDevice> const&); - RefPtr<FramebufferDevice> m_framebuffer_device; + Console(VirtIODisplayConnector const& parent_display_connector, DisplayConnector::ModeSetting current_resolution); + NonnullRefPtr<VirtIODisplayConnector> m_parent_display_connector; DirtyRect m_dirty_rect; }; diff --git a/Kernel/Graphics/VirtIOGPU/DisplayConnector.cpp b/Kernel/Graphics/VirtIOGPU/DisplayConnector.cpp new file mode 100644 index 0000000000..c9086f92ec --- /dev/null +++ b/Kernel/Graphics/VirtIOGPU/DisplayConnector.cpp @@ -0,0 +1,365 @@ +/* + * 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/Graphics/GraphicsManagement.h> +#include <Kernel/Graphics/VirtIOGPU/Console.h> +#include <Kernel/Graphics/VirtIOGPU/DisplayConnector.h> +#include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h> +#include <Kernel/Graphics/VirtIOGPU/Protocol.h> +#include <Kernel/Random.h> + +namespace Kernel { + +NonnullRefPtr<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(); + connector->initialize_console(); + return connector; +} + +VirtIODisplayConnector::VirtIODisplayConnector(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id) + : DisplayConnector() + , m_graphics_adapter(graphics_adapter) + , m_scanout_id(scanout_id) +{ +} + +void VirtIODisplayConnector::initialize_console() +{ + m_console = Kernel::Graphics::VirtIOGPU::Console::initialize(*this); +} + +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(create_framebuffer()); + 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; + 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 y) +{ + VERIFY(m_control_lock.is_locked()); + if (y == 0) + m_current_buffer = &m_main_buffer; + else if (y == m_display_info.rect.height) + m_current_buffer = &m_back_buffer; + else + return Error::from_errno(EINVAL); + return {}; +} +ErrorOr<void> VirtIODisplayConnector::unblank() +{ + return Error::from_errno(ENOTIMPL); +} + +ErrorOr<size_t> VirtIODisplayConnector::write_to_first_surface(u64 offset, UserOrKernelBuffer const& buffer, size_t length) +{ + VERIFY(m_control_lock.is_locked()); + if (offset + length > (m_buffer_size * 2)) + return Error::from_errno(EOVERFLOW); + if (offset < m_buffer_size && (offset + length) > (m_buffer_size)) + return Error::from_errno(EOVERFLOW); + if (offset < m_buffer_size) { + TRY(buffer.read(m_main_buffer.framebuffer_data + offset, 0, length)); + } else { + TRY(buffer.read(m_back_buffer.framebuffer_data + offset - m_buffer_size, 0, length)); + } + + return length; +} + +ErrorOr<void> VirtIODisplayConnector::flush_rectangle(size_t buffer_index, FBRect const& rect) +{ + VERIFY(m_flushing_lock.is_locked()); + 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 + }; + + auto& buffer = buffer_from_index(buffer_index); + transfer_framebuffer_data_to_host(dirty_rect, buffer); + if (&buffer == m_current_buffer) { + // Flushing directly to screen + flush_displayed_image(dirty_rect, buffer); + buffer.dirty_rect = {}; + } else { + 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; + } + } + 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 + }; + auto& buffer = buffer_from_index(0); + transfer_framebuffer_data_to_host(dirty_rect, buffer); + if (&buffer == m_current_buffer) { + // Flushing directly to screen + flush_displayed_image(dirty_rect, buffer); + buffer.dirty_rect = {}; + } else { + 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; + } + } + 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::clear_to_black(Buffer& buffer) +{ + size_t width = m_display_info.rect.width; + size_t height = m_display_info.rect.height; + u8* data = buffer.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; + } +} + +void VirtIODisplayConnector::draw_ntsc_test_pattern(Buffer& buffer) +{ + constexpr u8 colors[12][4] = { + { 0xff, 0xff, 0xff, 0xff }, // White + { 0x00, 0xff, 0xff, 0xff }, // Primary + Composite colors + { 0xff, 0xff, 0x00, 0xff }, + { 0x00, 0xff, 0x00, 0xff }, + { 0xff, 0x00, 0xff, 0xff }, + { 0x00, 0x00, 0xff, 0xff }, + { 0xff, 0x00, 0x00, 0xff }, + { 0xba, 0x01, 0x5f, 0xff }, // Dark blue + { 0x8d, 0x3d, 0x00, 0xff }, // Purple + { 0x22, 0x22, 0x22, 0xff }, // Shades of gray + { 0x10, 0x10, 0x10, 0xff }, + { 0x00, 0x00, 0x00, 0xff }, + }; + size_t width = m_display_info.rect.width; + size_t height = m_display_info.rect.height; + u8* data = buffer.framebuffer_data; + // Draw NTSC test card + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + size_t color = 0; + if (3 * y < 2 * height) { + // Top 2/3 of image is 7 vertical stripes of color spectrum + color = (7 * x) / width; + } else if (4 * y < 3 * height) { + // 2/3 mark to 3/4 mark is backwards color spectrum alternating with black + auto segment = (7 * x) / width; + color = segment % 2 ? 10 : 6 - segment; + } else { + if (28 * x < 5 * width) { + color = 8; + } else if (28 * x < 10 * width) { + color = 0; + } else if (28 * x < 15 * width) { + color = 7; + } else if (28 * x < 20 * width) { + color = 10; + } else if (7 * x < 6 * width) { + // Grayscale gradient + color = 26 - ((21 * x) / width); + } else { + // Solid black + color = 10; + } + } + u8* pixel = &data[4 * (y * width + x)]; + for (int i = 0; i < 4; ++i) { + pixel[i] = colors[color][i]; + } + } + } + dbgln_if(VIRTIO_DEBUG, "Finish drawing the pattern"); +} + +u8* VirtIODisplayConnector::framebuffer_data() +{ + return m_current_buffer->framebuffer_data; +} + +ErrorOr<void> VirtIODisplayConnector::create_framebuffer() +{ + SpinlockLocker locker(m_graphics_adapter->operation_lock()); + // First delete any existing framebuffers to free the memory first + m_framebuffer = nullptr; + m_framebuffer_sink_vmobject = nullptr; + + // Allocate frame buffer for both front and back + m_buffer_size = calculate_framebuffer_size(m_display_info.rect.width, m_display_info.rect.height); + auto region_name = TRY(KString::formatted("VirtGPU FrameBuffer #{}", m_scanout_id.value())); + m_framebuffer = TRY(MM.allocate_kernel_region(m_buffer_size * 2, region_name->view(), Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow)); + auto write_sink_page = TRY(MM.allocate_user_physical_page(Memory::MemoryManager::ShouldZeroFill::No)); + auto num_needed_pages = m_framebuffer->vmobject().page_count(); + + NonnullRefPtrVector<Memory::PhysicalPage> pages; + for (auto i = 0u; i < num_needed_pages; ++i) { + TRY(pages.try_append(write_sink_page)); + } + m_framebuffer_sink_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_physical_pages(pages.span())); + + m_current_buffer = &buffer_from_index(m_last_set_buffer_index.load()); + create_buffer(m_main_buffer, 0, m_buffer_size); + create_buffer(m_back_buffer, m_buffer_size, m_buffer_size); + + return {}; +} + +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>) +{ + return m_display_info; +} + +void VirtIODisplayConnector::create_buffer(Buffer& buffer, size_t framebuffer_offset, size_t framebuffer_size) +{ + VERIFY(m_graphics_adapter->operation_lock().is_locked()); + buffer.framebuffer_offset = framebuffer_offset; + buffer.framebuffer_data = m_framebuffer->vaddr().as_ptr() + framebuffer_offset; + + // 1. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D + if (buffer.resource_id.value() != 0) + m_graphics_adapter->delete_resource(buffer.resource_id); + buffer.resource_id = m_graphics_adapter->create_2d_resource(m_display_info.rect); + + // 2. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING + m_graphics_adapter->ensure_backing_storage(buffer.resource_id, *m_framebuffer, buffer.framebuffer_offset, framebuffer_size); + // 3. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout. + if (&buffer == m_current_buffer) + m_graphics_adapter->set_scanout_resource(m_scanout_id, buffer.resource_id, m_display_info.rect); + // 4. Render our test pattern + draw_ntsc_test_pattern(buffer); + // 5. Use VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D to update the host resource from guest memory. + transfer_framebuffer_data_to_host(m_display_info.rect, buffer); + // 6. Use VIRTIO_GPU_CMD_RESOURCE_FLUSH to flush the updated resource to the display. + if (&buffer == m_current_buffer) + flush_displayed_image(m_display_info.rect, buffer); + + // 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, m_display_info.rect.x + m_display_info.rect.width) - buffer.dirty_rect.x; + buffer.dirty_rect.height = min(dirty_bottom, m_display_info.rect.y + m_display_info.rect.height) - buffer.dirty_rect.y; + } + + m_display_info.enabled = 1; +} + +void VirtIODisplayConnector::transfer_framebuffer_data_to_host(Graphics::VirtIOGPU::Protocol::Rect const& rect, Buffer& buffer) +{ + m_graphics_adapter->transfer_framebuffer_data_to_host(m_scanout_id, buffer.resource_id, rect); +} + +void VirtIODisplayConnector::flush_dirty_window(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, Buffer& buffer) +{ + m_graphics_adapter->flush_dirty_rectangle(m_scanout_id, buffer.resource_id, dirty_rect); +} + +void VirtIODisplayConnector::flush_displayed_image(Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect, Buffer& buffer) +{ + m_graphics_adapter->flush_displayed_image(buffer.resource_id, dirty_rect); +} + +} diff --git a/Kernel/Graphics/VirtIOGPU/DisplayConnector.h b/Kernel/Graphics/VirtIOGPU/DisplayConnector.h new file mode 100644 index 0000000000..1a3b462b63 --- /dev/null +++ b/Kernel/Graphics/VirtIOGPU/DisplayConnector.h @@ -0,0 +1,123 @@ +/* + * 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/Graphics/Console/Console.h> +#include <Kernel/Graphics/DisplayConnector.h> +#include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h> +#include <Kernel/Graphics/VirtIOGPU/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; + +private: + struct Buffer { + size_t framebuffer_offset { 0 }; + u8* framebuffer_data { nullptr }; + Graphics::VirtIOGPU::Protocol::Rect dirty_rect {}; + Graphics::VirtIOGPU::ResourceID resource_id { 0 }; + }; + +public: + static NonnullRefPtr<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::Protocol::DisplayInfoResponse::Display display_information(Badge<VirtIOGraphicsAdapter>); + +private: + void initialize_console(); + virtual bool mutable_mode_setting_capable() const override { return true; } + virtual bool double_framebuffering_capable() const override { return true; } + 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<size_t> write_to_first_surface(u64 offset, UserOrKernelBuffer const&, size_t length) override; + 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; + +private: + VirtIODisplayConnector(VirtIOGraphicsAdapter& graphics_adapter, Graphics::VirtIOGPU::ScanoutID scanout_id); + + void query_display_information(); + ErrorOr<void> query_edid_from_virtio_adapter(); + void query_display_edid(); + + void flush_dirty_window(Graphics::VirtIOGPU::Protocol::Rect const&, Buffer&); + void transfer_framebuffer_data_to_host(Graphics::VirtIOGPU::Protocol::Rect const&, Buffer&); + void flush_displayed_image(Graphics::VirtIOGPU::Protocol::Rect const&, Buffer&); + + // Basic 2D framebuffer methods + static size_t 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).value(); + } + u8* framebuffer_data(); + void draw_ntsc_test_pattern(Buffer&); + void clear_to_black(Buffer&); + ErrorOr<void> create_framebuffer(); + void create_buffer(Buffer&, size_t, size_t); + void set_buffer(int); + static bool is_valid_buffer_index(int buffer_index) + { + return buffer_index == 0 || buffer_index == 1; + } + Buffer& buffer_from_index(int buffer_index) + { + return buffer_index == 0 ? m_main_buffer : m_back_buffer; + } + Buffer& current_buffer() const { return *m_current_buffer; } + + // Member data + // Context used for kernel operations (e.g. flushing resources to scanout) + Graphics::VirtIOGPU::ContextID m_kernel_context_id; + + NonnullRefPtr<VirtIOGraphicsAdapter> m_graphics_adapter; + RefPtr<Graphics::Console> m_console; + Graphics::VirtIOGPU::Protocol::DisplayInfoResponse::Display m_display_info {}; + Graphics::VirtIOGPU::ScanoutID m_scanout_id; + + // 2D framebuffer Member data + size_t m_buffer_size { 0 }; + Buffer* m_current_buffer { nullptr }; + Atomic<int, AK::memory_order_relaxed> m_last_set_buffer_index { 0 }; + Buffer m_main_buffer; + Buffer m_back_buffer; + OwnPtr<Memory::Region> m_framebuffer; + RefPtr<Memory::VMObject> m_framebuffer_sink_vmobject; + + constexpr static size_t NUM_TRANSFER_REGION_PAGES = 256; +}; + +} diff --git a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp b/Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp deleted file mode 100644 index 8a0de5ca66..0000000000 --- a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.cpp +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com> - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include <Kernel/Graphics/GraphicsManagement.h> -#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> -#include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h> -#include <LibC/sys/ioctl_numbers.h> - -namespace Kernel::Graphics::VirtIOGPU { - -RefPtr<GraphicsAdapter> FramebufferDevice::adapter() const -{ - auto adapter = m_graphics_adapter.strong_ref(); - // FIXME: Propagate error gracefully - VERIFY(adapter); - return static_cast<GraphicsAdapter&>(*adapter); -} - -ErrorOr<size_t> FramebufferDevice::buffer_length(size_t head) const -{ - // Note: This FramebufferDevice class doesn't support multihead setup. - // We take care to verify this at the GenericFramebufferDevice::ioctl method - // so if we happen to accidentally have a value different than 0, assert. - VERIFY(head == 0); - SpinlockLocker locker(m_resolution_lock); - return display_info().rect.width * display_info().rect.height * 4; -} -ErrorOr<size_t> FramebufferDevice::pitch(size_t head) const -{ - // Note: This FramebufferDevice class doesn't support multihead setup. - // We take care to verify this at the GenericFramebufferDevice::ioctl method - // so if we happen to accidentally have a value different than 0, assert. - VERIFY(head == 0); - SpinlockLocker locker(m_resolution_lock); - return display_info().rect.width * 4; -} -ErrorOr<size_t> FramebufferDevice::height(size_t head) const -{ - // Note: This FramebufferDevice class doesn't support multihead setup. - // We take care to verify this at the GenericFramebufferDevice::ioctl method - // so if we happen to accidentally have a value different than 0, assert. - VERIFY(head == 0); - SpinlockLocker locker(m_resolution_lock); - return display_info().rect.height; -} -ErrorOr<size_t> FramebufferDevice::width(size_t head) const -{ - // Note: This FramebufferDevice class doesn't support multihead setup. - // We take care to verify this at the GenericFramebufferDevice::ioctl method - // so if we happen to accidentally have a value different than 0, assert. - VERIFY(head == 0); - SpinlockLocker locker(m_resolution_lock); - return display_info().rect.width; -} -ErrorOr<size_t> FramebufferDevice::vertical_offset(size_t head) const -{ - // Note: This FramebufferDevice class doesn't support multihead setup. - // We take care to verify this at the GenericFramebufferDevice::ioctl method - // so if we happen to accidentally have a value different than 0, assert. - VERIFY(head == 0); - return 0; -} -ErrorOr<bool> FramebufferDevice::vertical_offsetted(size_t head) const -{ - // Note: This FramebufferDevice class doesn't support multihead setup. - // We take care to verify this at the GenericFramebufferDevice::ioctl method - // so if we happen to accidentally have a value different than 0, assert. - VERIFY(head == 0); - return false; -} - -ErrorOr<void> FramebufferDevice::set_head_resolution(size_t head, size_t width, size_t height, size_t) -{ - // Note: This class doesn't support multihead setup (yet!). - // We take care to verify this at the GenericFramebufferDevice::ioctl method - // so if we happen to accidentally have a value different than 0, assert. - VERIFY(head == 0); - if (width > MAX_VIRTIOGPU_RESOLUTION_WIDTH || height > MAX_VIRTIOGPU_RESOLUTION_HEIGHT) - return Error::from_errno(ENOTSUP); - - auto& info = display_info(); - - info.rect = { - .x = 0, - .y = 0, - .width = (u32)width, - .height = (u32)height, - }; - - // FIXME: Would be nice to be able to return ErrorOr here. - TRY(create_framebuffer()); - return {}; -} -ErrorOr<void> FramebufferDevice::set_head_buffer(size_t, bool) -{ - return Error::from_errno(ENOTSUP); -} -ErrorOr<void> FramebufferDevice::flush_head_buffer(size_t) -{ - // Note: This class doesn't support flushing. - // We take care to verify this at the GenericFramebufferDevice::ioctl method - // so if we happen to accidentally reach this code, assert. - VERIFY_NOT_REACHED(); -} -ErrorOr<void> FramebufferDevice::flush_rectangle(size_t buffer_index, FBRect const& rect) -{ - MutexLocker locker(adapter()->operation_lock()); - Protocol::Rect dirty_rect { - .x = rect.x, - .y = rect.y, - .width = rect.width, - .height = rect.height - }; - // FIXME: Find a better ErrorOr<void> here. - if (!m_are_writes_active) - return Error::from_errno(EIO); - auto& buffer = buffer_from_index(buffer_index); - transfer_framebuffer_data_to_host(dirty_rect, buffer); - if (&buffer == m_current_buffer) { - // Flushing directly to screen - flush_displayed_image(dirty_rect, buffer); - buffer.dirty_rect = {}; - } else { - 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; - } - } - return {}; -} - -ErrorOr<ByteBuffer> FramebufferDevice::get_edid(size_t head) const -{ - // Note: This FramebufferDevice class doesn't support multihead setup. - // We take care to verify this at the GenericFramebufferDevice::ioctl method - // so if we happen to accidentally have a value different than 0, assert. - VERIFY(head == 0); - return adapter()->get_edid(m_scanout.value()); -} - -FramebufferDevice::FramebufferDevice(GraphicsAdapter const& adapter, ScanoutID scanout) - : GenericFramebufferDevice(adapter) - , m_scanout(scanout) -{ - if (display_info().enabled) { - // FIXME: This should be in a place where we can handle allocation failures. - auto result = create_framebuffer(); - VERIFY(!result.is_error()); - } -} - -FramebufferDevice::~FramebufferDevice() = default; - -ErrorOr<void> FramebufferDevice::create_framebuffer() -{ - MutexLocker locker(adapter()->operation_lock()); - // First delete any existing framebuffers to free the memory first - m_framebuffer = nullptr; - m_framebuffer_sink_vmobject = nullptr; - - // Allocate frame buffer for both front and back - auto& info = display_info(); - m_buffer_size = calculate_framebuffer_size(info.rect.width, info.rect.height); - auto region_name = TRY(KString::formatted("VirtGPU FrameBuffer #{}", m_scanout.value())); - m_framebuffer = TRY(MM.allocate_kernel_region(m_buffer_size * 2, region_name->view(), Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow)); - auto write_sink_page = TRY(MM.allocate_user_physical_page(Memory::MemoryManager::ShouldZeroFill::No)); - auto num_needed_pages = m_framebuffer->vmobject().page_count(); - - NonnullRefPtrVector<Memory::PhysicalPage> pages; - for (auto i = 0u; i < num_needed_pages; ++i) { - TRY(pages.try_append(write_sink_page)); - } - m_framebuffer_sink_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_physical_pages(pages.span())); - - m_current_buffer = &buffer_from_index(m_last_set_buffer_index.load()); - create_buffer(m_main_buffer, 0, m_buffer_size); - create_buffer(m_back_buffer, m_buffer_size, m_buffer_size); - - return {}; -} - -void FramebufferDevice::create_buffer(Buffer& buffer, size_t framebuffer_offset, size_t framebuffer_size) -{ - buffer.framebuffer_offset = framebuffer_offset; - buffer.framebuffer_data = m_framebuffer->vaddr().as_ptr() + framebuffer_offset; - - auto& info = display_info(); - - // 1. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D - if (buffer.resource_id.value() != 0) - adapter()->delete_resource(buffer.resource_id); - buffer.resource_id = adapter()->create_2d_resource(info.rect); - - // 2. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING - adapter()->ensure_backing_storage(buffer.resource_id, *m_framebuffer, buffer.framebuffer_offset, framebuffer_size); - // 3. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout. - if (&buffer == m_current_buffer) - adapter()->set_scanout_resource(m_scanout.value(), buffer.resource_id, info.rect); - // 4. Render our test pattern - draw_ntsc_test_pattern(buffer); - // 5. Use VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D to update the host resource from guest memory. - transfer_framebuffer_data_to_host(info.rect, buffer); - // 6. Use VIRTIO_GPU_CMD_RESOURCE_FLUSH to flush the updated resource to the display. - if (&buffer == m_current_buffer) - flush_displayed_image(info.rect, buffer); - - // 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, info.rect.x + info.rect.width) - buffer.dirty_rect.x; - buffer.dirty_rect.height = min(dirty_bottom, info.rect.y + info.rect.height) - buffer.dirty_rect.y; - } - - info.enabled = 1; -} - -Protocol::DisplayInfoResponse::Display const& FramebufferDevice::display_info() const -{ - return adapter()->display_info(m_scanout); -} - -Protocol::DisplayInfoResponse::Display& FramebufferDevice::display_info() -{ - return adapter()->display_info(m_scanout); -} - -void FramebufferDevice::transfer_framebuffer_data_to_host(Protocol::Rect const& rect, Buffer& buffer) -{ - adapter()->transfer_framebuffer_data_to_host(m_scanout, buffer.resource_id, rect); -} - -void FramebufferDevice::flush_dirty_window(Protocol::Rect const& dirty_rect, Buffer& buffer) -{ - adapter()->flush_dirty_rectangle(m_scanout, buffer.resource_id, dirty_rect); -} - -void FramebufferDevice::flush_displayed_image(Protocol::Rect const& dirty_rect, Buffer& buffer) -{ - adapter()->flush_displayed_image(buffer.resource_id, dirty_rect); -} - -void FramebufferDevice::set_buffer(int buffer_index) -{ - auto& buffer = buffer_index == 0 ? m_main_buffer : m_back_buffer; - MutexLocker locker(adapter()->operation_lock()); - if (&buffer == m_current_buffer) - return; - m_current_buffer = &buffer; - adapter()->set_scanout_resource(m_scanout.value(), buffer.resource_id, display_info().rect); - adapter()->flush_displayed_image(buffer.resource_id, buffer.dirty_rect); // QEMU SDL backend requires this (as per spec) - buffer.dirty_rect = {}; -} - -ErrorOr<Memory::Region*> FramebufferDevice::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared) -{ - TRY(process.require_promise(Pledge::video)); - if (!shared) - return ENODEV; - if (offset != 0 || !m_framebuffer) - return ENXIO; - if (range.size() > m_framebuffer->size()) - return EOVERFLOW; - - // We only allow one process to map the region - if (m_userspace_mmap_region) - return ENOMEM; - - RefPtr<Memory::VMObject> vmobject; - if (m_are_writes_active) { - vmobject = TRY(m_framebuffer->vmobject().try_clone()); - } else { - vmobject = m_framebuffer_sink_vmobject; - if (vmobject.is_null()) - return ENOMEM; - } - - m_userspace_mmap_region = TRY(process.address_space().allocate_region_with_vmobject( - range, - vmobject.release_nonnull(), - 0, - "VirtIOGPU Framebuffer", - prot, - shared)); - - return m_userspace_mmap_region.unsafe_ptr(); -} - -void FramebufferDevice::deactivate_writes() -{ - m_are_writes_active = false; - if (m_userspace_mmap_region) { - auto* region = m_userspace_mmap_region.unsafe_ptr(); - auto maybe_vm_object = m_framebuffer_sink_vmobject->try_clone(); - // FIXME: Would be nice to be able to return a ErrorOr<void> here. - VERIFY(!maybe_vm_object.is_error()); - region->set_vmobject(maybe_vm_object.release_value()); - region->remap(); - } - set_buffer(0); - clear_to_black(buffer_from_index(0)); -} - -void FramebufferDevice::activate_writes() -{ - m_are_writes_active = true; - auto last_set_buffer_index = m_last_set_buffer_index.load(); - if (m_userspace_mmap_region) { - auto* region = m_userspace_mmap_region.unsafe_ptr(); - region->set_vmobject(m_framebuffer->vmobject()); - region->remap(); - } - set_buffer(last_set_buffer_index); -} - -void FramebufferDevice::clear_to_black(Buffer& buffer) -{ - auto& info = display_info(); - size_t width = info.rect.width; - size_t height = info.rect.height; - u8* data = buffer.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; - } -} - -void FramebufferDevice::draw_ntsc_test_pattern(Buffer& buffer) -{ - constexpr u8 colors[12][4] = { - { 0xff, 0xff, 0xff, 0xff }, // White - { 0x00, 0xff, 0xff, 0xff }, // Primary + Composite colors - { 0xff, 0xff, 0x00, 0xff }, - { 0x00, 0xff, 0x00, 0xff }, - { 0xff, 0x00, 0xff, 0xff }, - { 0x00, 0x00, 0xff, 0xff }, - { 0xff, 0x00, 0x00, 0xff }, - { 0xba, 0x01, 0x5f, 0xff }, // Dark blue - { 0x8d, 0x3d, 0x00, 0xff }, // Purple - { 0x22, 0x22, 0x22, 0xff }, // Shades of gray - { 0x10, 0x10, 0x10, 0xff }, - { 0x00, 0x00, 0x00, 0xff }, - }; - auto& info = display_info(); - size_t width = info.rect.width; - size_t height = info.rect.height; - u8* data = buffer.framebuffer_data; - // Draw NTSC test card - for (size_t y = 0; y < height; ++y) { - for (size_t x = 0; x < width; ++x) { - size_t color = 0; - if (3 * y < 2 * height) { - // Top 2/3 of image is 7 vertical stripes of color spectrum - color = (7 * x) / width; - } else if (4 * y < 3 * height) { - // 2/3 mark to 3/4 mark is backwards color spectrum alternating with black - auto segment = (7 * x) / width; - color = segment % 2 ? 10 : 6 - segment; - } else { - if (28 * x < 5 * width) { - color = 8; - } else if (28 * x < 10 * width) { - color = 0; - } else if (28 * x < 15 * width) { - color = 7; - } else if (28 * x < 20 * width) { - color = 10; - } else if (7 * x < 6 * width) { - // Grayscale gradient - color = 26 - ((21 * x) / width); - } else { - // Solid black - color = 10; - } - } - u8* pixel = &data[4 * (y * width + x)]; - for (int i = 0; i < 4; ++i) { - pixel[i] = colors[color][i]; - } - } - } - dbgln_if(VIRTIO_DEBUG, "Finish drawing the pattern"); -} - -u8* FramebufferDevice::framebuffer_data() -{ - return m_current_buffer->framebuffer_data; -} - -} diff --git a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.h b/Kernel/Graphics/VirtIOGPU/FramebufferDevice.h deleted file mode 100644 index 1aed6582b9..0000000000 --- a/Kernel/Graphics/VirtIOGPU/FramebufferDevice.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2021, Sahan Fernando <sahan.h.fernando@gmail.com> - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include <Kernel/Bus/VirtIO/Device.h> -#include <Kernel/Bus/VirtIO/Queue.h> -#include <Kernel/Graphics/FramebufferDevice.h> -#include <Kernel/Graphics/GenericGraphicsAdapter.h> -#include <Kernel/Graphics/VirtIOGPU/Protocol.h> - -namespace Kernel::Graphics::VirtIOGPU { - -class GraphicsAdapter; -class FramebufferDevice final : public GenericFramebufferDevice { - friend class Console; - struct Buffer { - size_t framebuffer_offset { 0 }; - u8* framebuffer_data { nullptr }; - Protocol::Rect dirty_rect {}; - ResourceID resource_id { 0 }; - }; - -public: - FramebufferDevice(GraphicsAdapter const&, ScanoutID); - virtual ~FramebufferDevice() override; - - virtual ErrorOr<void> try_to_initialize() override { return {}; } - - virtual void deactivate_writes() override; - virtual void activate_writes() override; - - size_t width() const { return display_info().rect.width; } - size_t height() const { return display_info().rect.height; } - size_t pitch() const { return display_info().rect.width * 4; } - - static size_t 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).value(); - } - - u8* framebuffer_data(); - -private: - virtual bool multihead_support() const override { return false; } - virtual bool flushing_support() const override { return false; } - virtual bool partial_flushing_support() const override { return true; } - virtual size_t heads_count() const override { return 1; } - virtual ErrorOr<size_t> buffer_length(size_t head) const override; - virtual ErrorOr<size_t> pitch(size_t head) const override; - virtual ErrorOr<size_t> height(size_t head) const override; - virtual ErrorOr<size_t> width(size_t head) const override; - virtual ErrorOr<size_t> vertical_offset(size_t head) const override; - virtual ErrorOr<bool> vertical_offsetted(size_t head) const override; - - virtual ErrorOr<void> set_head_resolution(size_t head, size_t width, size_t height, size_t pitch) override; - virtual ErrorOr<void> set_head_buffer(size_t head, bool second_buffer) override; - virtual ErrorOr<void> flush_head_buffer(size_t head) override; - virtual ErrorOr<void> flush_rectangle(size_t head, FBRect const&) override; - - virtual ErrorOr<ByteBuffer> get_edid(size_t head) const override; - - void flush_dirty_window(Protocol::Rect const&, Buffer&); - void transfer_framebuffer_data_to_host(Protocol::Rect const&, Buffer&); - void flush_displayed_image(Protocol::Rect const&, Buffer&); - - void draw_ntsc_test_pattern(Buffer&); - - Protocol::DisplayInfoResponse::Display const& display_info() const; - Protocol::DisplayInfoResponse::Display& display_info(); - - void clear_to_black(Buffer&); - - ErrorOr<void> create_framebuffer(); - void create_buffer(Buffer&, size_t, size_t); - void set_buffer(int); - - virtual ErrorOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override; - - static bool is_valid_buffer_index(int buffer_index) - { - return buffer_index == 0 || buffer_index == 1; - } - - Buffer& buffer_from_index(int buffer_index) - { - return buffer_index == 0 ? m_main_buffer : m_back_buffer; - } - Buffer& current_buffer() const { return *m_current_buffer; } - - RefPtr<GraphicsAdapter> adapter() const; - - const ScanoutID m_scanout; - Buffer* m_current_buffer { nullptr }; - Atomic<int, AK::memory_order_relaxed> m_last_set_buffer_index { 0 }; - Buffer m_main_buffer; - Buffer m_back_buffer; - OwnPtr<Memory::Region> m_framebuffer; - RefPtr<Memory::VMObject> m_framebuffer_sink_vmobject; - size_t m_buffer_size { 0 }; - bool m_are_writes_active { true }; - // FIXME: This needs to be cleaned up if the WindowServer exits while we are in a tty - WeakPtr<Memory::Region> m_userspace_mmap_region; -}; - -} diff --git a/Kernel/Graphics/VirtIOGPU/GPU3DDevice.cpp b/Kernel/Graphics/VirtIOGPU/GPU3DDevice.cpp index 00caaa725b..7405c3ecd7 100644 --- a/Kernel/Graphics/VirtIOGPU/GPU3DDevice.cpp +++ b/Kernel/Graphics/VirtIOGPU/GPU3DDevice.cpp @@ -13,15 +13,15 @@ #include <Kernel/Random.h> #include <LibC/sys/ioctl_numbers.h> -namespace Kernel::Graphics::VirtIOGPU { +namespace Kernel { -GPU3DDevice::PerContextState::PerContextState(ContextID context_id, OwnPtr<Memory::Region> transfer_buffer_region) +VirtIOGPU3DDevice::PerContextState::PerContextState(Graphics::VirtIOGPU::ContextID context_id, OwnPtr<Memory::Region> transfer_buffer_region) : m_context_id(context_id) , m_transfer_buffer_region(move(transfer_buffer_region)) { } -NonnullRefPtr<GPU3DDevice> GPU3DDevice::must_create(GraphicsAdapter& adapter) +NonnullRefPtr<VirtIOGPU3DDevice> VirtIOGPU3DDevice::must_create(VirtIOGraphicsAdapter const& adapter) { // Setup memory transfer region auto region_result = MM.allocate_kernel_region( @@ -30,25 +30,25 @@ NonnullRefPtr<GPU3DDevice> GPU3DDevice::must_create(GraphicsAdapter& adapter) Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow); VERIFY(!region_result.is_error()); - auto device = MUST(DeviceManagement::try_create_device<VirtIOGPU::GPU3DDevice>(adapter, region_result.release_value())); + auto device = MUST(DeviceManagement::try_create_device<VirtIOGPU3DDevice>(adapter, region_result.release_value())); return device; } -GPU3DDevice::GPU3DDevice(GraphicsAdapter& graphics_adapter, NonnullOwnPtr<Memory::Region> transfer_buffer_region) +VirtIOGPU3DDevice::VirtIOGPU3DDevice(VirtIOGraphicsAdapter const& graphics_adapter, NonnullOwnPtr<Memory::Region> transfer_buffer_region) : CharacterDevice(28, 0) , m_graphics_adapter(graphics_adapter) , m_transfer_buffer_region(move(transfer_buffer_region)) { - m_kernel_context_id = m_graphics_adapter.create_context(); + m_kernel_context_id = m_graphics_adapter->create_context(); } -void GPU3DDevice::detach(OpenFileDescription& description) +void VirtIOGPU3DDevice::detach(OpenFileDescription& description) { m_context_state_lookup.remove(&description); CharacterDevice::detach(description); } -ErrorOr<RefPtr<GPU3DDevice::PerContextState>> GPU3DDevice::get_context_for_description(OpenFileDescription& description) +ErrorOr<RefPtr<VirtIOGPU3DDevice::PerContextState>> VirtIOGPU3DDevice::get_context_for_description(OpenFileDescription& description) { auto res = m_context_state_lookup.get(&description); if (!res.has_value()) @@ -56,16 +56,16 @@ ErrorOr<RefPtr<GPU3DDevice::PerContextState>> GPU3DDevice::get_context_for_descr return res.value(); } -ErrorOr<void> GPU3DDevice::ioctl(OpenFileDescription& description, unsigned request, Userspace<void*> arg) +ErrorOr<void> VirtIOGPU3DDevice::ioctl(OpenFileDescription& description, unsigned request, Userspace<void*> arg) { // TODO: We really should have ioctls for destroying resources as well switch (request) { case VIRGL_IOCTL_CREATE_CONTEXT: { if (m_context_state_lookup.contains(&description)) return EEXIST; - MutexLocker locker(m_graphics_adapter.operation_lock()); + 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 = m_graphics_adapter.create_context(); + auto context_id = m_graphics_adapter->create_context(); RefPtr<PerContextState> per_context_state = TRY(PerContextState::try_create(context_id)); TRY(m_context_state_lookup.try_set(&description, per_context_state)); return {}; @@ -92,10 +92,10 @@ ErrorOr<void> GPU3DDevice::ioctl(OpenFileDescription& description, unsigned requ } case VIRGL_IOCTL_SUBMIT_CMD: { auto context_id = TRY(get_context_for_description(description))->context_id(); - MutexLocker locker(m_graphics_adapter.operation_lock()); + SpinlockLocker 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(context_id, [&](Bytes buffer) { + 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)); @@ -108,8 +108,8 @@ ErrorOr<void> GPU3DDevice::ioctl(OpenFileDescription& description, unsigned requ 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), + 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, @@ -121,10 +121,10 @@ ErrorOr<void> GPU3DDevice::ioctl(OpenFileDescription& description, unsigned requ .flags = spec.flags, .padding = 0, }; - 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, per_context_state->context_id()); - m_graphics_adapter.ensure_backing_storage(resource_id, per_context_state->transfer_buffer_region(), 0, NUM_TRANSFER_REGION_PAGES * PAGE_SIZE); + SpinlockLocker 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, per_context_state->context_id()); + m_graphics_adapter->ensure_backing_storage(resource_id, per_context_state->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); diff --git a/Kernel/Graphics/VirtIOGPU/GPU3DDevice.h b/Kernel/Graphics/VirtIOGPU/GPU3DDevice.h index 5ad653f085..45f6cf8411 100644 --- a/Kernel/Graphics/VirtIOGPU/GPU3DDevice.h +++ b/Kernel/Graphics/VirtIOGPU/GPU3DDevice.h @@ -9,7 +9,6 @@ #include <AK/DistinctNumeric.h> #include <Kernel/Devices/CharacterDevice.h> #include <Kernel/Devices/DeviceManagement.h> -#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> #include <Kernel/Graphics/VirtIOGPU/Protocol.h> namespace Kernel::Graphics::VirtIOGPU { @@ -87,18 +86,23 @@ union ClearType { u32 value; }; -class GPU3DDevice : public CharacterDevice { - friend class Kernel::DeviceManagement; +} + +namespace Kernel { + +class VirtIOGraphicsAdapter; +class VirtIOGPU3DDevice : public CharacterDevice { + friend class DeviceManagement; public: - static NonnullRefPtr<GPU3DDevice> must_create(GraphicsAdapter&); + static NonnullRefPtr<VirtIOGPU3DDevice> must_create(VirtIOGraphicsAdapter const&); private: - GPU3DDevice(GraphicsAdapter& graphics_adapter, NonnullOwnPtr<Memory::Region> transfer_buffer_region); + VirtIOGPU3DDevice(VirtIOGraphicsAdapter const& graphics_adapter, NonnullOwnPtr<Memory::Region> transfer_buffer_region); class PerContextState : public RefCounted<PerContextState> { public: - static ErrorOr<RefPtr<PerContextState>> try_create(ContextID context_id) + static ErrorOr<RefPtr<PerContextState>> try_create(Graphics::VirtIOGPU::ContextID context_id) { auto region_result = TRY(MM.allocate_kernel_region( NUM_TRANSFER_REGION_PAGES * PAGE_SIZE, @@ -107,13 +111,13 @@ private: AllocationStrategy::AllocateNow)); return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PerContextState(context_id, move(region_result)))); } - ContextID context_id() { return m_context_id; } + Graphics::VirtIOGPU::ContextID context_id() { return m_context_id; } Memory::Region& transfer_buffer_region() { return *m_transfer_buffer_region; } private: PerContextState() = delete; - explicit PerContextState(ContextID context_id, OwnPtr<Memory::Region> transfer_buffer_region); - ContextID m_context_id; + explicit PerContextState(Graphics::VirtIOGPU::ContextID context_id, OwnPtr<Memory::Region> transfer_buffer_region); + Graphics::VirtIOGPU::ContextID m_context_id; OwnPtr<Memory::Region> m_transfer_buffer_region; }; @@ -129,9 +133,9 @@ private: private: ErrorOr<RefPtr<PerContextState>> get_context_for_description(OpenFileDescription&); - Kernel::Graphics::VirtIOGPU::GraphicsAdapter& m_graphics_adapter; + NonnullRefPtr<VirtIOGraphicsAdapter> m_graphics_adapter; // Context used for kernel operations (e.g. flushing resources to scanout) - ContextID m_kernel_context_id; + Graphics::VirtIOGPU::ContextID m_kernel_context_id; HashMap<OpenFileDescription*, RefPtr<PerContextState>> m_context_state_lookup; // Memory management for backing buffers NonnullOwnPtr<Memory::Region> m_transfer_buffer_region; diff --git a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp index 485c18c04b..1748304484 100644 --- a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp +++ b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.cpp @@ -11,67 +11,52 @@ #include <Kernel/Graphics/Console/GenericFramebufferConsole.h> #include <Kernel/Graphics/GraphicsManagement.h> #include <Kernel/Graphics/VirtIOGPU/Console.h> -#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> +#include <Kernel/Graphics/VirtIOGPU/DisplayConnector.h> #include <Kernel/Graphics/VirtIOGPU/GPU3DDevice.h> #include <Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h> -namespace Kernel::Graphics::VirtIOGPU { +namespace Kernel { #define DEVICE_EVENTS_READ 0x0 #define DEVICE_EVENTS_CLEAR 0x4 #define DEVICE_NUM_SCANOUTS 0x8 -NonnullRefPtr<GraphicsAdapter> GraphicsAdapter::initialize(PCI::DeviceIdentifier const& device_identifier) +NonnullRefPtr<VirtIOGraphicsAdapter> VirtIOGraphicsAdapter::initialize(PCI::DeviceIdentifier const& device_identifier) { VERIFY(device_identifier.hardware_id().vendor_id == PCI::VendorID::VirtIO); - auto adapter = adopt_ref(*new GraphicsAdapter(device_identifier)); + // Setup memory transfer region + auto scratch_space_region = MUST(MM.allocate_contiguous_kernel_region( + 32 * PAGE_SIZE, + "VirtGPU Scratch Space", + Memory::Region::Access::ReadWrite)); + + auto adapter = adopt_ref(*new (nothrow) VirtIOGraphicsAdapter(device_identifier, move(scratch_space_region))); adapter->initialize(); + MUST(adapter->initialize_adapter()); return adapter; } -GraphicsAdapter::GraphicsAdapter(PCI::DeviceIdentifier const& device_identifier) - : VirtIO::Device(device_identifier) -{ - auto region_or_error = MM.allocate_contiguous_kernel_region(32 * PAGE_SIZE, "VirtGPU Scratch Space", Memory::Region::Access::ReadWrite); - if (region_or_error.is_error()) - TODO(); - m_scratch_space = region_or_error.release_value(); -} - -void GraphicsAdapter::initialize_framebuffer_devices() +ErrorOr<void> VirtIOGraphicsAdapter::initialize_adapter() { - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Initializing framebuffer devices"); - VERIFY(!m_created_framebuffer_devices); - create_framebuffer_devices(); - m_created_framebuffer_devices = true; - - GraphicsManagement::the().set_console(*default_console()); -} - -void GraphicsAdapter::enable_consoles() -{ - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Enabling consoles"); - for_each_framebuffer([&](auto& framebuffer, auto& console) { - framebuffer.deactivate_writes(); - console.enable(); - return IterationDecision::Continue; - }); + VERIFY(m_num_scanouts <= VIRTIO_GPU_MAX_SCANOUTS); + 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; + MUST(query_and_set_edid(index, *display_connector)); + display_connector->set_safe_mode_setting_after_initialization({}); + } + return {}; } -void GraphicsAdapter::disable_consoles() +VirtIOGraphicsAdapter::VirtIOGraphicsAdapter(PCI::DeviceIdentifier const& device_identifier, NonnullOwnPtr<Memory::Region> scratch_space_region) + : VirtIO::Device(device_identifier) + , m_scratch_space(move(scratch_space_region)) { - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Disabling consoles"); - for_each_framebuffer([&](auto& framebuffer, auto& console) { - console.disable(); - framebuffer.activate_writes(); - return IterationDecision::Continue; - }); } -void GraphicsAdapter::initialize() +void VirtIOGraphicsAdapter::initialize() { - Device::initialize(); - VERIFY(!!m_scratch_space); + VirtIO::Device::initialize(); if (auto* config = get_config(VirtIO::ConfigurationType::Device)) { m_device_configuration = config; bool success = negotiate_features([&](u64 supported_features) { @@ -94,27 +79,23 @@ void GraphicsAdapter::initialize() } VERIFY(success); finish_init(); - initialize_3d_device(); - MutexLocker locker(m_operation_lock); - // Get display information using VIRTIO_GPU_CMD_GET_DISPLAY_INFO - query_display_information(); - query_display_edid({}); } else { VERIFY_NOT_REACHED(); } } -void GraphicsAdapter::create_framebuffer_devices() +Graphics::VirtIOGPU::ResourceID VirtIOGraphicsAdapter::allocate_resource_id(Badge<VirtIODisplayConnector>) { - for (size_t i = 0; i < min(m_num_scanouts, VIRTIO_GPU_MAX_SCANOUTS); i++) { - auto& scanout = m_scanouts[i]; - scanout.framebuffer = adopt_ref(*new VirtIOGPU::FramebufferDevice(*this, i)); - scanout.framebuffer->after_inserting(); - scanout.console = Kernel::Graphics::VirtIOGPU::Console::initialize(scanout.framebuffer); - } + return m_resource_id_counter++; +} + +Graphics::VirtIOGPU::ContextID VirtIOGraphicsAdapter::allocate_context_id(Badge<VirtIODisplayConnector>) +{ + // FIXME: This should really be tracked using a bitmap, instead of an atomic counter + return m_context_id_counter++; } -bool GraphicsAdapter::handle_device_config_change() +bool VirtIOGraphicsAdapter::handle_device_config_change() { auto events = get_pending_events(); if (events & VIRTIO_GPU_EVENT_DISPLAY) { @@ -129,7 +110,7 @@ bool GraphicsAdapter::handle_device_config_change() return true; } -void GraphicsAdapter::handle_queue_update(u16 queue_index) +void VirtIOGraphicsAdapter::handle_queue_update(u16 queue_index) { dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Handle queue update"); VERIFY(queue_index == CONTROLQ); @@ -140,145 +121,75 @@ void GraphicsAdapter::handle_queue_update(u16 queue_index) m_outstanding_request.wake_all(); } -u32 GraphicsAdapter::get_pending_events() +u32 VirtIOGraphicsAdapter::get_pending_events() { return config_read32(*m_device_configuration, DEVICE_EVENTS_READ); } -void GraphicsAdapter::clear_pending_events(u32 event_bitmask) +void VirtIOGraphicsAdapter::clear_pending_events(u32 event_bitmask) { config_write32(*m_device_configuration, DEVICE_EVENTS_CLEAR, event_bitmask); } -void GraphicsAdapter::query_display_information() +ErrorOr<void> VirtIOGraphicsAdapter::query_and_set_edid(u32 scanout_id, VirtIODisplayConnector& display_connector) { - VERIFY(m_operation_lock.is_locked()); - auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::ControlHeader>(); - populate_virtio_gpu_request_header(request, Protocol::CommandType::VIRTIO_GPU_CMD_GET_DISPLAY_INFO, 0); - auto& response = writer.append_structure<Protocol::DisplayInfoResponse>(); - - synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - - for (size_t i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; ++i) { - auto& scanout = m_scanouts[i].display_info; - scanout = response.scanout_modes[i]; - dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Scanout {}: enabled: {} x: {}, y: {}, width: {}, height: {}", i, !!scanout.enabled, scanout.rect.x, scanout.rect.y, scanout.rect.width, scanout.rect.height); - if (scanout.enabled && !m_default_scanout.has_value()) - m_default_scanout = i; - - m_scanouts[i].edid = {}; - } - VERIFY(m_default_scanout.has_value()); -} - -void GraphicsAdapter::query_display_edid(Optional<ScanoutID> scanout_id) -{ - VERIFY(m_operation_lock.is_locked()); - + SpinlockLocker locker(m_operation_lock); if (!is_feature_accepted(VIRTIO_GPU_F_EDID)) - return; - - for (size_t i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; ++i) { - if (scanout_id.has_value() && scanout_id.value() != i) - continue; - - // scanout.display_info.enabled doesn't seem to reflect the actual state, - // even if we were to call query_display_information prior to calling - // this function. So, just ignore, we seem to get EDID information regardless. - - auto query_edid_result = query_edid(i); - if (query_edid_result.is_error()) { - dbgln("VirtIO::GraphicsAdapater: Scanout {}: Failed to parse EDID: {}", i, query_edid_result.error()); - m_scanouts[i].edid = {}; - } else { - m_scanouts[i].edid = query_edid_result.release_value(); - if (m_scanouts[i].edid.has_value()) { - auto& parsed_edid = m_scanouts[i].edid.value(); - dbgln("VirtIO::GraphicsAdapater: Scanout {}: EDID {}: Manufacturer: {} Product: {} Serial #{}", i, - parsed_edid.version(), parsed_edid.legacy_manufacturer_id(), parsed_edid.product_code(), parsed_edid.serial_number()); - if (auto screen_size = parsed_edid.screen_size(); screen_size.has_value()) { - auto& size = screen_size.value(); - dbgln("VirtIO::GraphicsAdapater: Scanout {}: Screen size: {}cm x {}cm", i, - size.horizontal_cm(), size.vertical_cm()); - } else if (auto aspect_ratio = parsed_edid.aspect_ratio(); aspect_ratio.has_value()) { - auto& ratio = aspect_ratio.value(); - dbgln("VirtIO::GraphicsAdapater: Scanout {}: Aspect ratio: {} : 1", i, ratio.ratio()); - } else { - dbgln("VirtIO::GraphicsAdapater: Scanout {}: Unknown screen size or aspect ratio", i); - } - } else { - dbgln("VirtIO::GraphicsAdapater: Scanout {}: No EDID", i); - } - } - } -} + return Error::from_errno(ENOTSUP); -ErrorOr<ByteBuffer> GraphicsAdapter::get_edid(size_t output_port_index) const -{ - if (output_port_index >= VIRTIO_GPU_MAX_SCANOUTS) - return Error::from_errno(ENODEV); - auto& edid = m_scanouts[output_port_index].edid; - if (edid.has_value()) - return ByteBuffer::copy(edid.value().bytes()); - return ByteBuffer {}; -} - -auto GraphicsAdapter::query_edid(u32 scanout_id) -> ErrorOr<Optional<EDID::Parser>> -{ - VERIFY(m_operation_lock.is_locked()); auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::GetEDID>(); - auto& response = writer.append_structure<Protocol::GetEDIDResponse>(); + 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, Protocol::CommandType::VIRTIO_GPU_CMD_GET_EDID, 0); + 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; synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - if (response.header.type != to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_EDID)) + if (response.header.type != to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_EDID)) return Error::from_string_literal("VirtIO::GraphicsAdapter: Failed to get EDID"); if (response.size == 0) return Error::from_string_literal("VirtIO::GraphicsAdapter: Failed to get EDID, empty buffer"); - auto edid_buffer = TRY(ByteBuffer::copy(response.edid, response.size)); - auto edid = TRY(EDID::Parser::from_bytes(move(edid_buffer))); - return edid; + 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 {}; } -ResourceID GraphicsAdapter::create_2d_resource(Protocol::Rect rect) +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<Protocol::ResourceCreate2D>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); + 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, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, 0); + 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(Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM); + request.format = to_underlying(Graphics::VirtIOGPU::Protocol::TextureFormat::VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM); synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + VERIFY(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; } -ResourceID GraphicsAdapter::create_3d_resource(Protocol::Resource3DSpecification const& resource_3d_specification) +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<Protocol::ResourceCreate3D>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); + 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, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_CREATE_3D, 0); + populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_CREATE_3D, 0); auto resource_id = allocate_resource_id(); request.resource_id = resource_id.value(); @@ -286,17 +197,17 @@ ResourceID GraphicsAdapter::create_3d_resource(Protocol::Resource3DSpecification 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(Protocol::ResourceCreate3D, target) == sizeof(resource_3d_specification))); + 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)); 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)); + VERIFY(response.type == static_cast<u32>(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; } -void GraphicsAdapter::ensure_backing_storage(ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length) +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()); @@ -306,100 +217,97 @@ void GraphicsAdapter::ensure_backing_storage(ResourceID resource_id, Memory::Reg size_t num_mem_regions = buffer_length / PAGE_SIZE; auto writer = create_scratchspace_writer(); - auto& request = writer.append_structure<Protocol::ResourceAttachBacking>(); - const size_t header_block_size = sizeof(request) + num_mem_regions * sizeof(Protocol::MemoryEntry); + 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, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, 0); + 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<Protocol::MemoryEntry>(); + 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<Protocol::ControlHeader>(); + auto& response = writer.append_structure<Graphics::VirtIOGPU::Protocol::ControlHeader>(); synchronous_virtio_gpu_command(start_of_scratch_space(), header_block_size, sizeof(response)); - VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + VERIFY(response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Allocated backing storage"); } -void GraphicsAdapter::detach_backing_storage(ResourceID resource_id) +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<Protocol::ResourceDetachBacking>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); + 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, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, 0); + populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, 0); 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)); + VERIFY(response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Detached backing storage"); } -void GraphicsAdapter::set_scanout_resource(ScanoutID scanout, ResourceID resource_id, Protocol::Rect rect) +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<Protocol::SetScanOut>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); + 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, Protocol::CommandType::VIRTIO_GPU_CMD_SET_SCANOUT, 0); + 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; synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + VERIFY(response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); dbgln_if(VIRTIO_DEBUG, "VirtIO::GraphicsAdapter: Set backing scanout"); } - - // Now that the Scanout should be enabled, update the EDID - query_display_edid(scanout); } -void GraphicsAdapter::transfer_framebuffer_data_to_host(ScanoutID scanout, ResourceID resource_id, Protocol::Rect const& dirty_rect) +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<Protocol::TransferToHost2D>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); + 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, Protocol::CommandType::VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, 0); - request.offset = (dirty_rect.x + (dirty_rect.y * m_scanouts[scanout.value()].display_info.rect.width)) * sizeof(u32); + 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; synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + VERIFY(response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); } -void GraphicsAdapter::flush_displayed_image(ResourceID resource_id, Protocol::Rect const& dirty_rect) +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<Protocol::ResourceFlush>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); + 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, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_FLUSH, 0); + 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; synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + VERIFY(response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); } -void GraphicsAdapter::synchronous_virtio_gpu_command(PhysicalAddress buffer_start, size_t request_size, size_t response_size) +void VirtIOGraphicsAdapter::synchronous_virtio_gpu_command(PhysicalAddress buffer_start, size_t request_size, size_t response_size) { VERIFY(m_operation_lock.is_locked()); VERIFY(m_outstanding_request.is_empty()); @@ -415,7 +323,7 @@ void GraphicsAdapter::synchronous_virtio_gpu_command(PhysicalAddress buffer_star m_outstanding_request.wait_forever(); } -void GraphicsAdapter::populate_virtio_gpu_request_header(Protocol::ControlHeader& header, Protocol::CommandType ctrl_type, u32 flags) +void VirtIOGraphicsAdapter::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; @@ -424,61 +332,57 @@ void GraphicsAdapter::populate_virtio_gpu_request_header(Protocol::ControlHeader header.padding = 0; } -void GraphicsAdapter::flush_dirty_rectangle(ScanoutID scanout_id, ResourceID resource_id, Protocol::Rect const& dirty_rect) +void VirtIOGraphicsAdapter::flush_dirty_rectangle(Graphics::VirtIOGPU::ScanoutID scanout_id, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect) { - MutexLocker locker(m_operation_lock); + VERIFY(m_operation_lock.is_locked()); transfer_framebuffer_data_to_host(scanout_id, resource_id, dirty_rect); flush_displayed_image(resource_id, dirty_rect); } -ResourceID GraphicsAdapter::allocate_resource_id() +Graphics::VirtIOGPU::ResourceID VirtIOGraphicsAdapter::allocate_resource_id() { - VERIFY(m_operation_lock.is_locked()); - m_resource_id_counter = m_resource_id_counter.value() + 1; - return m_resource_id_counter; + return m_resource_id_counter++; } -ContextID GraphicsAdapter::allocate_context_id() +Graphics::VirtIOGPU::ContextID VirtIOGraphicsAdapter::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; + return m_context_id_counter++; } -void GraphicsAdapter::delete_resource(ResourceID resource_id) +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<Protocol::ResourceUnref>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); + 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, Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_UNREF, 0); + populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_RESOURCE_UNREF, 0); 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)); + VERIFY(response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); } -void GraphicsAdapter::initialize_3d_device() +void VirtIOGraphicsAdapter::initialize_3d_device() { if (m_has_virgl_support) { - MutexLocker locker(m_operation_lock); - m_3d_device = VirtIOGPU::GPU3DDevice::must_create(*this); + SpinlockLocker locker(m_operation_lock); + m_3d_device = VirtIOGPU3DDevice::must_create(*this); } } -ContextID GraphicsAdapter::create_context() +Graphics::VirtIOGPU::ContextID VirtIOGraphicsAdapter::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>(); + 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, Protocol::CommandType::VIRTIO_GPU_CMD_CTX_CREATE, 0); + populate_virtio_gpu_request_header(request.header, Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_CMD_CTX_CREATE, 0); request.header.context_id = ctx_id.value(); request.name_length = strlen(region_name); memset(request.debug_name.data(), 0, 64); @@ -487,53 +391,53 @@ ContextID GraphicsAdapter::create_context() synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + VERIFY(response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); return ctx_id; } -void GraphicsAdapter::submit_command_buffer(ContextID context_id, Function<size_t(Bytes)> buffer_writer) +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<Protocol::CommandSubmit>(); + auto& request = writer.append_structure<Graphics::VirtIOGPU::Protocol::CommandSubmit>(); - populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_SUBMIT_3D, 0); + 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(Protocol::ControlHeader); + 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(Protocol::ControlHeader); + 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(Protocol::ControlHeader) != 0) { - VERIFY((writer.current_offset() % alignof(Protocol::ControlHeader)) % sizeof(u32) == 0); - writer.append_structure<u32>() = to_underlying(VirGLCommand::NOP); + 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<Protocol::ControlHeader>(); + auto& response = writer.append_structure<Graphics::VirtIOGPU::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)); + VERIFY(response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); } -void GraphicsAdapter::attach_resource_to_context(ResourceID resource_id, ContextID context_id) +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<Protocol::ContextAttachResource>(); - auto& response = writer.append_structure<Protocol::ControlHeader>(); - populate_virtio_gpu_request_header(request.header, Protocol::CommandType::VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, 0); + 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(); synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); - VERIFY(response.type == to_underlying(Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); + VERIFY(response.type == to_underlying(Graphics::VirtIOGPU::Protocol::CommandType::VIRTIO_GPU_RESP_OK_NODATA)); } } diff --git a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h index 579aafb69c..b0e756f27c 100644 --- a/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h +++ b/Kernel/Graphics/VirtIOGPU/GraphicsAdapter.h @@ -10,16 +10,10 @@ #include <AK/DistinctNumeric.h> #include <Kernel/Bus/VirtIO/Device.h> #include <Kernel/Bus/VirtIO/Queue.h> -#include <Kernel/Devices/BlockDevice.h> #include <Kernel/Graphics/GenericGraphicsAdapter.h> -#include <Kernel/Graphics/VirtIOGPU/Console.h> -#include <Kernel/Graphics/VirtIOGPU/FramebufferDevice.h> #include <Kernel/Graphics/VirtIOGPU/Protocol.h> -#include <LibEDID/EDID.h> -namespace Kernel::Graphics::VirtIOGPU { - -class GPU3DDevice; +namespace Kernel { #define VIRTIO_GPU_F_VIRGL (1 << 0) #define VIRTIO_GPU_F_EDID (1 << 1) @@ -34,16 +28,16 @@ class GPU3DDevice; #define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) -class FramebufferDevice; -class GraphicsAdapter final +class VirtIODisplayConnector; +class VirtIOGPU3DDevice; +class VirtIOGraphicsAdapter final : public GenericGraphicsAdapter , public VirtIO::Device { - friend class FramebufferDevice; + friend class VirtIODisplayConnector; + friend class VirtIOGPU3DDevice; public: - static NonnullRefPtr<GraphicsAdapter> initialize(PCI::DeviceIdentifier const&); - - virtual bool framebuffer_devices_initialized() const override { return m_created_framebuffer_devices; } + static NonnullRefPtr<VirtIOGraphicsAdapter> initialize(PCI::DeviceIdentifier const&); // FIXME: There's a VirtIO VGA GPU variant, so we should consider that virtual bool vga_compatible() const override { return false; } @@ -51,76 +45,46 @@ public: virtual void initialize() override; void initialize_3d_device(); - ErrorOr<ByteBuffer> get_edid(size_t output_port_index) const override; - -private: - void flush_dirty_rectangle(ScanoutID, ResourceID, Protocol::Rect const& dirty_rect); - - template<typename F> - IterationDecision for_each_framebuffer(F f) - { - for (auto& scanout : m_scanouts) { - if (!scanout.framebuffer) - continue; - IterationDecision decision = f(*scanout.framebuffer, *scanout.console); - if (decision != IterationDecision::Continue) - return decision; - } - return IterationDecision::Continue; - } - - RefPtr<Console> default_console() - { - if (m_default_scanout.has_value()) - return m_scanouts[m_default_scanout.value().value()].console; - return {}; - } - auto& display_info(ScanoutID scanout) const - { - VERIFY(scanout.value() < VIRTIO_GPU_MAX_SCANOUTS); - return m_scanouts[scanout.value()].display_info; - } - auto& display_info(ScanoutID scanout) - { - VERIFY(scanout.value() < VIRTIO_GPU_MAX_SCANOUTS); - return m_scanouts[scanout.value()].display_info; - } - - explicit GraphicsAdapter(PCI::DeviceIdentifier const&); - - void create_framebuffer_devices(); - - virtual void initialize_framebuffer_devices() override; - virtual void enable_consoles() override; - virtual void disable_consoles() override; + bool edid_feature_accepted() const; - virtual bool modesetting_capable() const override { return false; } - virtual bool double_framebuffering_capable() const override { return false; } - - virtual bool try_to_set_resolution(size_t, size_t, size_t) override { return false; } - virtual bool set_y_offset(size_t, size_t) override { return false; } + Graphics::VirtIOGPU::ResourceID allocate_resource_id(Badge<VirtIODisplayConnector>); + Graphics::VirtIOGPU::ContextID allocate_context_id(Badge<VirtIODisplayConnector>); +private: + void flush_dirty_rectangle(Graphics::VirtIOGPU::ScanoutID, Graphics::VirtIOGPU::ResourceID, Graphics::VirtIOGPU::Protocol::Rect const& dirty_rect); struct Scanout { - RefPtr<Graphics::VirtIOGPU::FramebufferDevice> framebuffer; - RefPtr<Console> console; - Protocol::DisplayInfoResponse::Display display_info {}; - Optional<EDID::Parser> edid; + RefPtr<VirtIODisplayConnector> display_connector; }; + VirtIOGraphicsAdapter(PCI::DeviceIdentifier const&, NonnullOwnPtr<Memory::Region> scratch_space_region); + + ErrorOr<void> initialize_adapter(); + // ^GenericGraphicsAdapter + // FIXME: Remove all of these methods when we get rid of the FramebufferDevice class. + virtual ErrorOr<ByteBuffer> get_edid(size_t) const override { VERIFY_NOT_REACHED(); } + virtual bool try_to_set_resolution(size_t, size_t, size_t) override { VERIFY_NOT_REACHED(); } + virtual bool set_y_offset(size_t, size_t) override { VERIFY_NOT_REACHED(); } + virtual void initialize_framebuffer_devices() override { } + virtual void enable_consoles() override { } + virtual void disable_consoles() override { } + virtual bool framebuffer_devices_initialized() const override { return false; } + virtual bool modesetting_capable() const override { return true; } + virtual bool double_framebuffering_capable() const override { return true; } + 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); // 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; } + Graphics::VirtIOGPU::ContextID create_context(); + void attach_resource_to_context(Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::ContextID context_id); + 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; } - ResourceID allocate_resource_id(); - ContextID allocate_context_id(); + Graphics::VirtIOGPU::ResourceID allocate_resource_id(); + Graphics::VirtIOGPU::ContextID allocate_context_id(); PhysicalAddress start_of_scratch_space() const { return m_scratch_space->physical_page(0)->paddr(); } AK::BinaryBufferWriter create_scratchspace_writer() @@ -128,36 +92,31 @@ private: return { Bytes(m_scratch_space->vaddr().as_ptr(), m_scratch_space->size()) }; } void synchronous_virtio_gpu_command(PhysicalAddress buffer_start, size_t request_size, size_t response_size); - void populate_virtio_gpu_request_header(Protocol::ControlHeader& header, Protocol::CommandType ctrl_type, u32 flags = 0); - - void query_display_information(); - void query_display_edid(Optional<ScanoutID>); - ResourceID create_2d_resource(Protocol::Rect rect); - ResourceID create_3d_resource(Protocol::Resource3DSpecification const& resource_3d_specification); - 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 detach_backing_storage(ResourceID resource_id); - void set_scanout_resource(ScanoutID scanout, ResourceID resource_id, Protocol::Rect rect); - void transfer_framebuffer_data_to_host(ScanoutID scanout, ResourceID resource_id, Protocol::Rect const& rect); - void flush_displayed_image(ResourceID resource_id, Protocol::Rect const& dirty_rect); - ErrorOr<Optional<EDID::Parser>> query_edid(u32 scanout_id); - - bool m_created_framebuffer_devices { false }; - Optional<ScanoutID> m_default_scanout; + void populate_virtio_gpu_request_header(Graphics::VirtIOGPU::Protocol::ControlHeader& header, Graphics::VirtIOGPU::Protocol::CommandType ctrl_type, u32 flags = 0); + + Graphics::VirtIOGPU::ResourceID create_2d_resource(Graphics::VirtIOGPU::Protocol::Rect rect); + Graphics::VirtIOGPU::ResourceID create_3d_resource(Graphics::VirtIOGPU::Protocol::Resource3DSpecification const& resource_3d_specification); + void delete_resource(Graphics::VirtIOGPU::ResourceID resource_id); + void ensure_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id, Memory::Region const& region, size_t buffer_offset, size_t buffer_length); + void detach_backing_storage(Graphics::VirtIOGPU::ResourceID resource_id); + void set_scanout_resource(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect rect); + void transfer_framebuffer_data_to_host(Graphics::VirtIOGPU::ScanoutID scanout, Graphics::VirtIOGPU::ResourceID resource_id, Graphics::VirtIOGPU::Protocol::Rect const& rect); + 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 }; - ResourceID m_resource_id_counter { 0 }; - ContextID m_context_id_counter { 0 }; - RefPtr<GPU3DDevice> m_3d_device; + // Note: Resource ID 0 is invalid, and we must not allocate 0 as the first resource ID. + Atomic<u32> m_resource_id_counter { 1 }; + Atomic<u32> m_context_id_counter { 1 }; + RefPtr<VirtIOGPU3DDevice> m_3d_device; bool m_has_virgl_support { false }; // Synchronous commands WaitQueue m_outstanding_request; - Mutex m_operation_lock; - OwnPtr<Memory::Region> m_scratch_space; - - friend class Kernel::Graphics::VirtIOGPU::GPU3DDevice; + Spinlock m_operation_lock; + NonnullOwnPtr<Memory::Region> m_scratch_space; }; } |