Documentation
¶
Overview ¶
Package ggcanvas provides seamless integration between gg 2D graphics and gogpu GPU-accelerated windows.
This package enables drawing 2D UI elements directly in GPU-accelerated windows by managing the CPU-to-GPU pipeline automatically. The data flow is:
gg.Context (draw) -> Pixmap (CPU) -> GPU Texture -> Window
Architecture ¶
Canvas wraps a gg.Context and manages the texture upload pipeline:
- Draw operations use the familiar gg API
- Flush() uploads pixel data to GPU texture
- RenderTo() draws the texture to a gogpu window
Usage ¶
Basic usage with gogpu:
canvas := ggcanvas.New(app.GPUContextProvider(), 800, 600) defer canvas.Close() // Draw with gg API cc := canvas.Context() cc.SetRGB(1, 0, 0) cc.DrawCircle(400, 300, 100) cc.Fill() // Render to gogpu window canvas.RenderTo(dc)
Thread Safety ¶
Canvas is NOT safe for concurrent use. Create one Canvas per goroutine, or use external synchronization.
Performance Notes ¶
- Texture is created lazily on first Flush()
- Dirty tracking avoids unnecessary GPU uploads
- Consider canvas size vs window size for optimal performance
Integration Without Circular Imports ¶
This package uses interfaces to avoid importing gogpu directly:
- gpucontext.DeviceProvider for device access
- Local interfaces for texture creation and drawing
This allows gg to provide integration without creating circular dependencies.
Index ¶
- Variables
- type Canvas
- func MustNew(provider gpucontext.DeviceProvider, width, height int) *Canvas
- func MustNewWithScale(provider gpucontext.DeviceProvider, width, height int, scale float64) *Canvas
- func New(provider gpucontext.DeviceProvider, width, height int) (*Canvas, error)
- func NewWithScale(provider gpucontext.DeviceProvider, width, height int, scale float64) (*Canvas, error)
- func (c *Canvas) Close() error
- func (c *Canvas) Context() *gg.Context
- func (c *Canvas) DeviceScale() float64
- func (c *Canvas) Draw(fn func(*gg.Context)) error
- func (c *Canvas) EnsureGPUTexture(dc RenderTarget) error
- func (c *Canvas) Flush() (any, error)
- func (c *Canvas) FlushPixmap() (any, error)
- func (c *Canvas) Height() int
- func (c *Canvas) IsDirty() bool
- func (c *Canvas) LastDamage() image.Rectangle
- func (c *Canvas) LastDamageRects() []image.Rectangle
- func (c *Canvas) MarkDirty()
- func (c *Canvas) MarkDirtyRegion(r image.Rectangle)
- func (c *Canvas) NeedsAnimationFrame() bool
- func (c *Canvas) PixmapTextureView() gpucontext.TextureView
- func (c *Canvas) Provider() gpucontext.DeviceProvider
- func (c *Canvas) Render(dc RenderTarget) error
- func (c *Canvas) RenderDirect(surfaceView gpucontext.TextureView, width, height uint32) error
- func (c *Canvas) RenderDirectWithDamage(surfaceView gpucontext.TextureView, width, height uint32, ...) error
- func (c *Canvas) RenderDirectWithDamageRects(surfaceView gpucontext.TextureView, width, height uint32, ...) error
- func (c *Canvas) RenderTo(dc gpucontext.TextureDrawer) error
- func (c *Canvas) RenderToEx(dc gpucontext.TextureDrawer, opts RenderOptions) error
- func (c *Canvas) RenderToPosition(dc gpucontext.TextureDrawer, x, y float32) error
- func (c *Canvas) RenderToScaled(dc gpucontext.TextureDrawer, scale float32) error
- func (c *Canvas) Resize(width, height int) error
- func (c *Canvas) SetDeviceScale(scale float64)
- func (c *Canvas) SetPresentDamage(rects []image.Rectangle)
- func (c *Canvas) Size() (width, height int)
- func (c *Canvas) Texture() any
- func (c *Canvas) Width() int
- type DamageRectSetter
- type RenderOptions
- type RenderTarget
Constants ¶
This section is empty.
Variables ¶
var ( // ErrCanvasClosed is returned when operations are attempted on a closed canvas. ErrCanvasClosed = errors.New("ggcanvas: canvas is closed") // ErrInvalidDimensions is returned when width or height is invalid. ErrInvalidDimensions = errors.New("ggcanvas: invalid dimensions") // ErrNilProvider is returned when a nil DeviceProvider is passed. ErrNilProvider = errors.New("ggcanvas: nil DeviceProvider") // ErrTextureCreationFailed is returned when texture creation fails. ErrTextureCreationFailed = errors.New("ggcanvas: texture creation failed") )
Common errors returned by Canvas operations.
var ( // ErrInvalidDrawContext is returned when the draw context doesn't implement // gpucontext.TextureDrawer. ErrInvalidDrawContext = errors.New("ggcanvas: dc must implement gpucontext.TextureDrawer") // ErrInvalidRenderer is returned when the renderer doesn't implement // gpucontext.TextureCreator. ErrInvalidRenderer = errors.New("ggcanvas: renderer must implement gpucontext.TextureCreator") )
Rendering errors.
Functions ¶
This section is empty.
Types ¶
type Canvas ¶
type Canvas struct {
// contains filtered or unexported fields
}
Canvas wraps gg.Context with gogpu integration. It manages the CPU-to-GPU pipeline automatically.
Canvas is NOT safe for concurrent use. Create one Canvas per goroutine, or use external synchronization.
func MustNew ¶
func MustNew(provider gpucontext.DeviceProvider, width, height int) *Canvas
MustNew is like New but panics on error. Use only when errors are programming mistakes (e.g., hardcoded dimensions).
func MustNewWithScale ¶ added in v0.34.0
func MustNewWithScale(provider gpucontext.DeviceProvider, width, height int, scale float64) *Canvas
MustNewWithScale is like NewWithScale but panics on error.
func New ¶
func New(provider gpucontext.DeviceProvider, width, height int) (*Canvas, error)
New creates a Canvas for integrated mode. The provider should come from gogpu.App.GPUContextProvider(). The width and height are logical dimensions.
If the provider also implements gpucontext.WindowProvider, the device scale is auto-detected for HiDPI/Retina support. Otherwise defaults to 1.0. Use Context() to access and configure the drawing context.
Returns error if dimensions are invalid or provider is nil.
func NewWithScale ¶ added in v0.34.0
func NewWithScale(provider gpucontext.DeviceProvider, width, height int, scale float64) (*Canvas, error)
NewWithScale creates a Canvas with HiDPI device scale support. The width and height are logical dimensions. The internal pixmap is allocated at physical resolution (width*scale x height*scale).
The provider should come from gogpu.App.GPUContextProvider(). Scale factor should come from the platform (e.g., gogpu.Context.ScaleFactor()). Typical values: 1.0 (standard), 2.0 (macOS Retina), 3.0 (mobile HiDPI).
Example:
scale := dc.ScaleFactor() // from gogpu.Context canvas, err := ggcanvas.NewWithScale(provider, 800, 600, scale)
Returns error if dimensions are invalid, provider is nil, or scale <= 0.
func (*Canvas) Close ¶
Close releases all resources associated with the Canvas. After Close, the Canvas should not be used. Close is idempotent - multiple calls are safe.
func (*Canvas) Context ¶
Context returns the gg drawing context. All gg drawing methods are available through this context.
After drawing, call MarkDirty() to flag the canvas for GPU upload, or call Flush() which handles this automatically.
Returns nil if the canvas is closed.
func (*Canvas) DeviceScale ¶ added in v0.34.0
DeviceScale returns the current device scale factor. Returns 1.0 if the canvas was created without HiDPI support.
func (*Canvas) Draw ¶ added in v0.28.0
Draw calls fn with the gg context and marks the canvas as dirty. This is the recommended way to update canvas content, as it ensures the dirty flag is set correctly for GPU upload on next Flush/RenderTo.
BeginAcceleratorFrame is called before fn to reset per-frame GPU state. This ensures the first render pass clears the surface while mid-frame CPU fallback flushes (bitmap text, gradient fill) use LoadOpLoad to preserve previously drawn content. See RENDER-DIRECT-003.
Per-frame state (matrix, path, clip, mask) is automatically reset via Push/Pop wrapper (Skia SkAutoCanvasRestore pattern, ADR-032). Configuration state (font, paint color, textMode) persists across frames.
func (*Canvas) EnsureGPUTexture ¶ added in v0.43.0
func (c *Canvas) EnsureGPUTexture(dc RenderTarget) error
EnsureGPUTexture promotes the internal pendingTexture to a real GPU texture if needed. After this call, PixmapTextureView() returns non-nil.
Call this once after the first FlushPixmap() to create the GPU texture. Subsequent calls are no-ops (texture already promoted). The RenderTarget provides TextureCreator for GPU texture allocation.
This is the setup step for zero-readback compositing:
canvas.FlushPixmap() // upload pixmap (no GPU readback) canvas.EnsureGPUTexture(dc.RenderTarget()) // promote once view := canvas.PixmapTextureView() // now non-nil cc.DrawGPUTextureBase(view, ...) // base layer cc.FlushGPUWithView(surface, ...) // single pass
func (*Canvas) Flush ¶
Flush uploads the canvas content to GPU texture if dirty. Returns the texture for manual drawing if needed.
This first calls FlushGPU() to render any pending GPU-accelerated shapes (SDF, stencil, text) back into the CPU pixmap, then uploads the pixmap to a GPU texture. For zero-readback rendering, use FlushPixmap() instead.
The texture is created lazily on first Flush(). Subsequent calls only upload data if dirty flag is set.
Returns error if texture creation or update fails, or if canvas is closed.
func (*Canvas) FlushPixmap ¶ added in v0.43.0
FlushPixmap uploads the CPU pixmap to GPU texture without flushing GPU shapes. Unlike Flush(), this does NOT call FlushGPU() — pending GPU-accelerated shapes remain queued in GPURenderContext for the caller to flush separately (e.g., via FlushGPUWithView for zero-readback rendering to a surface view).
Use this when GPU shapes should render directly to the display surface instead of being read back into the CPU pixmap. See ADR-006.
func (*Canvas) IsDirty ¶
IsDirty returns true if the canvas has pending changes that need to be uploaded to the GPU.
func (*Canvas) LastDamage ¶ added in v0.45.0
LastDamage returns the damage rectangle (union) from the most recent frame.
func (*Canvas) LastDamageRects ¶ added in v0.45.0
LastDamageRects returns individual damage rectangles from the most recent frame.
func (*Canvas) MarkDirty ¶
func (c *Canvas) MarkDirty()
MarkDirty flags the canvas for GPU upload on next Flush(). Call this after drawing operations if you want explicit control over when uploads happen.
MarkDirty invalidates the entire canvas. For partial invalidation, use MarkDirtyRegion to upload only the changed region.
func (*Canvas) MarkDirtyRegion ¶ added in v0.41.0
MarkDirtyRegion flags a rectangular region of the canvas as dirty. On the next Flush(), only the accumulated dirty region is uploaded to the GPU (if the texture supports partial upload), which can be significantly faster than uploading the entire pixmap.
Multiple calls accumulate into the bounding rectangle of all dirty regions. The region is in physical pixel coordinates (after device scale).
func (*Canvas) NeedsAnimationFrame ¶ added in v0.45.2
NeedsAnimationFrame reports whether the canvas needs another frame for debug overlay fade animation. Caller should RequestRedraw if true.
func (*Canvas) PixmapTextureView ¶ added in v0.43.0
func (c *Canvas) PixmapTextureView() gpucontext.TextureView
PixmapTextureView returns the GPU texture view of the uploaded pixmap. Returns nil if the pixmap has not been uploaded yet (call FlushPixmap first) or if the texture does not expose a view (e.g., pendingTexture before promotion).
Use this with DrawGPUTextureBase for single-pass zero-readback compositing:
canvas.FlushPixmap() // upload, no GPU readback view := canvas.PixmapTextureView() // get GPU texture view cc.DrawGPUTextureBase(view, 0, 0, w, h) // base layer cc.FlushGPUWithView(surfaceView, sw, sh) // single pass compositor
The view is valid until the texture is destroyed (resize, close). Uses Go structural typing — no gogpu import required.
func (*Canvas) Provider ¶
func (c *Canvas) Provider() gpucontext.DeviceProvider
Provider returns the DeviceProvider associated with this canvas. Returns nil if the canvas is closed.
func (*Canvas) Render ¶ added in v0.37.3
func (c *Canvas) Render(dc RenderTarget) error
Render presents canvas content to the screen. Works on all backends.
On GPU backends (Vulkan, DX12, Metal, GLES): renders directly to surface via GPU shaders (zero-copy, optimal performance).
On software backend or when GPU-direct fails: falls back to universal path where gg CPU rasterizer renders to pixmap, uploads to texture, and presents via textured quad.
This is the recommended way to present canvas content — one call, all backends.
canvas.Draw(func(cc *gg.Context) { ... })
canvas.Render(dc) // dc is *gogpu.Context
func (*Canvas) RenderDirect ¶ added in v0.28.0
func (c *Canvas) RenderDirect(surfaceView gpucontext.TextureView, width, height uint32) error
RenderDirect renders canvas content directly to the given surface view, bypassing the GPU->CPU->GPU readback. This is the zero-copy rendering path for use with gogpu's surface texture view.
When the GPU accelerator supports direct surface rendering, shapes are rendered directly to the provided surface view via MSAA resolve. No staging buffers, no ReadBuffer, no texture upload -- pure GPU-to-GPU.
If the accelerator doesn't support surface rendering, or if no GPU accelerator is registered, this method falls back to the readback path via Flush().
The surfaceView is a type-safe opaque handle obtained from dc.RenderTarget().SurfaceView(). Pass a zero-value (IsNil() == true) to use the readback path.
Example:
app.OnDraw(func(dc *gogpu.Context) {
canvas.Draw(func(cc *gg.Context) { ... })
w, h := dc.SurfaceSize()
canvas.RenderDirect(dc.RenderTarget().SurfaceView(), w, h)
})
func (*Canvas) RenderDirectWithDamage ¶ added in v0.46.5
func (c *Canvas) RenderDirectWithDamage(surfaceView gpucontext.TextureView, width, height uint32, damage image.Rectangle) error
RenderDirectWithDamage renders canvas content to a surface view with a damage rect hint. Only the damaged region is re-rendered; the rest preserves the previous frame (LoadOpLoad + scissor). This enables per-boundary incremental updates without full-frame re-render.
Use when only a subset of GPU content changed (e.g., a single RepaintBoundary item in a compositor). Pass image.Rectangle{} for full-frame render.
func (*Canvas) RenderDirectWithDamageRects ¶ added in v0.46.7
func (c *Canvas) RenderDirectWithDamageRects(surfaceView gpucontext.TextureView, width, height uint32, rects []image.Rectangle) error
RenderDirectWithDamageRects renders with multiple damage rects (ADR-028). Each rect gets its own scissor — per-draw dynamic scissor for distant dirty regions. Falls back to single-rect behavior when len(rects) <= 1.
func (*Canvas) RenderTo ¶
func (c *Canvas) RenderTo(dc gpucontext.TextureDrawer) error
RenderTo draws the canvas content to a gpucontext.TextureDrawer. This is the primary integration method.
The dc parameter should be obtained from gogpu.Context.AsTextureDrawer(). The canvas content is flushed to GPU and drawn at position (0, 0).
Example:
app.OnDraw(func(dc *gogpu.Context) {
canvas.RenderTo(dc.AsTextureDrawer())
})
Returns error if:
- Canvas is closed
- Texture creation or drawing fails
func (*Canvas) RenderToEx ¶
func (c *Canvas) RenderToEx(dc gpucontext.TextureDrawer, opts RenderOptions) error
RenderToEx draws the canvas with additional options. Use this when you need positioning, scaling, or transparency control.
Example:
opts := ggcanvas.RenderOptions{
X: 100, Y: 50,
ScaleX: 0.5, ScaleY: 0.5,
Alpha: 0.8,
}
canvas.RenderToEx(dc.AsTextureDrawer(), opts)
func (*Canvas) RenderToPosition ¶
func (c *Canvas) RenderToPosition(dc gpucontext.TextureDrawer, x, y float32) error
RenderToPosition is a convenience method for rendering at a specific position.
canvas.RenderToPosition(dc.AsTextureDrawer(), 100, 50)
is equivalent to:
canvas.RenderToEx(dc.AsTextureDrawer(), RenderOptions{X: 100, Y: 50, ScaleX: 1, ScaleY: 1, Alpha: 1})
func (*Canvas) RenderToScaled ¶
func (c *Canvas) RenderToScaled(dc gpucontext.TextureDrawer, scale float32) error
RenderToScaled is a convenience method for rendering with uniform scaling.
canvas.RenderToScaled(dc.AsTextureDrawer(), 0.5) // Render at half size
func (*Canvas) Resize ¶
Resize changes canvas dimensions. This recreates internal buffers and clears the canvas.
Returns error if dimensions are invalid or canvas is closed.
func (*Canvas) SetDeviceScale ¶ added in v0.34.0
SetDeviceScale changes the device scale factor on the canvas. This delegates to the gg.Context and marks the canvas for re-upload. Scale must be > 0; values <= 0 are ignored.
func (*Canvas) SetPresentDamage ¶ added in v0.45.4
SetPresentDamage sets damage rectangles for the next present call (ADR-021 Level 4). Rects are in physical pixels with top-left origin. They are forwarded to gogpu SetDamageRects() → wgpu PresentWithDamage() → OS compositor hint (VK_KHR_incremental_present, DX12 Present1, eglSwapBuffersWithDamage).
Callers with retained-mode knowledge (e.g. ui widget tree) should provide BOTH old and new bounds of moved/resized objects. Immediate-mode callers can pass FrameDamage() rects (new positions only) when old positions are covered by full-surface redraw.
Rects are consumed after one present and do not persist across frames. When nil or empty, the full surface is presented (backward compatible).
type DamageRectSetter ¶ added in v0.45.0
DamageRectSetter is an optional interface for RenderTargets that support damage-aware presentation (ADR-021 Level 3-4). gogpu.ContextRenderTarget implements this via Context.SetDamageRects().
type RenderOptions ¶
type RenderOptions struct {
// X, Y is the position to draw the texture (default: 0, 0)
X, Y float32
// ScaleX, ScaleY are the scale factors (default: 1, 1)
// Values < 1 shrink, values > 1 enlarge
ScaleX float32
ScaleY float32
// Alpha is the opacity from 0 (transparent) to 1 (opaque) (default: 1)
Alpha float32
// FlipY flips the texture vertically (default: false)
// Useful when coordinate systems differ between gg and GPU
FlipY bool
}
RenderOptions controls how canvas is rendered to the target.
func DefaultRenderOptions ¶
func DefaultRenderOptions() RenderOptions
DefaultRenderOptions returns options with sensible defaults.
type RenderTarget ¶ added in v0.37.3
type RenderTarget interface {
SurfaceView() gpucontext.TextureView
SurfaceSize() (uint32, uint32)
PresentTexture(tex any) error
}
RenderTarget is the interface for presenting canvas content on screen. Implement this on your application context. *gogpu.Context satisfies this via the gogpu.RenderTarget() adapter.