gpu/internal/driver,gpu/headless: don't y-axis flip OpenGL ReadPixels images

The CPU fallback of the compute renderer needs ReadPixels data in OpenGL
format (origin at bottom left). Unfortunately, the OpenGL driver
automatically mirrors images in the Y-axis to match the top left origin
image.RGBA.

Remove the mirroring from the driver and introduce a DownloadImage to
restore the old behaviour.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-03-08 18:12:33 +01:00
parent 2aa1cc8112
commit 69cff4b96b
4 changed files with 44 additions and 35 deletions
+7 -11
View File
@@ -26,7 +26,7 @@ func TestFramebufferClear(t *testing.T) {
b := newBackend(t)
sz := image.Point{X: 800, Y: 600}
fbo := setupFBO(t, b, sz)
img := screenshot(t, fbo, sz)
img := screenshot(t, b, fbo, sz)
if got := img.RGBAAt(0, 0); got != clearColExpect {
t.Errorf("got color %v, expected %v", got, clearColExpect)
}
@@ -43,7 +43,7 @@ func TestSimpleShader(t *testing.T) {
defer p.Release()
b.BindProgram(p)
b.DrawArrays(driver.DrawModeTriangles, 0, 3)
img := screenshot(t, fbo, sz)
img := screenshot(t, b, fbo, sz)
if got := img.RGBAAt(0, 0); got != clearColExpect {
t.Errorf("got color %v, expected %v", got, clearColExpect)
}
@@ -90,7 +90,7 @@ func TestInputShader(t *testing.T) {
defer layout.Release()
b.BindInputLayout(layout)
b.DrawArrays(driver.DrawModeTriangles, 0, 3)
img := screenshot(t, fbo, sz)
img := screenshot(t, b, fbo, sz)
if got := img.RGBAAt(0, 0); got != clearColExpect {
t.Errorf("got color %v, expected %v", got, clearColExpect)
}
@@ -115,11 +115,11 @@ func TestFramebuffers(t *testing.T) {
b.Clear(fcol1.Float32())
b.BindFramebuffer(fbo2)
b.Clear(fcol2.Float32())
img := screenshot(t, fbo1, sz)
img := screenshot(t, b, fbo1, sz)
if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col1) {
t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col1))
}
img = screenshot(t, fbo2, sz)
img = screenshot(t, b, fbo2, sz)
if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col2) {
t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col2))
}
@@ -184,12 +184,8 @@ func newBackend(t *testing.T) driver.Device {
return b
}
func screenshot(t *testing.T, fbo driver.Framebuffer, size image.Point) *image.RGBA {
img := image.NewRGBA(image.Rectangle{Max: size})
err := fbo.ReadPixels(
image.Rectangle{
Max: image.Point{X: size.X, Y: size.Y},
}, img.Pix)
func screenshot(t *testing.T, d driver.Device, fbo driver.Framebuffer, size image.Point) *image.RGBA {
img, err := driver.DownloadImage(d, fbo, image.Rectangle{Max: size})
if err != nil {
t.Fatal(err)
}
+4 -5
View File
@@ -118,12 +118,11 @@ func (w *Window) Frame(frame *op.Ops) error {
// Screenshot returns an image with the content of the window.
func (w *Window) Screenshot() (*image.RGBA, error) {
img := image.NewRGBA(image.Rectangle{Max: w.size})
var img *image.RGBA
err := contextDo(w.ctx, func() error {
return w.fbo.ReadPixels(
image.Rectangle{
Max: image.Point{X: w.size.X, Y: w.size.Y},
}, img.Pix)
var err error
img, err = driver.DownloadImage(w.dev, w.fbo, image.Rectangle{Max: w.size})
return err
})
if err != nil {
return nil, err
+32 -2
View File
@@ -138,8 +138,11 @@ type DepthFunc uint8
type Features uint
type Caps struct {
Features Features
MaxTextureSize int
// BottomLeftOrigin is true if the driver has the origin in the lower left
// corner. The OpenGL driver returns true.
BottomLeftOrigin bool
Features Features
MaxTextureSize int
}
type Program interface {
@@ -233,6 +236,33 @@ func (f Features) Has(feats Features) bool {
return f&feats == feats
}
func DownloadImage(d Device, f Framebuffer, r image.Rectangle) (*image.RGBA, error) {
img := image.NewRGBA(r)
if err := f.ReadPixels(r, img.Pix); err != nil {
return nil, err
}
if d.Caps().BottomLeftOrigin {
// OpenGL origin is in the lower-left corner. Flip the image to
// match.
flipImageY(r.Dx()*4, r.Dy(), img.Pix)
}
return img, nil
}
func flipImageY(stride, height int, pixels []byte) {
// Flip image in y-direction. OpenGL's origin is in the lower
// left corner.
row := make([]uint8, stride)
for y := 0; y < height/2; y++ {
y1 := height - y - 1
dest := y1 * stride
src := y * stride
copy(row, pixels[dest:])
copy(pixels[dest:], pixels[src:src+len(row)])
copy(pixels[src:], row)
}
}
func UploadImage(t Texture, offset image.Point, img *image.RGBA) {
var pixels []byte
size := img.Bounds().Size()
+1 -17
View File
@@ -154,6 +154,7 @@ func newOpenGLDevice(api driver.OpenGL) (driver.Device, error) {
alphaTriple: alphaTripleFor(ver),
srgbaTriple: srgbaTriple,
}
b.feats.BottomLeftOrigin = true
if ffboErr == nil {
b.feats.Features |= driver.FeatureFloatRenderTargets
}
@@ -771,26 +772,9 @@ func (f *gpuFramebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
return errors.New("unexpected RGBA size")
}
f.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, src.Dx(), src.Dy(), gl.RGBA, gl.UNSIGNED_BYTE, pixels)
// OpenGL origin is in the lower-left corner. Flip the image to
// match.
flipImageY(src.Dx()*4, src.Dy(), pixels)
return glErr(f.backend.funcs)
}
func flipImageY(stride int, height int, pixels []byte) {
// Flip image in y-direction. OpenGL's origin is in the lower
// left corner.
row := make([]uint8, stride)
for y := 0; y < height/2; y++ {
y1 := height - y - 1
dest := y1 * stride
src := y * stride
copy(row, pixels[dest:])
copy(pixels[dest:], pixels[src:src+len(row)])
copy(pixels[src:], row)
}
}
func (b *Backend) BindFramebuffer(fbo driver.Framebuffer) {
b.funcs.BindFramebuffer(gl.FRAMEBUFFER, fbo.(*gpuFramebuffer).obj)
}