summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorTom <tomut@yahoo.com>2021-06-25 20:44:48 -0600
committerAndreas Kling <kling@serenityos.org>2021-06-27 09:46:27 +0200
commit8749235046b5176a889ee7a45b8b4cbaa49fc851 (patch)
treed10206d7bd8994daa82ca208d83b11c56a2bb1d7 /Kernel
parent708f27ca0e9716026dc6efd0bce4567a12064825 (diff)
downloadserenity-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.cpp170
-rw-r--r--Kernel/Graphics/VirtIOGPU/VirtIOFrameBufferDevice.h32
-rw-r--r--Kernel/Graphics/VirtIOGPU/VirtIOGPU.cpp161
-rw-r--r--Kernel/Graphics/VirtIOGPU/VirtIOGPU.h67
-rw-r--r--Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.cpp23
-rw-r--r--Kernel/Graphics/VirtIOGPU/VirtIOGPUConsole.h6
-rw-r--r--Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.cpp31
-rw-r--r--Kernel/Graphics/VirtIOGPU/VirtIOGraphicsAdapter.h5
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 };
};
}