blob: fbf948328951b4a579083111d9fe38addf2a353c [file] [log] [blame]
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x11driver
import (
"image"
"sync"
"github.com/jezek/xgb"
"github.com/jezek/xgb/xproto"
"golang.org/x/exp/shiny/driver/internal/swizzle"
)
const (
xPutImageReqSizeMax = (1 << 16) * 4
xPutImageReqSizeFixed = 28
xPutImageReqDataSize = xPutImageReqSizeMax - xPutImageReqSizeFixed
)
type bufferFallbackImpl struct {
xc *xgb.Conn
buf []byte
rgba image.RGBA
size image.Point
mu sync.Mutex
nUpload uint32
released bool
}
func (b *bufferFallbackImpl) Size() image.Point { return b.size }
func (b *bufferFallbackImpl) Bounds() image.Rectangle { return image.Rectangle{Max: b.size} }
func (b *bufferFallbackImpl) RGBA() *image.RGBA { return &b.rgba }
func (b *bufferFallbackImpl) preUpload() {
// Check that the program hasn't tried to modify the rgba field via the
// pointer returned by the bufferFallbackImpl.RGBA method. This check doesn't catch
// 100% of all cases; it simply tries to detect some invalid uses of a
// screen.Buffer such as:
// *buffer.RGBA() = anotherImageRGBA
if len(b.buf) != 0 && len(b.rgba.Pix) != 0 && &b.buf[0] != &b.rgba.Pix[0] {
panic("x11driver: invalid Buffer.RGBA modification")
}
b.mu.Lock()
defer b.mu.Unlock()
if b.released {
panic("x11driver: Buffer.Upload called after Buffer.Release")
}
if b.nUpload == 0 {
swizzle.BGRA(b.buf)
}
b.nUpload++
}
func (b *bufferFallbackImpl) postUpload() {
b.mu.Lock()
defer b.mu.Unlock()
b.nUpload--
if b.nUpload != 0 {
return
}
if !b.released {
swizzle.BGRA(b.buf)
}
}
func (b *bufferFallbackImpl) Release() {
b.mu.Lock()
defer b.mu.Unlock()
b.released = true
}
func (b *bufferFallbackImpl) upload(xd xproto.Drawable, xg xproto.Gcontext, depth uint8, dp image.Point, sr image.Rectangle) {
originalSRMin := sr.Min
sr = sr.Intersect(b.Bounds())
if sr.Empty() {
return
}
dp = dp.Add(sr.Min.Sub(originalSRMin))
b.preUpload()
b.putImage(xd, xg, depth, dp, sr)
b.postUpload()
}
// putImage issues xproto.PutImage requests in batches.
func (b *bufferFallbackImpl) putImage(xd xproto.Drawable, xg xproto.Gcontext, depth uint8, dp image.Point, sr image.Rectangle) {
widthPerReq := b.size.X
rowPerReq := xPutImageReqDataSize / (widthPerReq * 4)
dataPerReq := rowPerReq * widthPerReq * 4
dstX := dp.X
dstY := dp.Y
start := 0
end := 0
for end < len(b.buf) {
end = start + dataPerReq
if end > len(b.buf) {
end = len(b.buf)
}
data := b.buf[start:end]
heightPerReq := len(data) / (widthPerReq * 4)
xproto.PutImage(
b.xc, xproto.ImageFormatZPixmap, xd, xg,
uint16(widthPerReq), uint16(heightPerReq),
int16(dstX), int16(dstY),
0, depth, data)
start = end
dstY += rowPerReq
}
}