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
- Variables
- func AcceptFeatures(deviceFeatures uint64) (uint64, error)
- func DecodeRespMapInfo(resp []byte) (mapInfo uint32, ok bool)
- func EncodeCtxCreateVenus(ctxID uint32, debugName string) []byte
- func EncodeResourceCreateBlob(p BlobCreateParams) []byte
- func EncodeResourceMapBlob(ctxID, resourceID uint32, offset uint64) []byte
- func NegotiateVenusFeatures(deviceFeatures uint64) (negotiated uint64, ok bool)
- type BlobCreateParams
- type Display
- type Framebuffer
- type VirtioGPU
- func (g *VirtioGPU) ClearScreen(scanoutID uint32, r, gr, b, a float32) error
- func (g *VirtioGPU) ControlQueue() *common.Virtqueue
- func (g *VirtioGPU) DisplayInfo() ([]Display, error)
- func (g *VirtioGPU) DrawTexturedTriangle(scanoutID uint32, verts [15]float32, tex []byte, texW, texH uint32) error
- func (g *VirtioGPU) DrawTriangle(scanoutID uint32, verts [9]float32, color [4]float32) error
- func (g *VirtioGPU) SetupFramebuffer(scanoutID, width, height uint32) (*Framebuffer, error)
Constants ¶
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.
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.
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.
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.
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).
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).
const ( BlobFlagUseMappable uint32 = 0x0001 BlobFlagUseCrossDevice uint32 = 0x0004 )
Blob usage flags (VIRTIO_GPU_BLOB_FLAG_USE_*). USE_MAPPABLE is required for a ring blob the guest will mmap.
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.
const AcceptedFeatures3D uint64 = common.FeatureVersion1 | FeatureVirgl
AcceptedFeatures3D is the feature mask the 3D constructor negotiates ON: VERSION_1 (non-negotiable) plus VIRGL (bit 0).
const AcceptedFeaturesVenus uint64 = common.FeatureVersion1 | FeatureVirgl | FeatureResourceBlob | FeatureContextInit
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.
const CommandPollIterations = 200000
CommandPollIterations is the busy-poll budget spent waiting for the device to complete one control command.
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.
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.
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).
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 ¶
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.
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 ¶
AcceptFeatures returns the negotiated mask (requires VERSION_1).
func DecodeRespMapInfo ¶ added in v0.6.0
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
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
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
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 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 ¶
OpenVirtioGPU drives the full bring-up of one virtio-gpu device:
- Verify the PCI device ID is 0x1050 (modern GPU).
- InitModernConfig walks PCI caps + populates the BAR locators.
- Reset → ACK → DRIVER status progression.
- Read DeviceFeature, require VERSION_1, mask to VERSION_1 (NOT VIRGL), write DriverFeature.
- Set FEATURES_OK, verify it stuck.
- Allocate + publish controlq (queue 0) and cursorq (queue 1).
- DRIVER_OK status.
- Read num_scanouts (le32) from DeviceCfg offset 8.
func OpenVirtioGPU3D ¶ added in v0.3.0
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
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 ¶
ControlQueue exposes the control virtqueue handle for diagnostics.
func (*VirtioGPU) DisplayInfo ¶
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
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:
- RESOURCE_CREATE_2D(resource_id=1, format=BGRA, width, height).
- Allocate ceil(width*height*4 / PageSize) contiguous pages; hold the []byte as Pix (sliced to width*height*4).
- RESOURCE_ATTACH_BACKING(resource_id=1, one mem_entry = {phys, size}).
- SET_SCANOUT(scanout_id, resource_id=1, rect{0,0,width,height}).