mirror of
https://github.com/SerenityOS/serenity.git
synced 2026-11-07 04:29:30 -10:00
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:
1 parent
f03bb61135
commit
7e510d374a
15 files changed
+1299
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
@@ -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" },
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user