diff options
author | Tom <tomut@yahoo.com> | 2021-06-25 20:44:48 -0600 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-06-27 09:46:27 +0200 |
commit | 8749235046b5176a889ee7a45b8b4cbaa49fc851 (patch) | |
tree | d10206d7bd8994daa82ca208d83b11c56a2bb1d7 /Kernel | |
parent | 708f27ca0e9716026dc6efd0bce4567a12064825 (diff) | |
download | serenity-8749235046b5176a889ee7a45b8b4cbaa49fc851.zip |
Kernel: Add support for multiple VirtIO GPU outputs
This creates /dev/fbX devices for each physical output, owned by the
parent VirtIOGPU instance. This allows mapping and setting resolutions
independently for each output.
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.cpp | 170 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.h | 32 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/VirtIOGPU.cpp | 161 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/VirtIOGPU.h | 67 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.cpp | 23 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.h | 6 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.cpp | 31 | ||||
-rw-r--r-- | Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.h | 5 |
8 files changed, 303 insertions, 192 deletions
diff --git a/Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.cpp b/Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.cpp index b471e9e160..2729cdb6ff 100644 --- a/Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.cpp +++ b/Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.cpp @@ -10,12 +10,34 @@ namespace Kernel::Graphics { -VirtIOFrameBufferDevice::VirtIOFrameBufferDevice(RefPtr<VirtIOGPU> virtio_gpu) +VirtIOFrameBufferDevice::VirtIOFrameBufferDevice(VirtIOGPU& virtio_gpu, VirtIOGPUScanoutID scanout) : BlockDevice(29, GraphicsManagement::the().allocate_minor_device_number()) , m_gpu(virtio_gpu) + , m_scanout(scanout) { + auto& info = display_info(); + + Locker locker(m_gpu.operation_lock()); + + // 1. Allocate frame buffer + // FIXME: We really should be trying to allocate a small amount of pages initially, with ensure_backing_storage increasing the backing memory of the region as needed + size_t buffer_length = calculate_framebuffer_size(MAX_VIRTIOGPU_RESOLUTION_WIDTH, MAX_VIRTIOGPU_RESOLUTION_HEIGHT); + m_framebuffer = MM.allocate_kernel_region(page_round_up(buffer_length), String::formatted("VirtGPU FrameBuffer #{}", scanout.value()), Region::Access::Read | Region::Access::Write, AllocationStrategy::AllocateNow); + // 2. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D + m_resource_id = m_gpu.create_2d_resource(info.rect); + // 3. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING + m_gpu.ensure_backing_storage(*m_framebuffer, buffer_length, m_resource_id); + // 4. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout. + m_gpu.set_scanout_resource(m_scanout.value(), m_resource_id, info.rect); + // 5. Render our test pattern + draw_ntsc_test_pattern(); + // 6. Use VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D to update the host resource from guest memory. + transfer_framebuffer_data_to_host(info.rect); + // 7. Use VIRTIO_GPU_CMD_RESOURCE_FLUSH to flush the updated resource to the display. + flush_displayed_image(info.rect); + auto write_sink_page = MM.allocate_user_physical_page(MemoryManager::ShouldZeroFill::No).release_nonnull(); - auto num_needed_pages = m_gpu->framebuffer_vm_object().page_count(); + auto num_needed_pages = m_framebuffer->vmobject().page_count(); NonnullRefPtrVector<PhysicalPage> pages; for (auto i = 0u; i < num_needed_pages; ++i) { pages.append(write_sink_page); @@ -27,13 +49,66 @@ VirtIOFrameBufferDevice::~VirtIOFrameBufferDevice() { } +VirtIOGPURespDisplayInfo::VirtIOGPUDisplayOne const& VirtIOFrameBufferDevice::display_info() const +{ + return m_gpu.display_info(m_scanout); +} + +VirtIOGPURespDisplayInfo::VirtIOGPUDisplayOne& VirtIOFrameBufferDevice::display_info() +{ + return m_gpu.display_info(m_scanout); +} + +size_t VirtIOFrameBufferDevice::size_in_bytes() const +{ + auto& info = display_info(); + return info.rect.width * info.rect.height * sizeof(u32); +} + +void VirtIOFrameBufferDevice::flush_dirty_window(VirtIOGPURect const& dirty_rect) +{ + m_gpu.flush_dirty_window(m_scanout, dirty_rect, m_resource_id); +} + +void VirtIOFrameBufferDevice::transfer_framebuffer_data_to_host(VirtIOGPURect const& rect) +{ + m_gpu.transfer_framebuffer_data_to_host(m_scanout, rect, m_resource_id); +} + +void VirtIOFrameBufferDevice::flush_displayed_image(VirtIOGPURect const& dirty_rect) +{ + m_gpu.flush_displayed_image(dirty_rect, m_resource_id); +} + +bool VirtIOFrameBufferDevice::try_to_set_resolution(size_t width, size_t height) +{ + if (width > MAX_VIRTIOGPU_RESOLUTION_WIDTH || height > MAX_VIRTIOGPU_RESOLUTION_HEIGHT) + return false; + Locker locker(m_gpu.operation_lock()); + VirtIOGPURect rect = { + .x = 0, + .y = 0, + .width = (u32)width, + .height = (u32)height, + }; + auto old_resource_id = m_resource_id; + auto new_resource_id = m_gpu.create_2d_resource(rect); + m_gpu.ensure_backing_storage(*m_framebuffer, calculate_framebuffer_size(width, height), new_resource_id); + m_gpu.set_scanout_resource(m_scanout.value(), new_resource_id, rect); + m_gpu.detach_backing_storage(old_resource_id); + m_gpu.delete_resource(old_resource_id); + m_resource_id = new_resource_id; + display_info().rect = rect; + return true; +} + int VirtIOFrameBufferDevice::ioctl(FileDescription&, unsigned request, FlatPtr arg) { REQUIRE_PROMISE(video); switch (request) { case FB_IOCTL_GET_SIZE_IN_BYTES: { auto* out = (size_t*)arg; - size_t value = m_gpu->framebuffer_size_in_bytes(); + size_t value = size_in_bytes(); if (!copy_to_user(out, &value)) return -EFAULT; return 0; @@ -43,9 +118,9 @@ int VirtIOFrameBufferDevice::ioctl(FileDescription&, unsigned request, FlatPtr a FBResolution resolution; if (!copy_from_user(&resolution, user_resolution)) return -EFAULT; - if (!m_gpu->try_to_set_resolution(resolution.width, resolution.height)) + if (!try_to_set_resolution(resolution.width, resolution.height)) return -EINVAL; - resolution.pitch = m_gpu->framebuffer_pitch(); + resolution.pitch = pitch(); if (!copy_to_user(user_resolution, &resolution)) return -EFAULT; return 0; @@ -53,9 +128,9 @@ int VirtIOFrameBufferDevice::ioctl(FileDescription&, unsigned request, FlatPtr a case FB_IOCTL_GET_RESOLUTION: { auto* user_resolution = (FBResolution*)arg; FBResolution resolution; - resolution.pitch = m_gpu->framebuffer_pitch(); - resolution.width = m_gpu->framebuffer_width(); - resolution.height = m_gpu->framebuffer_height(); + resolution.pitch = pitch(); + resolution.width = width(); + resolution.height = height(); if (!copy_to_user(user_resolution, &resolution)) return -EFAULT; return 0; @@ -71,7 +146,7 @@ int VirtIOFrameBufferDevice::ioctl(FileDescription&, unsigned request, FlatPtr a .height = user_dirty_rect.height }; if (m_are_writes_active) - m_gpu->flush_dirty_window(dirty_rect); + flush_dirty_window(dirty_rect); return 0; } default: @@ -86,14 +161,14 @@ KResultOr<Region*> VirtIOFrameBufferDevice::mmap(Process& process, FileDescripti return ENODEV; if (offset != 0) return ENXIO; - if (range.size() != page_round_up(m_gpu->framebuffer_size_in_bytes())) + if (range.size() != page_round_up(size_in_bytes())) return EOVERFLOW; // We only allow one process to map the region if (m_userspace_mmap_region) return ENOMEM; - auto vmobject = m_are_writes_active ? m_gpu->framebuffer_vm_object().clone() : m_framebuffer_sink_vmobject; + auto vmobject = m_are_writes_active ? m_framebuffer->vmobject().clone() : m_framebuffer_sink_vmobject; if (vmobject.is_null()) return ENOMEM; @@ -127,9 +202,80 @@ void VirtIOFrameBufferDevice::activate_writes() m_are_writes_active = true; if (m_userspace_mmap_region) { auto* region = m_userspace_mmap_region.unsafe_ptr(); - region->set_vmobject(m_gpu->framebuffer_vm_object()); + region->set_vmobject(m_framebuffer->vmobject()); region->remap(); } } +void VirtIOFrameBufferDevice::clear_to_black() +{ + auto& info = display_info(); + size_t width = info.rect.width; + size_t height = info.rect.height; + u8* data = m_framebuffer->vaddr().as_ptr(); + 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 VirtIOFrameBufferDevice::draw_ntsc_test_pattern() +{ + static 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 = m_framebuffer->vaddr().as_ptr(); + // 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"); +} + } diff --git a/Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.h b/Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.h index f918bdbc69..d3b9a4c9f6 100644 --- a/Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.h +++ b/Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.h @@ -15,15 +15,40 @@ namespace Kernel::Graphics { class VirtIOFrameBufferDevice final : public BlockDevice { public: - VirtIOFrameBufferDevice(RefPtr<VirtIOGPU> virtio_gpu); + VirtIOFrameBufferDevice(VirtIOGPU& virtio_gpu, VirtIOGPUScanoutID); virtual ~VirtIOFrameBufferDevice() override; virtual void deactivate_writes(); virtual void activate_writes(); + bool try_to_set_resolution(size_t width, size_t height); + void clear_to_black(); + + VMObject& vm_object() { return m_framebuffer->vmobject(); } + Region& region() { return *m_framebuffer; } + + 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; } + + size_t size_in_bytes() const; + static size_t calculate_framebuffer_size(size_t width, size_t height) + { + return sizeof(u32) * width * height; + } + + void flush_dirty_window(VirtIOGPURect const&); + void transfer_framebuffer_data_to_host(VirtIOGPURect const&); + void flush_displayed_image(VirtIOGPURect const&); + + void draw_ntsc_test_pattern(); + private: virtual const char* class_name() const override { return "VirtIOFrameBuffer"; } + VirtIOGPURespDisplayInfo::VirtIOGPUDisplayOne const& display_info() const; + VirtIOGPURespDisplayInfo::VirtIOGPUDisplayOne& display_info(); + virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg) override; virtual KResultOr<Region*> mmap(Process&, FileDescription&, const Range&, u64 offset, int prot, bool shared) override; virtual bool can_read(const FileDescription&, size_t) const override { return true; } @@ -35,7 +60,10 @@ private: virtual mode_t required_mode() const override { return 0666; } virtual String device_name() const override { return String::formatted("fb{}", minor()); } - RefPtr<VirtIOGPU> m_gpu; + VirtIOGPU& m_gpu; + const VirtIOGPUScanoutID m_scanout; + VirtIOGPUResourceID m_resource_id { 0 }; + OwnPtr<Region> m_framebuffer; RefPtr<VMObject> m_framebuffer_sink_vmobject; bool m_are_writes_active { true }; // FIXME: This needs to be cleaned up if the WindowServer exits while we are in a tty diff --git a/Kernel/Graphics/VirtIOGPU/VirtIOGPU.cpp b/Kernel/Graphics/VirtIOGPU/VirtIOGPU.cpp index 9e7c3baa73..440d6d34c4 100644 --- a/Kernel/Graphics/VirtIOGPU/VirtIOGPU.cpp +++ b/Kernel/Graphics/VirtIOGPU/VirtIOGPU.cpp @@ -4,7 +4,9 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include <Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.h> #include <Kernel/Graphics/VirtIOGPU/VirtIOGPU.h> +#include <Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.h> #include <LibC/sys/ioctl_numbers.h> #define DEVICE_EVENTS_READ 0x0 @@ -38,23 +40,8 @@ VirtIOGPU::VirtIOGPU(PCI::Address address) VERIFY(success); finish_init(); Locker locker(m_operation_lock); - // 1. Get display information using VIRTIO_GPU_CMD_GET_DISPLAY_INFO + // Get display information using VIRTIO_GPU_CMD_GET_DISPLAY_INFO query_display_information(); - // 2. Create BUFFER using VIRTIO_GPU_CMD_RESOURCE_CREATE_2D - m_framebuffer_id = create_2d_resource(m_display_info.rect); - // 3. Attach backing storage using VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING - // FIXME: We really should be trying to allocate a small amount of pages initially, with ensure_backing_storage increasing the backing memory of the region as needed - size_t buffer_length = calculate_framebuffer_size(MAX_VIRTIOGPU_RESOLUTION_WIDTH, MAX_VIRTIOGPU_RESOLUTION_HEIGHT); - m_framebuffer = MM.allocate_kernel_region(page_round_up(buffer_length), "VirtGPU FrameBuffer", Region::Access::Read | Region::Access::Write, AllocationStrategy::AllocateNow); - ensure_backing_storage(*m_framebuffer, buffer_length, m_framebuffer_id); - // 4. Use VIRTIO_GPU_CMD_SET_SCANOUT to link the framebuffer to a display scanout. - set_scanout_resource(m_chosen_scanout.value(), m_framebuffer_id, m_display_info.rect); - // 5. Render our test pattern - draw_ntsc_test_pattern(); - // 6. 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); - // 7. Use VIRTIO_GPU_CMD_RESOURCE_FLUSH to flush the updated resource to the display. - flush_displayed_image(m_display_info.rect); } else { VERIFY_NOT_REACHED(); } @@ -64,6 +51,15 @@ VirtIOGPU::~VirtIOGPU() { } +void VirtIOGPU::create_framebuffer_devices() +{ + 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 VirtIOFrameBufferDevice(*this, i)); + scanout.console = Kernel::Graphics::VirtIOGPUConsole::initialize(scanout.framebuffer); + } +} + bool VirtIOGPU::handle_device_config_change() { return false; @@ -101,14 +97,19 @@ void VirtIOGPU::query_display_information() 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 = response.scanout_modes[i]; - if (!scanout.enabled) - continue; - dbgln_if(VIRTIO_DEBUG, "Scanout {}: x: {}, y: {}, width: {}, height: {}", i, scanout.rect.x, scanout.rect.y, scanout.rect.width, scanout.rect.height); - m_display_info = scanout; - m_chosen_scanout = i; + auto& scanout = m_scanouts[i].display_info; + scanout = response.scanout_modes[i]; + dbgln_if(VIRTIO_DEBUG, "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; + } else if (i < m_num_scanouts) { + // TODO: We should not enable all displays until the first resolution set + scanout.rect.width = 1024; + scanout.rect.height = 768; + scanout.enabled = true; + } } - VERIFY(m_chosen_scanout.has_value()); + VERIFY(m_default_scanout.has_value()); } VirtIOGPUResourceID VirtIOGPU::create_2d_resource(VirtIOGPURect rect) @@ -157,7 +158,7 @@ void VirtIOGPU::ensure_backing_storage(Region& region, size_t buffer_length, Vir request.resource_id = resource_id.value(); request.num_entries = num_mem_regions; for (size_t i = 0; i < num_mem_regions; ++i) { - request.entries[i].address = m_framebuffer->physical_page(i)->paddr().get(); + request.entries[i].address = region.physical_page(i)->paddr().get(); request.entries[i].length = PAGE_SIZE; } @@ -199,71 +200,15 @@ void VirtIOGPU::set_scanout_resource(VirtIOGPUScanoutID scanout, VirtIOGPUResour dbgln_if(VIRTIO_DEBUG, "VirtIOGPU: Set backing scanout"); } -void VirtIOGPU::draw_ntsc_test_pattern() -{ - static 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 = m_framebuffer->vaddr().as_ptr(); - // 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"); -} - -void VirtIOGPU::transfer_framebuffer_data_to_host(VirtIOGPURect dirty_rect) +void VirtIOGPU::transfer_framebuffer_data_to_host(VirtIOGPUScanoutID scanout, VirtIOGPURect const& dirty_rect, VirtIOGPUResourceID resource_id) { VERIFY(m_operation_lock.is_locked()); auto& request = *reinterpret_cast<VirtIOGPUTransferToHost2D*>(m_scratch_space->vaddr().as_ptr()); auto& response = *reinterpret_cast<VirtIOGPUCtrlHeader*>((m_scratch_space->vaddr().offset(sizeof(request)).as_ptr())); populate_virtio_gpu_request_header(request.header, VirtIOGPUCtrlType::VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, VIRTIO_GPU_FLAG_FENCE); - request.offset = (dirty_rect.x + (dirty_rect.y * m_display_info.rect.width)) * sizeof(u32); - request.resource_id = m_framebuffer_id.value(); + request.offset = (dirty_rect.x + (dirty_rect.y * m_scanouts[scanout.value()].display_info.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)); @@ -271,14 +216,14 @@ void VirtIOGPU::transfer_framebuffer_data_to_host(VirtIOGPURect dirty_rect) VERIFY(response.type == static_cast<u32>(VirtIOGPUCtrlType::VIRTIO_GPU_RESP_OK_NODATA)); } -void VirtIOGPU::flush_displayed_image(VirtIOGPURect dirty_rect) +void VirtIOGPU::flush_displayed_image(VirtIOGPURect const& dirty_rect, VirtIOGPUResourceID resource_id) { VERIFY(m_operation_lock.is_locked()); auto& request = *reinterpret_cast<VirtIOGPUResourceFlush*>(m_scratch_space->vaddr().as_ptr()); auto& response = *reinterpret_cast<VirtIOGPUCtrlHeader*>((m_scratch_space->vaddr().offset(sizeof(request)).as_ptr())); populate_virtio_gpu_request_header(request.header, VirtIOGPUCtrlType::VIRTIO_GPU_CMD_RESOURCE_FLUSH, VIRTIO_GPU_FLAG_FENCE); - request.resource_id = m_framebuffer_id.value(); + request.resource_id = resource_id.value(); request.rect = dirty_rect; synchronous_virtio_gpu_command(start_of_scratch_space(), sizeof(request), sizeof(response)); @@ -311,33 +256,11 @@ void VirtIOGPU::populate_virtio_gpu_request_header(VirtIOGPUCtrlHeader& header, header.padding = 0; } -void VirtIOGPU::flush_dirty_window(VirtIOGPURect dirty_rect) +void VirtIOGPU::flush_dirty_window(VirtIOGPUScanoutID scanout, VirtIOGPURect const& dirty_rect, VirtIOGPUResourceID resource_id) { Locker locker(m_operation_lock); - transfer_framebuffer_data_to_host(dirty_rect); - flush_displayed_image(dirty_rect); -} - -bool VirtIOGPU::try_to_set_resolution(size_t width, size_t height) -{ - if (width > MAX_VIRTIOGPU_RESOLUTION_WIDTH && height > MAX_VIRTIOGPU_RESOLUTION_HEIGHT) - return false; - Locker locker(m_operation_lock); - VirtIOGPURect rect = { - .x = 0, - .y = 0, - .width = (u32)width, - .height = (u32)height, - }; - auto old_framebuffer_id = m_framebuffer_id; - auto new_framebuffer_id = create_2d_resource(rect); - ensure_backing_storage(*m_framebuffer, calculate_framebuffer_size(width, height), new_framebuffer_id); - set_scanout_resource(m_chosen_scanout.value(), new_framebuffer_id, rect); - detach_backing_storage(old_framebuffer_id); - delete_resource(old_framebuffer_id); - m_framebuffer_id = new_framebuffer_id; - m_display_info.rect = rect; - return true; + transfer_framebuffer_data_to_host(scanout, dirty_rect, resource_id); + flush_displayed_image(dirty_rect, resource_id); } VirtIOGPUResourceID VirtIOGPU::allocate_resource_id() @@ -361,22 +284,4 @@ void VirtIOGPU::delete_resource(VirtIOGPUResourceID resource_id) VERIFY(response.type == static_cast<u32>(VirtIOGPUCtrlType::VIRTIO_GPU_RESP_OK_NODATA)); } -size_t VirtIOGPU::framebuffer_size_in_bytes() const -{ - return m_display_info.rect.width * m_display_info.rect.height * sizeof(u32); -} - -void VirtIOGPU::clear_to_black() -{ - size_t width = m_display_info.rect.width; - size_t height = m_display_info.rect.height; - u8* data = m_framebuffer->vaddr().as_ptr(); - 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; - } -} - } diff --git a/Kernel/Graphics/VirtIOGPU/VirtIOGPU.h b/Kernel/Graphics/VirtIOGPU/VirtIOGPU.h index c2e0e588ba..af8b6126c2 100644 --- a/Kernel/Graphics/VirtIOGPU/VirtIOGPU.h +++ b/Kernel/Graphics/VirtIOGPU/VirtIOGPU.h @@ -26,6 +26,9 @@ namespace Kernel::Graphics { +class VirtIOGPUConsole; +class VirtIOFrameBufferDevice; + TYPEDEF_DISTINCT_ORDERED_ID(u32, VirtIOGPUResourceID); TYPEDEF_DISTINCT_ORDERED_ID(u32, VirtIOGPUScanoutID); @@ -158,32 +161,54 @@ struct VirtIOGPUResourceFlush { class VirtIOGPU final : public VirtIODevice , public RefCounted<VirtIOGPU> { + friend class VirtIOFrameBufferDevice; + public: VirtIOGPU(PCI::Address); virtual ~VirtIOGPU() override; - bool try_to_set_resolution(size_t width, size_t height); - void clear_to_black(); + void flush_dirty_window(VirtIOGPUScanoutID, VirtIOGPURect const& dirty_rect, VirtIOGPUResourceID); - VMObject& framebuffer_vm_object() { return m_framebuffer->vmobject(); } - Region& framebuffer_region() { return *m_framebuffer; } + auto& display_info(VirtIOGPUScanoutID scanout) const + { + VERIFY(scanout.value() < VIRTIO_GPU_MAX_SCANOUTS); + return m_scanouts[scanout.value()].display_info; + } + auto& display_info(VirtIOGPUScanoutID scanout) + { + VERIFY(scanout.value() < VIRTIO_GPU_MAX_SCANOUTS); + return m_scanouts[scanout.value()].display_info; + } - size_t framebuffer_width() { return m_display_info.rect.width; } - size_t framebuffer_height() { return m_display_info.rect.height; } - size_t framebuffer_pitch() { return m_display_info.rect.width * 4; } + void create_framebuffer_devices(); - size_t framebuffer_size_in_bytes() const; - size_t calculate_framebuffer_size(size_t width, size_t height) const + template<typename F> + IterationDecision for_each_framebuffer(F f) { - return sizeof(u32) * width * height; + for (size_t i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { + auto& scanout = m_scanouts[i]; + if (!scanout.framebuffer) + continue; + IterationDecision decision = f(*scanout.framebuffer, *scanout.console); + if (decision != IterationDecision::Continue) + return decision; + } + return IterationDecision::Continue; } - void flush_dirty_window(VirtIOGPURect dirty_rect); + RefPtr<VirtIOGPUConsole> default_console() + { + if (m_default_scanout.has_value()) + return m_scanouts[m_default_scanout.value().value()].console; + return {}; + } private: virtual bool handle_device_config_change() override; virtual void handle_queue_update(u16 queue_index) override; + auto& operation_lock() { return m_operation_lock; } + u32 get_pending_events(); void clear_pending_events(u32 event_bitmask); @@ -198,16 +223,20 @@ private: void ensure_backing_storage(Region& region, size_t buffer_length, VirtIOGPUResourceID resource_id); void detach_backing_storage(VirtIOGPUResourceID resource_id); void set_scanout_resource(VirtIOGPUScanoutID scanout, VirtIOGPUResourceID resource_id, VirtIOGPURect rect); - void draw_ntsc_test_pattern(); - void transfer_framebuffer_data_to_host(VirtIOGPURect rect); - void flush_displayed_image(VirtIOGPURect dirty_rect); + void transfer_framebuffer_data_to_host(VirtIOGPUScanoutID scanout, VirtIOGPURect const& rect, VirtIOGPUResourceID resource_id); + void flush_displayed_image(VirtIOGPURect const& dirty_rect, VirtIOGPUResourceID resource_id); + + struct Scanout { + RefPtr<VirtIOFrameBufferDevice> framebuffer; + RefPtr<VirtIOGPUConsole> console; + VirtIOGPURespDisplayInfo::VirtIOGPUDisplayOne display_info {}; + }; + Optional<VirtIOGPUScanoutID> m_default_scanout; + size_t m_num_scanouts { 0 }; + Scanout m_scanouts[VIRTIO_GPU_MAX_SCANOUTS]; - VirtIOGPURespDisplayInfo::VirtIOGPUDisplayOne m_display_info; - Optional<VirtIOGPUScanoutID> m_chosen_scanout; - VirtIOGPUResourceID m_framebuffer_id { 0 }; Configuration const* m_device_configuration { nullptr }; - size_t m_num_scanouts { 0 }; - OwnPtr<Region> m_framebuffer; + VirtIOGPUResourceID m_resource_id_counter { 0 }; // Synchronous commands diff --git a/Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.cpp b/Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.cpp index ead2b8d580..f93d6bda4d 100644 --- a/Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.cpp +++ b/Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include <Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.h> #include <Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.h> #include <Kernel/WorkQueue.h> @@ -29,22 +30,22 @@ void DirtyRect::union_rect(size_t x, size_t y, size_t width, size_t height) } } -NonnullRefPtr<VirtIOGPUConsole> VirtIOGPUConsole::initialize(RefPtr<VirtIOGPU> gpu) +NonnullRefPtr<VirtIOGPUConsole> VirtIOGPUConsole::initialize(RefPtr<VirtIOFrameBufferDevice> const& framebuffer_device) { - return adopt_ref(*new VirtIOGPUConsole(gpu)); + return adopt_ref(*new VirtIOGPUConsole(framebuffer_device)); } -VirtIOGPUConsole::VirtIOGPUConsole(RefPtr<VirtIOGPU> gpu) - : GenericFramebufferConsole(gpu->framebuffer_width(), gpu->framebuffer_height(), gpu->framebuffer_pitch()) - , m_gpu(gpu) +VirtIOGPUConsole::VirtIOGPUConsole(RefPtr<VirtIOFrameBufferDevice> const& framebuffer_device) + : GenericFramebufferConsole(framebuffer_device->width(), framebuffer_device->height(), framebuffer_device->pitch()) + , m_framebuffer_device(framebuffer_device) { - m_framebuffer_region = gpu->framebuffer_region(); + m_framebuffer_region = m_framebuffer_device->region(); enqueue_refresh_timer(); } void VirtIOGPUConsole::set_resolution(size_t width, size_t height, size_t) { - auto did_set_resolution = m_gpu->try_to_set_resolution(width, height); + auto did_set_resolution = m_framebuffer_device->try_to_set_resolution(width, height); VERIFY(did_set_resolution); } @@ -66,7 +67,7 @@ void VirtIOGPUConsole::enqueue_refresh_timer() .height = (u32)rect.height(), }; g_io_work->queue([this, dirty_rect]() { - m_gpu->flush_dirty_window(dirty_rect); + m_framebuffer_device->flush_dirty_window(dirty_rect); m_dirty_rect.clear(); }); } @@ -78,9 +79,9 @@ void VirtIOGPUConsole::enqueue_refresh_timer() void VirtIOGPUConsole::enable() { GenericFramebufferConsole::enable(); - m_width = m_gpu->framebuffer_width(); - m_height = m_gpu->framebuffer_height(); - m_pitch = m_gpu->framebuffer_pitch(); + m_width = m_framebuffer_device->width(); + m_height = m_framebuffer_device->height(); + m_pitch = m_framebuffer_device->pitch(); m_dirty_rect.union_rect(0, 0, m_width, m_height); } diff --git a/Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.h b/Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.h index a3914b3709..03ec730956 100644 --- a/Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.h +++ b/Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.h @@ -32,7 +32,7 @@ private: class VirtIOGPUConsole final : public GenericFramebufferConsole { public: - static NonnullRefPtr<VirtIOGPUConsole> initialize(RefPtr<VirtIOGPU>); + static NonnullRefPtr<VirtIOGPUConsole> initialize(RefPtr<VirtIOFrameBufferDevice> const&); 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; @@ -45,9 +45,9 @@ private: return m_framebuffer_region.unsafe_ptr()->vaddr().as_ptr(); } - VirtIOGPUConsole(RefPtr<VirtIOGPU>); + VirtIOGPUConsole(RefPtr<VirtIOFrameBufferDevice> const&); WeakPtr<Region> m_framebuffer_region; - RefPtr<VirtIOGPU> m_gpu; + RefPtr<VirtIOFrameBufferDevice> m_framebuffer_device; DirtyRect m_dirty_rect; }; diff --git a/Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.cpp b/Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.cpp index bc7404847a..24b4b39024 100644 --- a/Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.cpp +++ b/Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.cpp @@ -20,35 +20,38 @@ VirtIOGraphicsAdapter::VirtIOGraphicsAdapter(PCI::Address base_address) : GraphicsDevice(base_address) { m_gpu_device = adopt_ref(*new VirtIOGPU(base_address)).leak_ref(); - m_framebuffer_console = Kernel::Graphics::VirtIOGPUConsole::initialize(m_gpu_device); - // FIXME: This is a very wrong way to do this... - GraphicsManagement::the().m_console = m_framebuffer_console; } void VirtIOGraphicsAdapter::initialize_framebuffer_devices() { dbgln_if(VIRTIO_DEBUG, "VirtIOGPU: Initializing framebuffer devices"); - VERIFY(m_framebuffer_device.is_null()); - m_framebuffer_device = adopt_ref(*new VirtIOFrameBufferDevice(m_gpu_device)).leak_ref(); + VERIFY(!m_created_framebuffer_devices); + m_gpu_device->create_framebuffer_devices(); + m_created_framebuffer_devices = true; + + // FIXME: This is a very wrong way to do this... + GraphicsManagement::the().m_console = m_gpu_device->default_console(); } void VirtIOGraphicsAdapter::enable_consoles() { dbgln_if(VIRTIO_DEBUG, "VirtIOGPU: Enabling consoles"); - VERIFY(m_framebuffer_console); - if (m_framebuffer_device) - m_framebuffer_device->deactivate_writes(); - m_gpu_device->clear_to_black(); - m_framebuffer_console->enable(); + m_gpu_device->for_each_framebuffer([&](auto& framebuffer, auto& console) { + framebuffer.deactivate_writes(); + framebuffer.clear_to_black(); + console.enable(); + return IterationDecision::Continue; + }); } void VirtIOGraphicsAdapter::disable_consoles() { dbgln_if(VIRTIO_DEBUG, "VirtIOGPU: Disabling consoles"); - VERIFY(m_framebuffer_device); - VERIFY(m_framebuffer_console); - m_framebuffer_console->disable(); - m_framebuffer_device->activate_writes(); + m_gpu_device->for_each_framebuffer([&](auto& framebuffer, auto& console) { + console.disable(); + framebuffer.activate_writes(); + return IterationDecision::Continue; + }); } } diff --git a/Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.h b/Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.h index f7f9ab3945..9a575b242a 100644 --- a/Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.h +++ b/Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.h @@ -18,7 +18,7 @@ class VirtIOGraphicsAdapter final : public GraphicsDevice { public: static NonnullRefPtr<VirtIOGraphicsAdapter> initialize(PCI::Address); - virtual bool framebuffer_devices_initialized() const override { return !m_framebuffer_device.is_null(); } + virtual bool framebuffer_devices_initialized() const override { return m_created_framebuffer_devices; } private: explicit VirtIOGraphicsAdapter(PCI::Address base_address); @@ -36,8 +36,7 @@ private: virtual bool set_y_offset(size_t, size_t) override { return false; } RefPtr<VirtIOGPU> m_gpu_device; - RefPtr<VirtIOFrameBufferDevice> m_framebuffer_device; - RefPtr<VirtIOGPUConsole> m_framebuffer_console; + bool m_created_framebuffer_devices { false }; }; } |