Libraries: Add an initial LibGL driver for the VideoCore VII 3D

This initial version of the driver only supports glClear{,Color,Depth}()
for now.

Since the only publicly available documentation for the VideoCore 3D
architecture (the VideoCore IV 3D Architecture Reference Guide) just
covers an older revision of the VideoCore 3D architecture, I used
Mesa's v3d_packet.xml as a reference to get definitions for the control
records of the VideoCore VII.

To determine which control records need to be generated for a minimal
example, I used Mesa's `V3D_DEBUG` environment variable and set it to
`cl` to make it dump the control lists it generates for a basic OpenGL
test program.
This commit is contained in:
Sönke Holz authored and Sönke Holz committed 2026-10-08 16:07:29 +02:00
1 parent f03bb61135
commit 7e510d374a
15 files changed
+1299

No files matched your search

+1
View File
@@ -69,6 +69,7 @@ add_subdirectory(LibUnicode)
add_subdirectory(LibURL)
add_subdirectory(LibUSBDB)
add_subdirectory(LibMedia)
add_subdirectory(LibV3DGPU)
add_subdirectory(LibVirtGPU)
add_subdirectory(LibVT)
add_subdirectory(LibWasm)
+1
View File
@@ -9,5 +9,6 @@ target_link_libraries(LibGPU PRIVATE ${CMAKE_DL_LIBS})
add_dependencies(LibGPU LibSoftGPU)
if (SERENITYOS)
add_dependencies(LibGPU LibV3DGPU)
add_dependencies(LibGPU LibVirtGPU)
endif()
+1
View File
@@ -16,6 +16,7 @@ namespace GPU {
static HashMap<StringView, char const*> const s_driver_path_map {
#if defined(AK_OS_SERENITY)
{ "softgpu"sv, "libsoftgpu.so.serenity" },
{ "v3dgpu"sv, "libv3dgpu.so.serenity" },
{ "virtgpu"sv, "libvirtgpu.so.serenity" },
#elif defined(AK_OS_MACOS)
{ "softgpu"sv, "liblagom-softgpu.dylib" },
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2025-2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Buffer.h"
#include "Definitions.h"
#include <Kernel/API/V3D.h>
#include <LibCore/System.h>
#include <sys/mman.h>
namespace V3DGPU {
extern int g_v3d_fd;
Buffer::~Buffer()
{
// The size of moved-from buffers is set to 0.
if (m_size == 0)
return;
VERIFY(m_mmap_address != nullptr);
if (auto result = Core::System::munmap(m_mmap_address, m_size); result.is_error())
dbgln("LibV3DGPU: ~Buffer(): munmap({}, {:#x}) failed: {}", m_mmap_address, m_size, result.release_error());
if (auto result = Core::System::ioctl(g_v3d_fd, V3D_FREE_BUFFER, m_gpu_virtual_address); result.is_error())
dbgln("LibV3DGPU: ~Buffer(): ioctl({}, V3D_FREE_BUFFER, {:#x}) failed: {}", g_v3d_fd, m_gpu_virtual_address, result.release_error());
}
ErrorOr<Buffer> Buffer::create(u32 size, StringView name)
{
VERIFY(size != 0);
VERIFY(size % V3D_PAGE_SIZE == 0);
V3DBuffer buffer = {
.size = size,
// Will be filled by the kernel.
.gpu_virtual_address = 0,
};
TRY(Core::System::ioctl(g_v3d_fd, V3D_ALLOCATE_BUFFER, &buffer));
VERIFY(buffer.gpu_virtual_address != 0);
auto* mmap_address = TRY(Core::System::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_RANDOMIZED, g_v3d_fd, buffer.gpu_virtual_address, 0, name));
return Buffer(buffer.gpu_virtual_address, buffer.size, mmap_address);
}
}
+66
View File
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2025-2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Forward.h>
#include <AK/Noncopyable.h>
#include <AK/StringView.h>
#include <AK/Types.h>
namespace V3DGPU {
class Buffer {
AK_MAKE_NONCOPYABLE(Buffer);
public:
~Buffer();
Buffer(Buffer&& other)
: m_gpu_virtual_address(other.m_gpu_virtual_address)
, m_size(other.m_size)
, m_mmap_address(other.m_mmap_address)
{
other.m_gpu_virtual_address = 0;
other.m_size = 0;
other.m_mmap_address = nullptr;
}
Buffer& operator=(Buffer&& other)
{
if (this != &other) {
this->~Buffer();
m_size = exchange(other.m_size, 0);
m_gpu_virtual_address = exchange(other.m_gpu_virtual_address, 0);
m_mmap_address = exchange(other.m_mmap_address, nullptr);
}
return *this;
}
static ErrorOr<Buffer> create(u32 size, StringView name);
Bytes data() { return Bytes { m_mmap_address, m_size }; }
ReadonlyBytes data() const { return ReadonlyBytes { m_mmap_address, m_size }; }
u32 size() const { return m_size; }
u32 gpu_virtual_address() const { return m_gpu_virtual_address; }
private:
Buffer(u32 address, u32 size, void* mmap_address)
: m_gpu_virtual_address(address)
, m_size(size)
, m_mmap_address(mmap_address)
{
}
u32 m_gpu_virtual_address { 0 };
u32 m_size { 0 };
void* m_mmap_address { nullptr };
};
}
@@ -0,0 +1,11 @@
set(SOURCES
Buffer.cpp
Device.cpp
Image.cpp
Shader.cpp
../LibGPU/Image.cpp
)
serenity_lib(LibV3DGPU v3dgpu)
target_link_libraries(LibV3DGPU PRIVATE LibCore)
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2025, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Buffer.h"
#include "Definitions.h"
#include <AK/ByteBuffer.h>
#include <AK/Error.h>
#include <AK/Span.h>
#include <AK/Types.h>
namespace V3DGPU {
class ControlList {
public:
template<typename T>
void append(T const& packet)
{
m_data.try_append(&packet, sizeof(packet)).release_value_but_fixme_should_propagate_errors();
}
void clear()
{
m_data.clear();
}
ReadonlyBytes data() const
{
return m_data.bytes();
}
ErrorOr<void> copy_to_gpu_buffer()
{
if (!m_buffer.has_value() || m_buffer->size() < m_data.size()) {
auto buffer_size = align_up_to(m_data.size(), V3D_PAGE_SIZE);
m_buffer = TRY(Buffer::create(buffer_size, "LibV3DGPU: Control List"sv));
}
m_data.bytes().copy_to(m_buffer->data());
return {};
}
Optional<Buffer const&> buffer() const
{
VERIFY(m_buffer.has_value());
return *m_buffer;
}
private:
AK::Detail::ByteBuffer<0> m_data;
Optional<Buffer> m_buffer;
};
}
@@ -0,0 +1,382 @@
/*
* Copyright (c) 2025-2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StdLibExtraDetails.h>
#include <AK/Types.h>
namespace V3DGPU::ControlRecord {
// Reference for control records: https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/broadcom/cle/v3d_packet.xml
// For type="address"
using Address = u32;
using Opcode = u8;
// <enum name="Memory Format" prefix="V3D_MEMORY_FORMAT">
enum class MemoryFormat : u32 {
Raster = 0,
Lineartile = 1,
UBLinear_1UIFBlocksWide = 2,
UBLinear_2UIFBlocksWide = 3,
UIF_NoXOR = 4,
UIF_XOR = 5,
};
// <enum name="Decimate Mode" prefix="V3D_DECIMATE_MODE">
enum class DecimateMode : u32 {
Sample0 = 0,
x4 = 1,
AllSamples = 3,
};
// <enum name="Internal Depth Type" prefix="V3D_INTERNAL_TYPE">
enum class InternalDepthType : u32 {
Depth32f = 0,
Depth24 = 1,
Depth16 = 2,
};
// <enum name="Render Target Type Clamp" prefix="V3D_RENDER_TARGET_TYPE_CLAMP" min_ver="71">
enum class RenderTargetTypeClamp : u32 {
TypeClamp8i = 0,
TypeClamp16i = 1,
TypeClamp32i = 2,
TypeClamp8ui = 4,
TypeClamp16ui = 5,
TypeClamp32ui = 6,
TypeClamp8 = 8,
TypeClamp16f = 9,
TypeClamp32f = 10,
TypeClamp8iClamped = 16,
TypeClamp16iClamped = 17,
TypeClamp32iClamped = 18,
TypeClamp8uiClamped = 20,
TypeClamp16uiClamped = 21,
TypeClamp32uiClamped = 22,
TypeClamp16fClampedNorm = 24,
TypeClamp16fClampedPos = 25,
TypeClamp16fClampedPQ = 26,
TypeClamp16fClampedHLG = 27,
// v3d_packet.xml defines "invalid" as 32, but that wouldn't fit in 5 bits?
};
// <enum name="Output Image Format" prefix="V3D_OUTPUT_IMAGE_FORMAT">
// FIXME: Add enumerators for all other formats.
enum class OutputImageFormat : u32 {
SRGB8_ALPHA8 = 0,
RGBA8 = 27,
};
// <enum name="Dither Mode" prefix="V3D_DITHER_MODE">
enum class DitherMode : u32 {
None = 0,
RGB = 1,
A = 2,
RGBA = 3,
};
// <packet code="4" name="Flush"/>
struct Flush {
Opcode opcode = 4;
};
static_assert(AssertSize<Flush, 1>());
// <packet code="6" name="Start Tile Binning"/>
struct StartTileBinning {
Opcode opcode = 6;
};
static_assert(AssertSize<StartTileBinning, 1>());
// <packet code="13" shortname="end_render" name="End of rendering"/>
struct EndOfRendering {
Opcode opcode = 13;
};
static_assert(AssertSize<EndOfRendering, 1>());
// <packet code="18" shortname="return" name="Return from sub-list"/>
struct ReturnFromSubList {
Opcode opcode = 18;
};
static_assert(AssertSize<ReturnFromSubList, 1>());
// <packet code="19" shortname="clear_vcd_cache" name="Flush VCD cache"/>
struct FlushVCDCache {
Opcode opcode = 19;
};
static_assert(AssertSize<FlushVCDCache, 1>());
// <packet code="20" shortname="generic_tile_list" name="Start Address of Generic Tile List">
struct [[gnu::packed]] StartAddressOfGenericTileList {
Opcode opcode = 20;
Address start;
Address end;
};
static_assert(AssertSize<StartAddressOfGenericTileList, 1 + 8>());
// <packet code="21" shortname="branch_implicit_tile" name="Branch to Implicit Tile List">
struct BranchToImplicitTileList {
Opcode opcode = 21;
u8 tile_list_set_number;
};
static_assert(AssertSize<BranchToImplicitTileList, 1 + 1>());
// <packet code="23" shortname="supertile_coords" name="Supertile Coordinates">
struct SupertileCoordinates {
Opcode opcode = 23;
u8 column_number_in_supertiles;
u8 row_number_in_supertiles;
};
static_assert(AssertSize<SupertileCoordinates, 1 + 2>());
// <packet code="25" shortname="clear_rt" name="Clear Render Targets" cl="R" min_ver="71"/>
struct ClearRenderTargets {
Opcode opcode = 25;
};
static_assert(AssertSize<ClearRenderTargets, 1>());
// <packet code="26" shortname="end_loads" name="End of Loads" cl="R"/>
struct EndOfLoads {
Opcode opcode = 26;
};
static_assert(AssertSize<EndOfLoads, 1>());
// <packet code="27" shortname="end_tile" name="End of Tile Marker" cl="R"/>
struct EndOfTileMarker {
Opcode opcode = 27;
};
static_assert(AssertSize<EndOfTileMarker, 1>());
// <packet code="29" shortname="store" name="Store Tile Buffer General" cl="R">
struct [[gnu::packed]] StoreTileBufferGeneral {
// <field name="Buffer to Store" size="4" start="0" type="uint">
enum class BufferToStore : u32 {
RenderTarget0 = 0,
RenderTarget1 = 1,
RenderTarget2 = 2,
RenderTarget3 = 3,
RenderTarget4 = 4,
RenderTarget5 = 5,
RenderTarget6 = 6,
RenderTarget7 = 7,
None = 8,
Z = 9,
Stencil = 10,
ZAndStencil = 11,
};
Opcode opcode = 29;
BufferToStore buffer_to_store : 4;
MemoryFormat memory_format : 3;
bool flip_y : 1;
DitherMode dither_mode : 2;
DecimateMode decimate_mode : 2;
OutputImageFormat output_image_format : 6;
bool clear_buffer_being_stored : 1;
bool channel_reverse : 1;
bool r_b_swap : 1;
u32 _reserved0 : 7;
u32 height_in_ub_or_stride : 20;
u16 height;
Address address;
};
static_assert(AssertSize<StoreTileBufferGeneral, 1 + 12>());
// <packet code="56" name="Prim List Format">
struct PrimListFormat {
// <field name="primitive type" size="6" start="0" type="uint">
enum class PrimitiveType : u8 {
ListPoints = 0,
ListLines = 1,
ListTriangles = 2,
};
Opcode opcode = 56;
PrimitiveType primitive_type : 6;
u8 _reserved0 : 1;
bool tri_strip_or_fan : 1;
};
static_assert(AssertSize<PrimListFormat, 1 + 1>());
// <packet name="Number of Layers" code="119">
struct NumberOfLayers {
Opcode opcode = 119;
u8 number_of_layers_minus_one;
};
static_assert(AssertSize<NumberOfLayers, 1 + 1>());
// <packet code="120" name="Tile Binning Mode Cfg" min_ver="71">
struct [[gnu::packed]] TileBinningModeCfg {
// <field name="tile allocation initial block size" size="2" start="2" type="uint">
enum class TileAllocationInitialBlockSize : u8 {
Size64Bytes = 0,
Size128Bytes = 1,
Size256Bytes = 2,
};
// <field name="tile allocation block size" size="2" start="4" type="uint">
enum class TileAllocationBlockSize : u8 {
Size64Bytes = 0,
Size128Bytes = 1,
Size256Bytes = 2,
};
// <field name="Log2 Tile Width" size="3" start="8" type="uint">
enum class Log2TileWidth : u8 {
Width8Pixels = 0,
Width16Pixels = 1,
Width32Pixels = 2,
Width64Pixels = 3,
};
// <field name="Log2 Tile Height" size="3" start="11" type="uint">
enum class Log2TileHeight : u8 {
Height8Pixels = 0,
Height16Pixels = 1,
Height32Pixels = 2,
Height64Pixels = 3,
};
Opcode opcode = 120;
u8 _reserved0 : 2;
TileAllocationInitialBlockSize tile_allocation_initial_block_size : 2;
TileAllocationBlockSize tile_allocation_block_size : 2;
u8 _reserved1 : 2;
Log2TileWidth log2_tile_width : 3;
Log2TileHeight log2_tile_height : 3;
u32 _reserved2 : 18;
u16 width_in_pixels_minus_one;
u16 height_in_pixels_minus_one;
};
static_assert(AssertSize<TileBinningModeCfg, 1 + 8>());
// <packet code="121" name="Tile Rendering Mode Cfg (Common)" cl="R" min_ver="71">
struct [[gnu::packed]] TileRenderingModeCfgCommon {
// <field name="Early-Z Test and Update Direction" size="1" start="45" type="uint">
enum class EarlyZTestAndUpdateDirection : u8 {
LT_LE = 0,
GT_GE = 1,
};
// <field name="Log2 Tile Width" size="3" start="52" type="uint">
enum class Log2TileWidth : u32 {
Width8Pixels = 0,
Width16Pixels = 1,
Width32Pixels = 2,
Width64Pixels = 3,
};
// <field name="Log2 Tile Height" size="3" start="55" type="uint">
enum class Log2TileHeight : u32 {
Height8Pixels = 0,
Height16Pixels = 1,
Height32Pixels = 2,
Height64Pixels = 3,
};
Opcode opcode = 121;
u8 sub_id : 3 = 0;
u8 _reserved0 : 1;
u8 number_of_render_targets_minus_one : 4;
u16 image_width_pixels;
u16 image_height_pixels;
u8 _reserved1 : 2;
bool multisample_mode_4x : 1;
bool double_buffer_in_non_ms_mode : 1;
bool depth_buffer_disable : 1;
EarlyZTestAndUpdateDirection early_z_test_and_update_direction : 1;
bool early_z_disable : 1;
InternalDepthType internal_depth_type : 4;
bool early_depth_stencil_clear : 1;
Log2TileWidth log2_tile_width : 3;
Log2TileHeight log2_tile_height : 3;
u8 pad : 6;
};
static_assert(AssertSize<TileRenderingModeCfgCommon, 1 + 8>());
// <packet code="121" name="Tile Rendering Mode Cfg (ZS Clear Values)" cl="R" min_ver="71">
struct [[gnu::packed]] TileRenderingModeCfgZSClearValues {
Opcode opcode = 121;
u8 sub_id : 4 = 1; // FIXME: Is this an error in v3d_packet.xml? The sub-id is 3 bits in other packets.
u8 _reserved0 : 4;
u8 stencil_clear_value;
f32 z_clear_value;
u16 unused;
};
static_assert(AssertSize<TileRenderingModeCfgZSClearValues, 1 + 8>());
// <packet code="121" name="Tile Rendering Mode Cfg (Render Target Part1)" cl="R" min_ver="71">
struct [[gnu::packed]] TileRenderingModeCfgRenderTargetPart1 {
Opcode opcode = 121;
u32 sub_id : 3 = 2;
u32 render_target_number : 3;
u32 _reserved0 : 1;
u32 base_address : 11;
u32 stride_minus_one : 7;
u32 internal_bpp : 2;
RenderTargetTypeClamp internal_type_and_clamping : 5;
u32 clear_color_low_bits;
};
static_assert(AssertSize<TileRenderingModeCfgRenderTargetPart1, 1 + 8>());
// <packet code="122" name="Multicore Rendering Supertile Cfg" cl="R">
struct [[gnu::packed]] MulticoreRenderingSupertileCfg {
Opcode opcode = 122;
u8 supertile_width_in_tiles_minus_one;
u8 supertile_height_in_tiles_minus_one;
u8 total_frame_width_in_supertiles;
u8 total_frame_height_in_supertiles;
u16 total_frame_width_in_tiles : 12;
u16 total_frame_height_in_tiles : 12;
bool multicore_enable : 1;
u8 _reserved0 : 3;
bool supertile_raster_order : 1;
u8 number_of_bin_tile_lists_minus_one : 3;
};
static_assert(AssertSize<MulticoreRenderingSupertileCfg, 1 + 8>());
// <packet code="123" shortname="multicore_rendering_tile_list_base" name="Multicore Rendering Tile List Set Base" cl="R">
struct [[gnu::packed]] MulticoreRenderingTileListSetBase {
Opcode opcode = 123;
u8 tile_list_set_number : 4;
u8 _reserved0 : 2;
Address address : 26;
};
static_assert(AssertSize<MulticoreRenderingTileListSetBase, 1 + 4>());
// <packet code="124" shortname="tile_coords" name="Tile Coordinates">
struct [[gnu::packed]] TileCoordinates {
Opcode opcode = 124;
u16 tile_column_number : 12;
u16 tile_row_number : 12;
};
static_assert(AssertSize<TileCoordinates, 1 + 3>());
// <packet code="125" shortname="implicit_tile_coords" name="Tile Coordinates Implicit"/>
struct TileCoordinatesImplicit {
Opcode opcode = 125;
};
static_assert(AssertSize<TileCoordinatesImplicit, 1>());
// <packet code="126" name="Tile List Initial Block Size">
struct TileListInitialBlockSize {
// <field name="Size of first block in chained tile lists" size="2" start="0" type="uint">
enum class SizeOfFirstBlockInChainedTileLists : u8 {
Size64Bytes = 0,
Size128Bytes = 1,
Size256Bytes = 2,
};
Opcode opcode = 126;
SizeOfFirstBlockInChainedTileLists size_of_first_block_in_chained_tile_lists : 2;
bool use_auto_chained_tile_lists : 1;
u8 _reserved0 : 5;
};
static_assert(AssertSize<TileListInitialBlockSize, 1 + 1>());
}
@@ -0,0 +1,15 @@
/*
* Copyright (c) 2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
namespace V3DGPU {
static constexpr size_t V3D_PAGE_SIZE = 4096;
}
+499
View File
@@ -0,0 +1,499 @@
/*
* Copyright (c) 2025-2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Device.h"
#include "ControlList.h"
#include "ControlRecords.h"
#include "Definitions.h"
#include "Image.h"
#include "Shader.h"
#include <AK/NonnullOwnPtr.h>
#include <Kernel/API/V3D.h>
#include <LibCore/File.h>
#include <LibCore/System.h>
// VideoCore IV 3D Architecture Reference Guide: https://docs.broadcom.com/doc/12358545
// This specification only covers an older revision of the VideoCore 3D architecture.
namespace V3DGPU {
int g_v3d_fd = -1;
static constexpr size_t TILE_ALLOC_MEMORY_INITIAL_BLOCK_SIZE = 128;
static constexpr size_t TILE_STATE_DATA_ARRAY_ELEMENT_SIZE = 256;
static constexpr size_t TILE_WIDTH = 64;
static constexpr size_t TILE_HEIGHT = 64;
ErrorOr<ControlList> Device::generate_initial_binner_control_list()
{
ControlList control_list;
ControlRecord::NumberOfLayers number_of_layers {};
number_of_layers.number_of_layers_minus_one = 1 - 1;
control_list.append(number_of_layers);
ControlRecord::TileBinningModeCfg tile_binning_mode_cfg {};
static_assert(TILE_ALLOC_MEMORY_INITIAL_BLOCK_SIZE == 128);
static_assert(TILE_WIDTH == 64);
static_assert(TILE_HEIGHT == 64);
tile_binning_mode_cfg.tile_allocation_initial_block_size = ControlRecord::TileBinningModeCfg::TileAllocationInitialBlockSize::Size128Bytes;
tile_binning_mode_cfg.tile_allocation_block_size = ControlRecord::TileBinningModeCfg::TileAllocationBlockSize::Size64Bytes;
tile_binning_mode_cfg.log2_tile_width = ControlRecord::TileBinningModeCfg::Log2TileWidth::Width64Pixels;
tile_binning_mode_cfg.log2_tile_height = ControlRecord::TileBinningModeCfg::Log2TileHeight::Height64Pixels;
tile_binning_mode_cfg.width_in_pixels_minus_one = m_framebuffer_size.width() - 1;
tile_binning_mode_cfg.height_in_pixels_minus_one = m_framebuffer_size.height() - 1;
control_list.append(tile_binning_mode_cfg);
ControlRecord::FlushVCDCache flush_vcd_cache {};
control_list.append(flush_vcd_cache);
ControlRecord::StartTileBinning start_tile_binning {};
control_list.append(start_tile_binning);
return control_list;
}
ErrorOr<ControlList> Device::generate_tile_list(u32 target_buffer_pitch, u32 target_buffer_address)
{
ControlList control_list;
ControlRecord::TileCoordinatesImplicit tile_coordinates_implicit {};
control_list.append(tile_coordinates_implicit);
ControlRecord::EndOfLoads end_of_loads {};
control_list.append(end_of_loads);
ControlRecord::PrimListFormat prim_list_format {};
prim_list_format.primitive_type = ControlRecord::PrimListFormat::PrimitiveType::ListTriangles;
prim_list_format.tri_strip_or_fan = false;
control_list.append(prim_list_format);
ControlRecord::BranchToImplicitTileList branch_to_implicit_tile_list {};
branch_to_implicit_tile_list.tile_list_set_number = 0;
control_list.append(branch_to_implicit_tile_list);
ControlRecord::StoreTileBufferGeneral store_tile_buffer_general {};
store_tile_buffer_general.buffer_to_store = ControlRecord::StoreTileBufferGeneral::BufferToStore::RenderTarget0;
store_tile_buffer_general.memory_format = ControlRecord::MemoryFormat::Raster;
store_tile_buffer_general.flip_y = false;
store_tile_buffer_general.dither_mode = ControlRecord::DitherMode::None;
store_tile_buffer_general.decimate_mode = ControlRecord::DecimateMode::Sample0;
store_tile_buffer_general.output_image_format = ControlRecord::OutputImageFormat::RGBA8;
store_tile_buffer_general.clear_buffer_being_stored = false;
store_tile_buffer_general.channel_reverse = false;
store_tile_buffer_general.r_b_swap = true;
store_tile_buffer_general.height_in_ub_or_stride = target_buffer_pitch;
store_tile_buffer_general.height = 0;
store_tile_buffer_general.address = target_buffer_address;
control_list.append(store_tile_buffer_general);
ControlRecord::ClearRenderTargets clear_render_targets {};
control_list.append(clear_render_targets);
ControlRecord::EndOfTileMarker end_of_tile_marker {};
control_list.append(end_of_tile_marker);
ControlRecord::ReturnFromSubList return_from_sub_list {};
control_list.append(return_from_sub_list);
TRY(control_list.copy_to_gpu_buffer());
return control_list;
}
ErrorOr<Device::RenderControlList> Device::generate_render_control_list(u32 target_buffer_pitch, u32 target_buffer_address)
{
ControlList control_list;
ControlRecord::TileRenderingModeCfgCommon tile_rendering_mode_cfg_common {};
static_assert(TILE_WIDTH == 64);
static_assert(TILE_HEIGHT == 64);
tile_rendering_mode_cfg_common.number_of_render_targets_minus_one = 1 - 1;
tile_rendering_mode_cfg_common.image_width_pixels = m_framebuffer_size.width();
tile_rendering_mode_cfg_common.image_height_pixels = m_framebuffer_size.height();
tile_rendering_mode_cfg_common.multisample_mode_4x = false;
tile_rendering_mode_cfg_common.double_buffer_in_non_ms_mode = false;
tile_rendering_mode_cfg_common.depth_buffer_disable = false;
tile_rendering_mode_cfg_common.early_z_test_and_update_direction = ControlRecord::TileRenderingModeCfgCommon::EarlyZTestAndUpdateDirection::LT_LE;
tile_rendering_mode_cfg_common.early_z_disable = false;
tile_rendering_mode_cfg_common.internal_depth_type = ControlRecord::InternalDepthType::Depth16;
tile_rendering_mode_cfg_common.early_depth_stencil_clear = true;
tile_rendering_mode_cfg_common.log2_tile_width = ControlRecord::TileRenderingModeCfgCommon::Log2TileWidth::Width64Pixels;
tile_rendering_mode_cfg_common.log2_tile_height = ControlRecord::TileRenderingModeCfgCommon::Log2TileHeight::Height64Pixels;
control_list.append(tile_rendering_mode_cfg_common);
ControlRecord::TileRenderingModeCfgRenderTargetPart1 tile_rendering_mode_cfg_render_target_part1 {};
tile_rendering_mode_cfg_render_target_part1.render_target_number = 0;
tile_rendering_mode_cfg_render_target_part1.base_address = 0;
tile_rendering_mode_cfg_render_target_part1.stride_minus_one = 32 - 1;
tile_rendering_mode_cfg_render_target_part1.internal_bpp = 0;
tile_rendering_mode_cfg_render_target_part1.internal_type_and_clamping = ControlRecord::RenderTargetTypeClamp::TypeClamp8;
tile_rendering_mode_cfg_render_target_part1.clear_color_low_bits = m_clear_color;
control_list.append(tile_rendering_mode_cfg_render_target_part1);
ControlRecord::TileRenderingModeCfgZSClearValues tile_rendering_mode_cfg_zs_clear_values {};
tile_rendering_mode_cfg_zs_clear_values.z_clear_value = m_clear_depth;
tile_rendering_mode_cfg_zs_clear_values.stencil_clear_value = 0;
control_list.append(tile_rendering_mode_cfg_zs_clear_values);
ControlRecord::TileListInitialBlockSize tile_list_initial_block_size {};
static_assert(TILE_ALLOC_MEMORY_INITIAL_BLOCK_SIZE == 128);
tile_list_initial_block_size.size_of_first_block_in_chained_tile_lists = ControlRecord::TileListInitialBlockSize::SizeOfFirstBlockInChainedTileLists::Size128Bytes;
tile_list_initial_block_size.use_auto_chained_tile_lists = true;
control_list.append(tile_list_initial_block_size);
ControlRecord::MulticoreRenderingTileListSetBase multicore_rendering_tile_list_set_base {};
multicore_rendering_tile_list_set_base.tile_list_set_number = 0;
multicore_rendering_tile_list_set_base.address = m_tile_alloc_memory_buffer->gpu_virtual_address() >> 6u;
control_list.append(multicore_rendering_tile_list_set_base);
ControlRecord::MulticoreRenderingSupertileCfg multicore_rendering_supertile_cfg {};
multicore_rendering_supertile_cfg.supertile_width_in_tiles_minus_one = 1 - 1;
multicore_rendering_supertile_cfg.supertile_height_in_tiles_minus_one = 1 - 1;
multicore_rendering_supertile_cfg.total_frame_width_in_supertiles = ceil_div(m_framebuffer_size.width(), TILE_WIDTH);
multicore_rendering_supertile_cfg.total_frame_height_in_supertiles = ceil_div(m_framebuffer_size.height(), TILE_HEIGHT);
multicore_rendering_supertile_cfg.total_frame_width_in_tiles = ceil_div(m_framebuffer_size.width(), TILE_WIDTH);
multicore_rendering_supertile_cfg.total_frame_height_in_tiles = ceil_div(m_framebuffer_size.height(), TILE_HEIGHT);
multicore_rendering_supertile_cfg.multicore_enable = false;
multicore_rendering_supertile_cfg.supertile_raster_order = false;
multicore_rendering_supertile_cfg.number_of_bin_tile_lists_minus_one = 1 - 1;
control_list.append(multicore_rendering_supertile_cfg);
ControlRecord::TileCoordinates tile_coordinates {};
tile_coordinates.tile_column_number = 0;
tile_coordinates.tile_row_number = 0;
control_list.append(tile_coordinates);
ControlRecord::EndOfLoads end_loads {};
control_list.append(end_loads);
ControlRecord::StoreTileBufferGeneral store_tile_buffer_general {};
store_tile_buffer_general.buffer_to_store = ControlRecord::StoreTileBufferGeneral::BufferToStore::None;
store_tile_buffer_general.memory_format = ControlRecord::MemoryFormat::Raster;
store_tile_buffer_general.flip_y = false;
store_tile_buffer_general.dither_mode = ControlRecord::DitherMode::None;
store_tile_buffer_general.decimate_mode = ControlRecord::DecimateMode::Sample0;
store_tile_buffer_general.output_image_format = ControlRecord::OutputImageFormat::RGBA8;
store_tile_buffer_general.clear_buffer_being_stored = false;
store_tile_buffer_general.channel_reverse = false;
store_tile_buffer_general.r_b_swap = false;
store_tile_buffer_general.height_in_ub_or_stride = 0;
store_tile_buffer_general.height = 0;
store_tile_buffer_general.address = 0;
control_list.append(store_tile_buffer_general);
ControlRecord::ClearRenderTargets clear_render_targets {};
control_list.append(clear_render_targets);
ControlRecord::EndOfTileMarker end_of_tile_marker {};
control_list.append(end_of_tile_marker);
// FIXME: Mesa seems to emit a second store here?
ControlRecord::FlushVCDCache flush_vcd_cache {};
control_list.append(flush_vcd_cache);
auto tile_list = TRY(generate_tile_list(target_buffer_pitch, target_buffer_address));
ControlRecord::StartAddressOfGenericTileList start_address_of_generic_tile_list {};
start_address_of_generic_tile_list.start = tile_list.buffer()->gpu_virtual_address();
start_address_of_generic_tile_list.end = tile_list.buffer()->gpu_virtual_address() + tile_list.data().size();
control_list.append(start_address_of_generic_tile_list);
for (int row_number_in_supertiles = 0; row_number_in_supertiles < ceil_div(m_framebuffer_size.height(), TILE_HEIGHT); row_number_in_supertiles++) {
for (int column_number_in_supertiles = 0; column_number_in_supertiles < ceil_div(m_framebuffer_size.width(), TILE_WIDTH); column_number_in_supertiles++) {
ControlRecord::SupertileCoordinates supertile_coordinates {};
supertile_coordinates.column_number_in_supertiles = column_number_in_supertiles;
supertile_coordinates.row_number_in_supertiles = row_number_in_supertiles;
control_list.append(supertile_coordinates);
}
}
ControlRecord::EndOfRendering end_of_rendering {};
control_list.append(end_of_rendering);
TRY(control_list.copy_to_gpu_buffer());
return RenderControlList {
.control_list = move(control_list),
.tile_list = move(tile_list),
};
}
Device::Device(NonnullOwnPtr<Core::File> gpu_file)
: m_gpu_file { move(gpu_file) }
{
}
ErrorOr<NonnullOwnPtr<Device>> Device::create(Gfx::IntSize min_size)
{
// FIXME: Don't hardcode this path.
static constexpr auto DEVICE_PATH = "/dev/gpu/render0"sv;
auto file_or_error = Core::File::open(DEVICE_PATH, Core::File::OpenMode::ReadWrite | Core::File::OpenMode::DontCreate);
if (file_or_error.is_error()) {
dbgln("LibV3DGPU: Failed to open \"{}\": {}", DEVICE_PATH, file_or_error.error());
return file_or_error.release_error();
}
auto file = file_or_error.release_value();
g_v3d_fd = file->fd();
auto device = make<Device>(move(file));
auto initialize_result = device->initialize_context(min_size);
if (initialize_result.is_error()) {
dbgln("LibV3DGPU: Failed to initialize context: {}", initialize_result.error());
return initialize_result.release_error();
}
return device;
}
ErrorOr<void> Device::initialize_context(Gfx::IntSize min_size)
{
m_framebuffer_size = min_size;
m_framebuffer = TRY(Buffer::create(align_up_to(m_framebuffer_size.area() * sizeof(u32), V3D_PAGE_SIZE), "LibV3DGPU: Framebuffer"sv));
m_framebuffer_data = m_framebuffer->data();
m_binner_control_list = TRY(generate_initial_binner_control_list());
auto tile_count = ceil_div(m_framebuffer_size.width(), TILE_WIDTH) * ceil_div(m_framebuffer_size.height(), TILE_HEIGHT);
auto tile_alloc_memory_size = align_up_to(tile_count * TILE_ALLOC_MEMORY_INITIAL_BLOCK_SIZE, V3D_PAGE_SIZE);
// Add some extra memory to avoid having the kernel allocate overspill memory.
// FIXME: There is probably a better way to calculate this.
tile_alloc_memory_size += 1 * MiB;
m_tile_alloc_memory_buffer = TRY(Buffer::create(tile_alloc_memory_size, "LibV3DGPU: Tile Alloc Memory"sv));
auto tile_state_data_array_memory_size = align_up_to(tile_count * TILE_STATE_DATA_ARRAY_ELEMENT_SIZE, V3D_PAGE_SIZE);
m_tile_state_data_array_buffer = TRY(Buffer::create(tile_state_data_array_memory_size, "LibV3DGPU: Tile State Data Array"sv));
return {};
}
GPU::DeviceInfo Device::info() const
{
return {
.vendor_name = "SerenityOS",
.device_name = "VideoCore VII 3D",
.num_texture_units = 1,
.num_lights = 8,
.max_clip_planes = 0,
.max_texture_size = 0,
.max_texture_lod_bias = 0.f,
.stencil_bits = 0,
.supports_npot_textures = false,
.supports_texture_clamp_to_edge = false,
.supports_texture_env_add = false,
};
}
void Device::draw_primitives(GPU::PrimitiveType, Vector<GPU::Vertex>&)
{
dbgln("V3DGPU::Device::draw_primitives(): unimplemented");
}
void Device::resize(Gfx::IntSize)
{
dbgln("V3DGPU::Device::resize(): unimplemented");
}
void Device::clear_color(FloatVector4 const& color)
{
auto clamped = color.clamped(0.0f, 1.0f);
auto r = static_cast<u8>(clamped.x() * 255u);
auto g = static_cast<u8>(clamped.y() * 255u);
auto b = static_cast<u8>(clamped.z() * 255u);
auto a = static_cast<u8>(clamped.w() * 255u);
m_clear_color = a << 24u | b << 16u | g << 8u | r;
}
void Device::clear_depth(GPU::DepthType depth)
{
m_clear_depth = clamp(depth, 0.0f, 1.0f);
}
void Device::clear_stencil(GPU::StencilType)
{
dbgln("V3DGPU::Device::clear_stencil(): unimplemented");
}
void Device::blit_from_color_buffer(Gfx::Bitmap& target)
{
ControlRecord::Flush flush {};
m_binner_control_list.append(flush);
m_binner_control_list.copy_to_gpu_buffer().release_value_but_fixme_should_propagate_errors();
auto new_render_control_list_state = render_control_list_state_needed_for_current_frame();
if (m_render_control_list_state != new_render_control_list_state) {
// Some parameter(s) changed that require regenerating the render control list.
m_render_control_list = generate_render_control_list(m_framebuffer_size.width() * sizeof(u32), m_framebuffer->gpu_virtual_address()).release_value_but_fixme_should_propagate_errors();
m_render_control_list_state = new_render_control_list_state;
}
V3DJob kernel_job = {
.tile_state_data_array_address = m_tile_state_data_array_buffer->gpu_virtual_address(),
.tile_allocation_memory_address = m_tile_alloc_memory_buffer->gpu_virtual_address(),
.tile_allocation_memory_size = m_tile_alloc_memory_buffer->size(),
.binning_control_list_address = m_binner_control_list.buffer()->gpu_virtual_address(),
.binning_control_list_size = static_cast<u32>(m_binner_control_list.data().size()),
.rendering_control_list_address = m_render_control_list.control_list.buffer()->gpu_virtual_address(),
.rendering_control_list_size = static_cast<u32>(m_render_control_list.control_list.data().size()),
};
Core::System::ioctl(g_v3d_fd, V3D_SUBMIT_JOB, &kernel_job).release_value_but_fixme_should_propagate_errors();
// FIXME: Add support for other pitches.
VERIFY(target.pitch() == m_framebuffer_size.width() * sizeof(u32));
VERIFY(target.data_size() == m_framebuffer_size.area() * sizeof(u32));
VERIFY(target.size() == m_framebuffer_size);
memcpy(target.scanline_u8(0), m_framebuffer_data.data(), target.data_size());
m_binner_control_list = generate_initial_binner_control_list().release_value_but_fixme_should_propagate_errors();
}
void Device::blit_from_color_buffer(NonnullRefPtr<GPU::Image>, u32, Vector2<u32>, Vector2<i32>, Vector3<i32>)
{
dbgln("V3DGPU::Device::blit_from_color_buffer(): unimplemented");
}
void Device::blit_from_color_buffer(void*, Vector2<i32>, GPU::ImageDataLayout const&)
{
dbgln("V3DGPU::Device::blit_from_color_buffer(): unimplemented");
}
void Device::blit_from_depth_buffer(void*, Vector2<i32>, GPU::ImageDataLayout const&)
{
dbgln("V3DGPU::Device::blit_from_depth_buffer(): unimplemented");
}
void Device::blit_from_depth_buffer(NonnullRefPtr<GPU::Image>, u32, Vector2<u32>, Vector2<i32>, Vector3<i32>)
{
dbgln("V3DGPU::Device::blit_from_depth_buffer(): unimplemented");
}
void Device::blit_to_color_buffer_at_raster_position(void const*, GPU::ImageDataLayout const&)
{
dbgln("V3DGPU::Device::blit_to_color_buffer_at_raster_position(): unimplemented");
}
void Device::blit_to_depth_buffer_at_raster_position(void const*, GPU::ImageDataLayout const&)
{
dbgln("V3DGPU::Device::blit_to_depth_buffer_at_raster_position(): unimplemented");
}
void Device::set_options(GPU::RasterizerOptions const&)
{
dbgln("V3DGPU::Device::set_options(): unimplemented");
}
void Device::set_light_model_params(GPU::LightModelParameters const&)
{
dbgln("V3DGPU::Device::set_light_model_params(): unimplemented");
}
GPU::RasterizerOptions Device::options() const
{
dbgln("V3DGPU::Device::options(): unimplemented");
return {};
}
GPU::LightModelParameters Device::light_model() const
{
dbgln("V3DGPU::Device::light_model(): unimplemented");
return {};
}
NonnullRefPtr<GPU::Image> Device::create_image(GPU::PixelFormat const& pixel_format, u32 width, u32 height, u32 depth, u32 max_levels)
{
dbgln("V3DGPU::Device::create_image(): unimplemented");
return adopt_ref(*new Image(this, pixel_format, width, height, depth, max_levels));
}
ErrorOr<NonnullRefPtr<GPU::Shader>> Device::create_shader(GPU::IR::Shader const&)
{
dbgln("V3DGPU::Device::create_shader(): unimplemented");
return adopt_ref(*new Shader(this));
}
void Device::set_model_view_transform(Gfx::FloatMatrix4x4 const&)
{
dbgln("V3DGPU::Device::set_model_view_transform(): unimplemented");
}
void Device::set_projection_transform(Gfx::FloatMatrix4x4 const&)
{
dbgln("V3DGPU::Device::set_projection_transform(): unimplemented");
}
void Device::set_sampler_config(unsigned, GPU::SamplerConfig const&)
{
dbgln("V3DGPU::Device::set_sampler_config(): unimplemented");
}
void Device::set_light_state(unsigned, GPU::Light const&)
{
dbgln("V3DGPU::Device::set_light_state(): unimplemented");
}
void Device::set_material_state(GPU::Face, GPU::Material const&)
{
dbgln("V3DGPU::Device::set_material_state(): unimplemented");
}
void Device::set_stencil_configuration(GPU::Face, GPU::StencilConfiguration const&)
{
dbgln("V3DGPU::Device::set_stencil_configuration(): unimplemented");
}
void Device::set_texture_unit_configuration(GPU::TextureUnitIndex, GPU::TextureUnitConfiguration const&)
{
dbgln("V3DGPU::Device::set_texture_unit_configuration(): unimplemented");
}
void Device::set_clip_planes(Vector<FloatVector4> const&)
{
dbgln("V3DGPU::Device::set_clip_planes(): unimplemented");
}
GPU::RasterPosition Device::raster_position() const
{
dbgln("V3DGPU::Device::raster_position(): unimplemented");
return {};
}
void Device::set_raster_position(GPU::RasterPosition const&)
{
dbgln("V3DGPU::Device::set_raster_position(): unimplemented");
}
void Device::set_raster_position(FloatVector4 const&)
{
dbgln("V3DGPU::Device::set_raster_position(): unimplemented");
}
void Device::bind_fragment_shader(RefPtr<GPU::Shader>)
{
dbgln("V3DGPU::Device::bind_fragment_shader(): unimplemented");
}
}
extern "C" GPU::Device* serenity_gpu_create_device(Gfx::IntSize size)
{
auto device_or_error = V3DGPU::Device::create(size);
if (device_or_error.is_error())
return nullptr;
return device_or_error.release_value().leak_ptr();
}
+115
View File
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Buffer.h"
#include "ControlList.h"
#include <AK/NonnullOwnPtr.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Vector.h>
#include <LibGPU/Device.h>
namespace V3DGPU {
struct VertexData;
class Device final : public GPU::Device {
public:
Device(NonnullOwnPtr<Core::File>);
static ErrorOr<NonnullOwnPtr<Device>> create(Gfx::IntSize min_size);
virtual GPU::DeviceInfo info() const override;
virtual void draw_primitives(GPU::PrimitiveType, Vector<GPU::Vertex>& vertices) override;
virtual void resize(Gfx::IntSize min_size) override;
virtual void clear_color(FloatVector4 const&) override;
virtual void clear_depth(GPU::DepthType) override;
virtual void clear_stencil(GPU::StencilType) override;
virtual void blit_from_color_buffer(Gfx::Bitmap& target) override;
virtual void blit_from_color_buffer(NonnullRefPtr<GPU::Image>, u32 level, Vector2<u32> input_size, Vector2<i32> input_offset, Vector3<i32> output_offset) override;
virtual void blit_from_color_buffer(void*, Vector2<i32> offset, GPU::ImageDataLayout const&) override;
virtual void blit_from_depth_buffer(void*, Vector2<i32> offset, GPU::ImageDataLayout const&) override;
virtual void blit_from_depth_buffer(NonnullRefPtr<GPU::Image>, u32 level, Vector2<u32> input_size, Vector2<i32> input_offset, Vector3<i32> output_offset) override;
virtual void blit_to_color_buffer_at_raster_position(void const*, GPU::ImageDataLayout const&) override;
virtual void blit_to_depth_buffer_at_raster_position(void const*, GPU::ImageDataLayout const&) override;
virtual void set_options(GPU::RasterizerOptions const&) override;
virtual void set_light_model_params(GPU::LightModelParameters const&) override;
virtual GPU::RasterizerOptions options() const override;
virtual GPU::LightModelParameters light_model() const override;
virtual NonnullRefPtr<GPU::Image> create_image(GPU::PixelFormat const&, u32 width, u32 height, u32 depth, u32 max_levels) override;
virtual ErrorOr<NonnullRefPtr<GPU::Shader>> create_shader(GPU::IR::Shader const&) override;
virtual void set_model_view_transform(FloatMatrix4x4 const&) override;
virtual void set_projection_transform(FloatMatrix4x4 const&) override;
virtual void set_sampler_config(unsigned, GPU::SamplerConfig const&) override;
virtual void set_light_state(unsigned, GPU::Light const&) override;
virtual void set_material_state(GPU::Face, GPU::Material const&) override;
virtual void set_stencil_configuration(GPU::Face, GPU::StencilConfiguration const&) override;
virtual void set_texture_unit_configuration(GPU::TextureUnitIndex, GPU::TextureUnitConfiguration const&) override;
virtual void set_clip_planes(Vector<FloatVector4> const&) override;
virtual GPU::RasterPosition raster_position() const override;
virtual void set_raster_position(GPU::RasterPosition const& raster_position) override;
virtual void set_raster_position(FloatVector4 const& position) override;
virtual void bind_fragment_shader(RefPtr<GPU::Shader>) override;
private:
ErrorOr<void> initialize_context(Gfx::IntSize min_size);
ErrorOr<ControlList> generate_initial_binner_control_list();
struct RenderControlList {
ControlList control_list;
ControlList tile_list;
};
ErrorOr<RenderControlList> generate_render_control_list(u32 target_buffer_pitch, u32 target_buffer_address);
ErrorOr<ControlList> generate_tile_list(u32 target_buffer_pitch, u32 target_buffer_address);
// This member should be before any Buffers (or anything containing Buffers, like ControlLists)
// to ensure that its destructor gets called last.
NonnullOwnPtr<Core::File> m_gpu_file;
Gfx::IntSize m_framebuffer_size { 0, 0 };
Bytes m_framebuffer_data;
Optional<Buffer> m_framebuffer;
ControlList m_binner_control_list;
RenderControlList m_render_control_list;
Optional<Buffer> m_tile_alloc_memory_buffer;
Optional<Buffer> m_tile_state_data_array_buffer;
u32 m_clear_color { 0x00'00'00'00 };
f32 m_clear_depth { 1.0f };
struct RenderControlListState {
// This struct contains all of the state that is "baked" into the render control list.
// If any of these members change, the render control list needs to be regenerated.
u32 clear_color { 0x00'00'00'00 };
f32 clear_depth { 1.0f };
bool operator==(RenderControlListState const&) const = default;
};
// This Optional is only empty before the first frame is rendered, so when we haven't generated a render control list yet.
Optional<RenderControlListState> m_render_control_list_state;
RenderControlListState render_control_list_state_needed_for_current_frame()
{
return {
.clear_color = m_clear_color,
.clear_depth = m_clear_depth,
};
}
};
}
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Image.h"
namespace V3DGPU {
Image::Image(void const* ownership_token, GPU::PixelFormat const& pixel_format, u32 width, u32 height, u32 depth, u32 max_levels)
: GPU::Image(ownership_token, pixel_format, width, height, depth, max_levels)
{
dbgln("V3DGPU::Image::Image(): unimplemented");
}
void Image::regenerate_mipmaps()
{
dbgln("V3DGPU::Image::regenerate_mipmaps(): unimplemented");
}
void Image::write_texels(u32, Vector3<i32> const&, void const*, GPU::ImageDataLayout const&)
{
dbgln("V3DGPU::Image::write_texels(): unimplemented");
}
void Image::read_texels(u32, Vector3<i32> const&, void*, GPU::ImageDataLayout const&) const
{
dbgln("V3DGPU::Image::read_texels(): unimplemented");
}
void Image::copy_texels(GPU::Image const&, u32, Vector3<u32> const&, Vector3<u32> const&, u32, Vector3<u32> const&)
{
dbgln("V3DGPU::Image::copy_texels(): unimplemented");
}
}
+24
View File
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGPU/Image.h>
namespace V3DGPU {
class Image final : public GPU::Image {
public:
Image(void const* ownership_token, GPU::PixelFormat const&, u32 width, u32 height, u32 depth, u32 max_levels);
virtual void regenerate_mipmaps() override;
virtual void write_texels(u32 level, Vector3<i32> const& output_offset, void const* input_data, GPU::ImageDataLayout const&) override;
virtual void read_texels(u32 level, Vector3<i32> const& input_offset, void* output_data, GPU::ImageDataLayout const&) const override;
virtual void copy_texels(GPU::Image const& source, u32 source_level, Vector3<u32> const& source_offset, Vector3<u32> const& size, u32 destination_level, Vector3<u32> const& destination_offset) override;
};
}
+16
View File
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Shader.h"
namespace V3DGPU {
Shader::Shader(void const* ownership_token)
: GPU::Shader(ownership_token)
{
}
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGPU/Shader.h>
namespace V3DGPU {
class Shader final : public GPU::Shader {
public:
Shader(void const* ownership_token);
};
}