gpu

package module
v0.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2026 License: BSD-3-Clause Imports: 4 Imported by: 0

README

go-virtio/gpu

Pure-Go virtio-gpu (2D framebuffer) driver targeting the go-virtio/common transport interfaces. Implements the modern-transport (Virtio 1.0+) init sequence and the control-queue command path for the standard PCI-bound virtio-gpu device (VID 0x1AF4, DID 0x1050).

Scope

2D framebuffer only. 3D / virgl is explicitly out of scope: it requires a Mesa-class GL/Vulkan stack to generate the OpenGL command streams the host consumes, which does not exist in Go. The driver negotiates exactly VIRTIO_F_VERSION_1 and deliberately does not negotiate VIRTIO_GPU_F_VIRGL (bit 0), keeping the device in plain 2D mode (Virtio 1.1 §5.7).

Like the sibling drivers this package owns device bring-up, both virtqueues — the control queue (controlq, carrying every 2D command) and the cursor queue (cursorq, set up for spec-completeness but unused) — and the on-the-wire virtio-gpu control protocol. Every command is a 2-descriptor chain (a read-only request followed by a device-writable response), built with common.AddChain.

It exposes a scanout-enumeration + framebuffer API:

  • DisplayInfo lists the device's scanouts (GET_DISPLAY_INFO).
  • SetupFramebuffer creates a host 2D resource, attaches guest backing, and binds it to a scanout (RESOURCE_CREATE_2D + RESOURCE_ATTACH_BACKING + SET_SCANOUT). The returned Framebuffer.Pix is a BGRA byte buffer the caller draws into.
  • Framebuffer.Flush pushes the drawn pixels to the host and refreshes the scanout (TRANSFER_TO_HOST_2D + RESOURCE_FLUSH).

Quick start

import (
    virtiogpu "github.com/go-virtio/gpu"
)

// transport is any value that implements go-virtio/common.Transport.
g, err := virtiogpu.OpenVirtioGPU(transport)
if err != nil {
    return err
}

displays, err := g.DisplayInfo()
if err != nil {
    return err
}
d := displays[0] // first scanout

fb, err := g.SetupFramebuffer(d.ScanoutID, d.Width, d.Height)
if err != nil {
    return err
}

// Draw BGRA pixels into fb.Pix, then push to the display.
for i := 0; i+3 < len(fb.Pix); i += 4 {
    fb.Pix[i+0] = 0x20 // B
    fb.Pix[i+1] = 0x80 // G
    fb.Pix[i+2] = 0xFF // R
    fb.Pix[i+3] = 0xFF // A
}
err = fb.Flush()

Sibling packages

License

BSD-3-Clause. See LICENSE.

Documentation

Overview

Package gpu is a pure-Go virtio-gpu (2D framebuffer) driver. It drives a modern (Virtio 1.0+) PCI virtio-gpu device through the transport interfaces defined in github.com/go-virtio/common; the same code drives a UEFI-backed device, a bare-metal device, or a virtio-mmio device depending on which common.Transport implementation the caller supplies.

Scope — two paths. The 2D framebuffer path (OpenVirtioGPU) negotiates exactly VIRTIO_F_VERSION_1 and drives the device in plain 2D mode (Virtio 1.1 §5.7). The 3D path (OpenVirtioGPU3D + ClearScreen, in gpu3d.go) additionally negotiates VIRTIO_GPU_F_VIRGL (bit 0) and offloads a host-GPU scanout clear to virglrenderer via a hand-encoded virgl command stream — still pure Go, CGO=0. What remains out of scope is a FULL OpenGL/Vulkan implementation in Go (the Mesa-class GL→TGSI stack needed to generate arbitrary command streams from an application API); the 3D path hand-authors only the fixed command streams it needs.

The driver owns device bring-up, the control virtqueue (controlq), the cursor virtqueue (cursorq, set up but unused), and the on-the-wire virtio-gpu control protocol (every command is a 2-descriptor chain: a read-only request followed by a device-writable response). It exposes a scanout-enumeration + framebuffer API:

  • DisplayInfo lists the device's scanouts (GET_DISPLAY_INFO).
  • SetupFramebuffer creates a host resource, attaches guest backing, and binds it to a scanout (RESOURCE_CREATE_2D + RESOURCE_ATTACH_BACKING + SET_SCANOUT). The returned Framebuffer's Pix is a BGRA byte buffer the caller draws into.
  • Framebuffer.Flush pushes the drawn pixels to the host and refreshes the scanout (TRANSFER_TO_HOST_2D + RESOURCE_FLUSH).

References:

  • Virtio 1.1 §5.7 "GPU Device" — device-type 16 binding.
  • Virtio 1.1 §5.7.4 "Device configuration layout" — virtio_gpu_config.
  • Virtio 1.1 §5.7.6 "Device Operation" — control + cursor queues, struct virtio_gpu_ctrl_hdr and the 2D command structs.
  • Virtio 1.1 §3.1.1 "Device Initialization".

3D / virgl extension of the virtio-gpu driver (Milestone 1). This file is purely additive: it does NOT touch the 2D OpenVirtioGPU / SetupFramebuffer / Flush / DisplayInfo path. It adds a second constructor, OpenVirtioGPU3D, that negotiates VIRTIO_GPU_F_VIRGL in addition to VERSION_1, and a single high-level operation, ClearScreen, that clears a scanout to a solid colour using the HOST GPU via virglrenderer.

M1 scope is deliberately tiny: stand up a virgl context, create a 3D render-target resource, submit a 3-command virgl buffer (CREATE SURFACE + SET_FRAMEBUFFER_STATE + CLEAR), pull the result back into the guest backing, and bind+flush it to a scanout. No Mesa is needed because the virgl command buffer for a flat clear is short enough to hand-encode.

References:

  • Linux uapi linux/virtio_gpu.h — the 3D control structs and command codes (VIRTIO_GPU_CMD_*_3D, VIRTIO_GPU_F_VIRGL).
  • virgl_protocol.h — VIRGL_CMD0 header layout, VIRGL_OBJECT_SURFACE, VIRGL_CCMD_* opcodes, VIRGL_OBJ_SURFACE_* / clear field order.
  • virgl_encode.c — the canonical encoders this hand-encoding mirrors.

Blob-resource + context-init primitives for the virtio-gpu 3D path (Milestone toward Venus). This file is PURELY additive: it adds the guest-side WIRE ENCODINGS for the blob-resource control commands and the context_init-bearing CTX_CREATE, plus a Venus-specific feature mask. It does NOT add any host round-trip method — every function here is a pure byte-builder whose output is asserted byte-for-byte in the unit tests.

Why only encoders, no host calls: the blob path's RUNTIME behaviour (whether a HOST3D blob actually becomes guest-mappable, what map_info caching type the host returns, whether the Venus ring goes live) is host/renderer-dependent and is NOT verifiable on this machine without a Venus-capable renderer. The structs below, by contrast, are fixed little-endian wire layouts defined in the kernel uapi and are fully verifiable offline. See README / the venus repo for the host-dependent boundary.

References (every constant/offset below is transcribed from these):

  • Linux uapi include/uapi/linux/virtio_gpu.h:
  • enum virtio_gpu_ctrl_type — command codes (sequential).
  • struct virtio_gpu_resource_create_blob.
  • struct virtio_gpu_resource_map_blob.
  • struct virtio_gpu_resp_map_info.
  • struct virtio_gpu_ctx_create (nlen, context_init, debug_name[64]).
  • VIRTIO_GPU_BLOB_MEM_* / VIRTIO_GPU_BLOB_FLAG_* constants.
  • VIRTIO_GPU_F_RESOURCE_BLOB (3) / VIRTIO_GPU_F_CONTEXT_INIT (4).
  • VIRTIO_GPU_CAPSET_VENUS (4).

3D / virgl extension of the virtio-gpu driver (Milestone 2). This file is purely additive: it does NOT touch the 2D path (OpenVirtioGPU / SetupFramebuffer / Flush / DisplayInfo) nor the M1 ClearScreen path in gpu3d.go. It adds a single high-level operation, DrawTriangle, that renders one flat-shaded triangle into a virtio-gpu scanout using the HOST GPU via virglrenderer — still pure Go, CGO=0.

Why this is the "hard part". A clear (M1) only needs three virgl commands and no shaders. A *draw* needs the whole Gallium draw pipeline encoded by hand: two shaders, four pipeline-state objects (vertex-elements, rasterizer, blend, depth-stencil-alpha), a vertex buffer with guest backing, and the DRAW_VBO itself — all in one SUBMIT_3D virgl command buffer. The crux is the shader encoding. virglrenderer does NOT take binary TGSI tokens over the wire: virgl_encode_shader_state() in Mesa (gallium/drivers/virgl) calls tgsi_dump_str() and ships the shader as a NUL-terminated ASCII TGSI *text* dump; the host (vrend_decode_create_shader -> vrend_create_shader -> tgsi_text_translate) re-parses that text into tokens. So this driver hand-authors TGSI text, which the host parser accepts (str_match_nocase_whole on "VERT"/"FRAG", "DCL", "IMM", "MOV", "END"; see gallium/auxiliary/tgsi/tgsi_text.c). This sidesteps the binary token format entirely.

Authoritative sources (every encoding below is annotated with the file + symbol it was derived from):

  • virgl_protocol.h (virglrenderer src/) — enum virgl_context_cmd, enum virgl_object_type, and the VIRGL_OBJ_*/VIRGL_SET_*/VIRGL_DRAW_VBO_* field-offset + size macros.
  • virgl_encode.c (Mesa gallium/drivers/virgl) — virgl_encode_shader_state, virgl_emit_shader_header, virgl_emit_shader_streamout, virgl_encoder_create_vertex_elements, virgl_encode_rasterizer_state, virgl_encode_blend_state, virgl_encode_dsa_state, virgl_encoder_set_vertex_buffers, virgl_encoder_draw_vbo, virgl_encoder_set_viewport_states, virgl_encoder_create_surface, virgl_encoder_set_framebuffer_state, virgl_encode_bind_object.
  • tgsi_text.c (Mesa gallium/auxiliary/tgsi) — accepted TGSI text syntax.
  • p_defines.h (Mesa gallium/include/pipe) — PIPE_SHADER_*, PIPE_BUFFER, PIPE_PRIM_TRIANGLES.
  • virgl_hw.h (virglrenderer src/) — VIRGL_BIND_*, VIRGL_FORMAT_*.

See the gpu3d_draw_test.go header and the package REPORT for the per-dword derivation and the UNCERTAINTIES that still need hardware validation.

3D / virgl extension of the virtio-gpu driver (Milestone 3). This file is purely additive: it does NOT touch the 2D path (OpenVirtioGPU / SetupFramebuffer / Flush / DisplayInfo), the M1 ClearScreen path in gpu3d.go, nor the M2 DrawTriangle path in gpu3d_draw.go. It adds a single high-level operation, DrawTexturedTriangle, that renders one triangle whose fragments are sampled from a host-side texture, into a virtio-gpu scanout using the HOST GPU via virglrenderer — still pure Go, CGO=0.

M3 = M2 (DrawTriangle) + a sampled texture. On top of the M2 draw pipeline it adds, per Mesa's virgl encoder + virglrenderer protocol:

  • a sampled-texture resource (RGBA8, VIRGL_BIND_SAMPLER_VIEW) with a guest backing the caller's texels are written into + TRANSFER_TO_HOST_3D;
  • CREATE_OBJECT(SAMPLER_VIEW=6) over that texture (format + texture-layer + texture-level + swizzle dwords);
  • CREATE_OBJECT(SAMPLER_STATE=7) (wrap/filter packing);
  • SET_SAMPLER_VIEWS(FRAGMENT, slot 0) + BIND_SAMPLER_STATES(FRAGMENT, slot 0);
  • a 2-element VERTEX_ELEMENTS layout (position R32G32B32_FLOAT @0 + texcoord R32G32_FLOAT @12, stride 20);
  • texturing TGSI shaders: the VS forwards a GENERIC[0] texcoord, the FS declares SAMP[0]/SVIEW[0] and TEX-samples OUT[0] COLOR.

Authoritative sources (each encoding below cites the file + symbol it was derived from; line numbers are from the notaz/mesa mirror of Mesa, branch master, fetched 2026-06-10):

  • virgl_protocol.h (Mesa src/gallium/drivers/virgl) — enum virgl_object_type (SAMPLER_VIEW=6, SAMPLER_STATE=7), enum virgl_context_cmd (SET_SAMPLER_VIEWS=10, BIND_SAMPLER_STATES=18), and the VIRGL_OBJ_SAMPLER_VIEW_* / VIRGL_OBJ_SAMPLE_STATE_S0_* / VIRGL_SET_SAMPLER_VIEWS_* / VIRGL_BIND_SAMPLER_STATES_* macros.
  • virgl_encode.c (Mesa src/gallium/drivers/virgl) — virgl_encode_sampler_view, virgl_encode_sampler_state, virgl_encode_set_sampler_views, virgl_encode_bind_sampler_states.
  • virgl_hw.h (Mesa src/gallium/drivers/virgl) — VIRGL_FORMAT_R32G32_FLOAT =29, VIRGL_FORMAT_R8G8B8A8_UNORM=67, VIRGL_BIND_SAMPLER_VIEW=(1<<3).
  • tgsi_text.c / tgsi_strings.c / tgsi_info.c (Mesa src/gallium/auxiliary/tgsi) — the DCL SAMP / DCL SVIEW grammar, the TEX instruction operand order, and the "2D"/"FLOAT"/"GENERIC" token names.
  • p_defines.h (Mesa src/gallium/include/pipe) — PIPE_SWIZZLE_*, PIPE_TEX_WRAP_*, PIPE_TEX_FILTER_*, PIPE_TEX_MIPFILTER_*.

See the gpu3d_tex_test.go header and the package REPORT for the per-dword derivation and the LOUD UNCERTAINTIES that still need hardware validation.

Index

Constants

View Source
const (
	ControlQueueIdx uint16 = 0
	CursorQueueIdx  uint16 = 1
)

Queue indices (Virtio 1.1 §5.7.2). The control queue carries every 2D command; the cursor queue carries cursor updates and is set up for spec-completeness but otherwise unused by this driver.

View Source
const (
	ControlQueueSize uint16 = 16
	CursorQueueSize  uint16 = 16
)

ControlQueueSize / CursorQueueSize are the desired ring sizes (clamped + rounded during setup). Commands are issued one at a time, so a small ring is plenty.

View Source
const (
	CmdCtxCreate          uint32 = 0x0200
	CmdCtxDestroy         uint32 = 0x0201
	CmdCtxAttachResource  uint32 = 0x0202
	CmdCtxDetachResource  uint32 = 0x0203
	CmdResourceCreate3D   uint32 = 0x0204
	CmdTransferToHost3D   uint32 = 0x0205
	CmdTransferFromHost3D uint32 = 0x0206
	CmdSubmit3D           uint32 = 0x0207
)

3D control-command request type codes (Linux uapi virtio_gpu.h, sequential from 0x0200). Success responses are OK_NODATA (0x1100), same as the 2D commands.

View Source
const (
	CapsetVirgl  uint32 = 1
	CapsetVirgl2 uint32 = 2
	CapsetVenus  uint32 = 4
)

CapsetVirgl / CapsetVirgl2 / CapsetVenus are the context_init capset ids. Venus (4) selects the Vulkan-over-virtio protocol for a context created with CTX_CREATE.context_init = CapsetVenus.

View Source
const (
	// VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB (2D block, 13th entry).
	CmdResourceCreateBlob uint32 = 0x010C
	// VIRTIO_GPU_CMD_SET_SCANOUT_BLOB (2D block, 14th entry) — included
	// for completeness; not used by the Venus ring path.
	CmdSetScanoutBlob uint32 = 0x010D
	// VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB (3D block, after SUBMIT_3D).
	CmdResourceMapBlob uint32 = 0x0208
	// VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB (3D block).
	CmdResourceUnmapBlob uint32 = 0x0209
	// VIRTIO_GPU_RESP_OK_MAP_INFO — the success response carrying
	// virtio_gpu_resp_map_info (map_info caching type).
	RespOKMapInfo uint32 = 0x1106
)

--- Blob command codes (virtio_gpu.h enum virtio_gpu_ctrl_type) ------

Derived by counting the sequential enum from the explicit anchors. The 2D block anchors at 0x0100; RESOURCE_CREATE_BLOB is the 13th 2D entry (0-based +0x0C). The 3D block anchors at 0x0200; RESOURCE_MAP_BLOB / UNMAP_BLOB follow SUBMIT_3D (0x0207) at +8/+9. RESP_OK_MAP_INFO is the 7th success response after the 0x1100 anchor (+0x06).

View Source
const (
	BlobMemGuest       uint32 = 0x0001
	BlobMemHost3D      uint32 = 0x0002
	BlobMemHost3DGuest uint32 = 0x0003
)

Blob memory types (VIRTIO_GPU_BLOB_MEM_*). HOST3D is the type used for a Venus command ring (host-allocated, host-visible, then guest-mapped via RESOURCE_MAP_BLOB).

View Source
const (
	BlobFlagUseMappable    uint32 = 0x0001
	BlobFlagUseShareable   uint32 = 0x0002
	BlobFlagUseCrossDevice uint32 = 0x0004
)

Blob usage flags (VIRTIO_GPU_BLOB_FLAG_USE_*). USE_MAPPABLE is required for a ring blob the guest will mmap.

View Source
const AcceptedFeatures uint64 = common.FeatureVersion1

AcceptedFeatures is the feature mask the driver negotiates ON — only the non-negotiable VIRTIO_F_VERSION_1. VIRTIO_GPU_F_VIRGL (bit 0) is deliberately NOT accepted, keeping the device in 2D mode.

View Source
const AcceptedFeatures3D uint64 = common.FeatureVersion1 | FeatureVirgl

AcceptedFeatures3D is the feature mask the 3D constructor negotiates ON: VERSION_1 (non-negotiable) plus VIRGL (bit 0).

AcceptedFeaturesVenus is the feature mask a Venus-capable bring-up negotiates ON: VERSION_1 (non-negotiable) plus VIRGL (bit 0, the virtio "3D available" gate the device still advertises), RESOURCE_BLOB (bit 3) and CONTEXT_INIT (bit 4). All three of VIRGL/BLOB/CONTEXT_INIT are needed before a context_init=VENUS context will come up.

View Source
const CommandPollIterations = 200000

CommandPollIterations is the busy-poll budget spent waiting for the device to complete one control command.

View Source
const DeviceType uint16 = 16

DeviceType is the virtio device-type encoding for virtio-gpu (Virtio 1.1 §5.7.1). Retained for callers enumerating PCI devices that want a stable name.

View Source
const FeatureContextInit uint64 = 1 << 4

FeatureContextInit is VIRTIO_GPU_F_CONTEXT_INIT = feature bit index 4: the host honours ctx_create.context_init (the capset selector). Required to stand up a Venus (capset 4) context rather than a default virgl one.

View Source
const FeatureResourceBlob uint64 = 1 << 3

FeatureResourceBlob is VIRTIO_GPU_F_RESOURCE_BLOB = feature bit index 3: the host supports blob resources (RESOURCE_CREATE_BLOB / *_MAP_BLOB). Required by the Venus transport (the command ring is a host-visible blob).

View Source
const FeatureVirgl uint64 = 1 << 0

VIRTIO_GPU_F_VIRGL is feature bit index 0 (Virtio 1.1 §5.7.3): the host advertises it when virglrenderer-backed 3D is available.

Variables

View Source
var (
	ErrNotModernDevice     = commonGPUError("go-virtio/gpu: device doesn't offer VIRTIO_F_VERSION_1 (legacy-only)")
	ErrFeaturesNotOK       = commonGPUError("go-virtio/gpu: FEATURES_OK status bit didn't stick after DriverFeature write")
	ErrInitWrongDeviceID   = commonGPUError("go-virtio/gpu: PCI device ID is not 0x1050 (modern GPU device)")
	ErrQueueNotAvailable   = commonGPUError("go-virtio/gpu: device reports QueueSize=0 for a required queue")
	ErrRequestTimeout      = commonGPUError("go-virtio/gpu: command poll timeout (device did not complete the command)")
	ErrGPUCommandFailed    = commonGPUError("go-virtio/gpu: device returned an error response to a control command")
	ErrNoScanout           = commonGPUError("go-virtio/gpu: no usable scanout (none enabled, or scanout id out of range)")
	ErrFramebufferTooLarge = commonGPUError("go-virtio/gpu: framebuffer dimensions are zero or exceed the supported bound")
)

Sentinel errors for the virtio-gpu path.

View Source
var ErrVirglUnavailable = commonGPUError("go-virtio/gpu: host does not support VIRTIO_GPU_F_VIRGL (no virglrenderer)")

ErrVirglUnavailable is returned by OpenVirtioGPU3D when the host does not support VIRTIO_GPU_F_VIRGL (no virglrenderer): the FEATURES_OK bit does not latch after the VIRGL driver-feature write, or the negotiated mask comes back without the VIRGL bit.

Functions

func AcceptFeatures

func AcceptFeatures(deviceFeatures uint64) (uint64, error)

AcceptFeatures returns the negotiated mask (requires VERSION_1).

func DecodeRespMapInfo added in v0.6.0

func DecodeRespMapInfo(resp []byte) (mapInfo uint32, ok bool)

DecodeRespMapInfo extracts the map_info caching type from a VIRTIO_GPU_RESP_OK_MAP_INFO response (struct virtio_gpu_resp_map_info). It returns (map_info, true) iff resp is at least respMapInfoSize bytes and hdr.type == RespOKMapInfo. The map_info value's MEANING (the host's chosen caching mode) is host-defined and not interpreted here.

func EncodeCtxCreateVenus added in v0.6.0

func EncodeCtxCreateVenus(ctxID uint32, debugName string) []byte

EncodeCtxCreateVenus builds the 96-byte CTX_CREATE request for a Venus context: context_init = CapsetVenus (4). Struct virtio_gpu_ctx_create: ctrl_hdr(24) + le32 nlen@24 + le32 context_init@28 + char debug_name[64]@32.

debugName is copied into debug_name[64] (truncated/zero-padded to 64), and nlen is set to its byte length (capped at 64), mirroring how the kernel's virtio-gpu DRM ioctl populates the field from a context name. For an anonymous Venus context pass debugName="" (nlen=0).

func EncodeResourceCreateBlob added in v0.6.0

func EncodeResourceCreateBlob(p BlobCreateParams) []byte

EncodeResourceCreateBlob builds the 56-byte RESOURCE_CREATE_BLOB request (without any trailing mem_entry array; NrEntries is encoded but the caller appends entries only when BlobMemGuest is used). Field order per struct virtio_gpu_resource_create_blob.

Layout (offsets from start of struct):

hdr.type      @0   = CmdResourceCreateBlob
hdr.ctx_id    @16  = CtxID
resource_id   @24
blob_mem      @28
blob_flags    @32
nr_entries    @36
blob_id       @40  (le64)
size          @48  (le64)

func EncodeResourceMapBlob added in v0.6.0

func EncodeResourceMapBlob(ctxID, resourceID uint32, offset uint64) []byte

EncodeResourceMapBlob builds the 40-byte RESOURCE_MAP_BLOB request. Field order per struct virtio_gpu_resource_map_blob.

Layout:

hdr.type     @0   = CmdResourceMapBlob
hdr.ctx_id   @16  = ctxID
resource_id  @24
padding      @28  = 0
offset       @32  (le64) — the host shmem window offset to map at.

func NegotiateVenusFeatures added in v0.6.0

func NegotiateVenusFeatures(deviceFeatures uint64) (negotiated uint64, ok bool)

NegotiateVenusFeatures returns the subset of AcceptedFeaturesVenus the device actually offers, AND a bool reporting whether the full Venus prerequisite set (VERSION_1 + VIRGL + RESOURCE_BLOB + CONTEXT_INIT) is present. A Venus context CANNOT be stood up unless ok is true. This is a pure mask computation; it performs no device I/O.

Types

type BlobCreateParams added in v0.6.0

type BlobCreateParams struct {
	CtxID      uint32
	ResourceID uint32
	BlobMem    uint32
	BlobFlags  uint32
	NrEntries  uint32
	BlobID     uint64
	Size       uint64
}

BlobCreateParams describes a RESOURCE_CREATE_BLOB request. For a Venus command-ring blob the host3d case is used (BlobMem=HOST3D, Flags=USE_MAPPABLE, BlobID host-meaningful, NrEntries=0 so no guest mem_entry array follows).

type Display

type Display struct {
	ScanoutID     uint32
	Width, Height uint32
	Enabled       bool
}

Display describes one scanout reported by GET_DISPLAY_INFO.

type Framebuffer

type Framebuffer struct {
	// Pix is the BGRA pixel buffer, width*height*4 bytes. The caller
	// writes pixels here, then calls Flush to display them.
	Pix []byte

	Width, Height uint32
	ResourceID    uint32
	// contains filtered or unexported fields
}

Framebuffer is a host 2D resource bound to a scanout, backed by a guest BGRA pixel buffer the caller draws into and then pushes via Flush.

func (*Framebuffer) Flush

func (fb *Framebuffer) Flush() error

Flush pushes the drawn pixels to the host resource and refreshes the scanout: TRANSFER_TO_HOST_2D(rect{0,0,W,H}, offset=0) then RESOURCE_FLUSH(rect{0,0,W,H}).

type VirtioGPU

type VirtioGPU struct {
	// Cfg is the modern-transport handle.
	Cfg *common.ModernConfig

	// NumScanouts is the device's advertised scanout count, read from
	// virtio_gpu_config.num_scanouts at Open.
	NumScanouts uint32

	// NegotiatedFeatures records the driver-feature handshake result.
	NegotiatedFeatures uint64
	// contains filtered or unexported fields
}

VirtioGPU wraps one initialised virtio-gpu device in 2D mode.

func OpenVirtioGPU

func OpenVirtioGPU(t common.Transport) (*VirtioGPU, error)

OpenVirtioGPU drives the full bring-up of one virtio-gpu device:

  1. Verify the PCI device ID is 0x1050 (modern GPU).
  2. InitModernConfig walks PCI caps + populates the BAR locators.
  3. Reset → ACK → DRIVER status progression.
  4. Read DeviceFeature, require VERSION_1, mask to VERSION_1 (NOT VIRGL), write DriverFeature.
  5. Set FEATURES_OK, verify it stuck.
  6. Allocate + publish controlq (queue 0) and cursorq (queue 1).
  7. DRIVER_OK status.
  8. Read num_scanouts (le32) from DeviceCfg offset 8.

func OpenVirtioGPU3D added in v0.3.0

func OpenVirtioGPU3D(t common.Transport) (*VirtioGPU, error)

OpenVirtioGPU3D drives the same bring-up as OpenVirtioGPU but additionally negotiates VIRTIO_GPU_F_VIRGL. If the host does not advertise virgl, the driver-feature write masks the bit off and FEATURES_OK still latches — but the resulting device has no 3D, so callers must check the negotiated mask. More importantly, if FEATURES_OK fails to latch after writing the driver features, the host has rejected the requested set: this is read as "no virglrenderer" and surfaced as ErrVirglUnavailable.

func (*VirtioGPU) ClearScreen added in v0.3.0

func (g *VirtioGPU) ClearScreen(scanoutID uint32, r, gr, b, a float32) error

ClearScreen clears scanout scanoutID to the solid RGBA colour (r,g,b,a) using the host GPU, executing the full M1 virgl sequence:

(a) DisplayInfo to resolve the scanout's width/height (and reject a
    disabled or out-of-range scanout with ErrNoScanout).
(b) CTX_CREATE(ctx=1).
(c) RESOURCE_CREATE_3D(res=1, w, h) — a BGRA render target.
(d) RESOURCE_ATTACH_BACKING(res=1, one mem_entry{phys, w*h*4}).
(e) CTX_ATTACH_RESOURCE(ctx=1, res=1).
(f) SUBMIT_3D(ctx=1, <CREATE SURFACE + SET_FRAMEBUFFER_STATE + CLEAR>).
(g) TRANSFER_TO_HOST_3D(ctx=1, box{0,0,0,w,h,1}, res=1).
(h) SET_SCANOUT(scanoutID, res=1, rect{0,0,w,h}).
(i) RESOURCE_FLUSH(res=1, rect{0,0,w,h}).

Every step checks its response is OK_NODATA; any other response type is ErrGPUCommandFailed.

func (*VirtioGPU) ControlQueue

func (g *VirtioGPU) ControlQueue() *common.Virtqueue

ControlQueue exposes the control virtqueue handle for diagnostics.

func (*VirtioGPU) DisplayInfo

func (g *VirtioGPU) DisplayInfo() ([]Display, error)

DisplayInfo issues GET_DISPLAY_INFO and parses the fixed array of 16 virtio_gpu_display_one entries into one Display per scanout index.

func (*VirtioGPU) DrawTexturedTriangle added in v0.5.0

func (g *VirtioGPU) DrawTexturedTriangle(scanoutID uint32, verts [15]float32, tex []byte, texW, texH uint32) error

DrawTexturedTriangle renders one triangle textured from the host GPU to scanout scanoutID.

VALIDATED against a real virglrenderer (software llvmpipe, via the go-virtio/validate vtest harness): the full textured-triangle stream is accepted and the texture is sampled and perspective-correctly interpolated across the primitive — a 2×2 red/green/blue/white texture yields a smooth multi-texel gradient in the framebuffer readback (24 distinct interior colours), no renderer error. The texture-specific commands (SAMPLER_VIEW / SAMPLER_STATE creation, SET_SAMPLER_VIEWS, BIND_SAMPLER_STATES) and the texcoord varying are all confirmed. This validation caught a real shader bug: the fragment texcoord input defaulted to CONSTANT (flat) interpolation until declared PERSPECTIVE (see fsTexText) — until then the whole triangle sampled a single texel. NOT yet pixel-asserted for exact texel placement. The shaders are shipped as TGSI *text* (virglrenderer re-parses tgsi_dump_str output), not binary tokens.

NEW INFERRED FIELDS in M3 (LOUD — byte-review these before publishing):

  • SAMPLER_VIEW packing: for a non-PIPE_BUFFER target, virgl_encode.c writes dword4 = first_layer | (last_layer<<16) and dword5 = first_level | (last_level<<8). M3 uses all-zero (single layer, single level), so both dwords are 0 — but the *shape* (which field lands in which dword) is inferred from source, not from a known-good capture.
  • SAMPLER_STATE S0 bits: the wrap/filter/compare bit packing (samplerStateS0). The chosen values (CLAMP_TO_EDGE/LINEAR/NONE) are a conventional default, not a captured one.
  • The swizzle dword (samplerViewSwizzle = identity RGBA = 0x688). Bit layout is from VIRGL_OBJ_SAMPLER_VIEW_SWIZZLE_* macros.
  • The texcoord vertex-element format VIRGL_FORMAT_R32G32_FLOAT (=29) and the 2-element layout / stride-20 vertex buffer.
  • The texturing TGSI grammar (DCL SAMP / DCL SVIEW / TEX with the "2D" target token) — parsed by tgsi_text.c, but never exercised here against a real host parser.
  • The sampled texture is created as RGBA8 (R8G8B8A8_UNORM) with the caller's bytes uploaded verbatim; no row-padding / stride handling is attempted (tightly packed texW*texH*4 bytes, stride=0 in the transfer).

verts is 3 vertices, each x,y,z,u,v (5 float32) = 15 float32: clip-space position (already in clip space, e.g. each xyz component in [-1,1]) plus a texture coordinate (u,v in [0,1]). tex is texW*texH*4 RGBA8 bytes.

The sequence mirrors DrawTriangle's host-GPU shape but adds the texture:

(a) resolve scanout dimensions (DisplayInfo).
(b) CTX_CREATE(ctx).
(c) RESOURCE_CREATE_3D(rt, BGRA, RENDER_TARGET) + backing + attach.
(d) RESOURCE_CREATE_3D(vbuf, PIPE_BUFFER, VERTEX_BUFFER) + backing
    (15 floats) + attach + TRANSFER_TO_HOST_3D.
(e) RESOURCE_CREATE_3D(tex, 2D, RGBA8, SAMPLER_VIEW) + backing (texels) +
    attach + TRANSFER_TO_HOST_3D.
(f) one SUBMIT_3D carrying the whole textured draw: create+bind VS/FS,
    create surface, create+bind 2-element vertex-elements / rasterizer /
    blend / dsa, create sampler-view + sampler-state, SET_SAMPLER_VIEWS,
    BIND_SAMPLER_STATES, SET_FRAMEBUFFER_STATE, SET_VIEWPORT_STATE,
    SET_VERTEX_BUFFERS, DRAW_VBO.
(g) TRANSFER_TO_HOST_3D(rt) to pull the rendered pixels into the backing.
(h) SET_SCANOUT(scanoutID, rt) + RESOURCE_FLUSH.

Every step checks its response is OK_NODATA; any other type is ErrGPUCommandFailed.

func (*VirtioGPU) DrawTriangle added in v0.4.0

func (g *VirtioGPU) DrawTriangle(scanoutID uint32, verts [9]float32, color [4]float32) error

VALIDATED against a real virglrenderer (software llvmpipe, via the go-virtio/validate vtest harness on a Debian guest): the full command stream is accepted and rasterizes a triangle — a non-uniform framebuffer readback (corners the background, centre a triangle fragment), with no renderer error. This validation is what caught the VIRGL_CCMD_BIND_SHADER encoding bug (was 32 = SET_TESS_STATE; the live renderer rejected the draw with "Illegal command buffer"; fixed to 31 in v0.5.1) and exercised the VIRGL_OBJECT_SURFACE = 8 fix. NOT yet pixel-asserted for exact geometry/colour — a working triangle is confirmed, a *correct* one is not formally checked. The shaders are shipped as TGSI *text* (virglrenderer re-parses tgsi_dump_str output), not binary tokens.

DrawTriangle renders one flat-shaded triangle to scanout scanoutID using the host GPU. verts is 3 clip-space vertices laid out x0,y0,z0, x1,y1,z1, x2,y2,z2 (the vertex shader passes them straight through to POSITION, so they must already be in clip space — e.g. each component in [-1,1]). color is the flat RGBA the fragment shader emits for every covered pixel.

The sequence mirrors ClearScreen's host-GPU shape but adds the draw pipeline:

(a) resolve scanout dimensions (DisplayInfo).
(b) CTX_CREATE(ctx).
(c) RESOURCE_CREATE_3D(rt, w, h, BGRA, RENDER_TARGET) + backing +
    CTX_ATTACH_RESOURCE.
(d) RESOURCE_CREATE_3D(vbuf, PIPE_BUFFER, VERTEX_BUFFER, w=byteSize) +
    backing (the 9 floats) + CTX_ATTACH_RESOURCE.
(e) TRANSFER_TO_HOST_3D(vbuf) so the host sees the vertices.
(f) one SUBMIT_3D carrying the whole draw: create+bind VS, create+bind
    FS, create surface, create+bind vertex-elements / rasterizer / blend
    / dsa, SET_FRAMEBUFFER_STATE, SET_VIEWPORT_STATE,
    SET_VERTEX_BUFFERS, DRAW_VBO.
(g) TRANSFER_TO_HOST_3D(rt) to pull the rendered pixels into the backing.
(h) SET_SCANOUT(scanoutID, rt) + RESOURCE_FLUSH.

Every step checks its response is OK_NODATA; any other type is ErrGPUCommandFailed.

func (*VirtioGPU) SetupFramebuffer

func (g *VirtioGPU) SetupFramebuffer(scanoutID, width, height uint32) (*Framebuffer, error)

SetupFramebuffer creates a 2D BGRA resource, attaches a guest-allocated backing store, and binds it to scanoutID:

  1. RESOURCE_CREATE_2D(resource_id=1, format=BGRA, width, height).
  2. Allocate ceil(width*height*4 / PageSize) contiguous pages; hold the []byte as Pix (sliced to width*height*4).
  3. RESOURCE_ATTACH_BACKING(resource_id=1, one mem_entry = {phys, size}).
  4. SET_SCANOUT(scanout_id, resource_id=1, rect{0,0,width,height}).

Directories

Path Synopsis
Package soft3d is a minimal, dependency-free software 3D rasterizer.
Package soft3d is a minimal, dependency-free software 3D rasterizer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL