gpu,gpu/internal: support variable strides in ReadPixels

It saves a roundtrip to scratch memory when the CPU fallback renderer
downloads rendered materials.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-08-26 19:18:29 +02:00
parent 06ffa42d16
commit 7f6e376424
6 changed files with 25 additions and 23 deletions
+1 -11
View File
@@ -95,7 +95,6 @@ type compute struct {
frag struct { frag struct {
buf driver.Buffer buf driver.Buffer
} }
scratch []byte
} }
timers struct { timers struct {
profile string profile string
@@ -1006,20 +1005,11 @@ func (g *compute) renderMaterials() error {
copyFBO := atlas.fbo copyFBO := atlas.fbo
data := atlas.cpuImage.Data() data := atlas.cpuImage.Data()
for _, r := range m.regions { for _, r := range m.regions {
dims := r.Size()
if n := dims.X * dims.Y * 4; n > len(m.scratch) {
m.scratch = make([]byte, n)
}
copyFBO.ReadPixels(r, m.scratch)
stride := atlas.size.X * 4 stride := atlas.size.X * 4
col := r.Min.X * 4 col := r.Min.X * 4
row := stride * r.Min.Y row := stride * r.Min.Y
off := col + row off := col + row
w := dims.X * 4 copyFBO.ReadPixels(r, data[off:], stride)
for y := 0; y < dims.Y; y++ {
copy(data[off:off+w], m.scratch[y*dims.X*4:])
off += stride
}
} }
} }
return nil return nil
+2 -2
View File
@@ -593,7 +593,7 @@ func (b *Buffer) Release() {
b.buf = nil b.buf = nil
} }
func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error { func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
if f.resource == nil { if f.resource == nil {
return errors.New("framebuffer does not support ReadPixels") return errors.New("framebuffer does not support ReadPixels")
} }
@@ -636,7 +636,7 @@ func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
return fmt.Errorf("ReadPixels: %v", err) return fmt.Errorf("ReadPixels: %v", err)
} }
defer f.ctx.Unmap(res, 0) defer f.ctx.Unmap(res, 0)
srcPitch := w * 4 srcPitch := stride
dstPitch := int(resMap.RowPitch) dstPitch := int(resMap.RowPitch)
mapSize := dstPitch * h mapSize := dstPitch * h
data := sliceOf(resMap.PData, mapSize) data := sliceOf(resMap.PData, mapSize)
+2 -2
View File
@@ -136,7 +136,7 @@ type Buffer interface {
type Framebuffer interface { type Framebuffer interface {
RenderTarget RenderTarget
Release() Release()
ReadPixels(src image.Rectangle, pixels []byte) error ReadPixels(src image.Rectangle, pixels []byte, stride int) error
} }
type Timer interface { type Timer interface {
@@ -212,7 +212,7 @@ func (f Features) Has(feats Features) bool {
func DownloadImage(d Device, f Framebuffer, r image.Rectangle) (*image.RGBA, error) { func DownloadImage(d Device, f Framebuffer, r image.Rectangle) (*image.RGBA, error) {
img := image.NewRGBA(r) img := image.NewRGBA(r)
if err := f.ReadPixels(r, img.Pix); err != nil { if err := f.ReadPixels(r, img.Pix, img.Stride); err != nil {
return nil, err return nil, err
} }
if d.Caps().BottomLeftOrigin { if d.Caps().BottomLeftOrigin {
+1 -2
View File
@@ -1121,7 +1121,7 @@ func (b *Buffer) Release() {
*b = Buffer{} *b = Buffer{}
} }
func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error { func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
if len(pixels) == 0 { if len(pixels) == 0 {
return nil return nil
} }
@@ -1135,7 +1135,6 @@ func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
height: C.NSUInteger(sz.Y), height: C.NSUInteger(sz.Y),
depth: 1, depth: 1,
} }
stride := 4 * sz.X
buf, off := f.backend.stagingBuffer(len(pixels)) buf, off := f.backend.stagingBuffer(len(pixels))
enc := f.backend.startBlit() enc := f.backend.startBlit()
C.blitEncCopyTextureToBuffer(enc, f.texture, buf, C.NSUInteger(off), C.NSUInteger(stride), C.NSUInteger(len(pixels)), msize, orig) C.blitEncCopyTextureToBuffer(enc, f.texture, buf, C.NSUInteger(off), C.NSUInteger(stride), C.NSUInteger(len(pixels)), msize, orig)
+18 -6
View File
@@ -80,6 +80,7 @@ type glState struct {
clearColor [4]float32 clearColor [4]float32
viewport [4]int viewport [4]int
unpack_row_length int unpack_row_length int
pack_row_length int
} }
type state struct { type state struct {
@@ -297,6 +298,7 @@ func (b *Backend) queryState() glState {
clearColor: b.funcs.GetFloat4(gl.COLOR_CLEAR_VALUE), clearColor: b.funcs.GetFloat4(gl.COLOR_CLEAR_VALUE),
viewport: b.funcs.GetInteger4(gl.VIEWPORT), viewport: b.funcs.GetInteger4(gl.VIEWPORT),
unpack_row_length: b.funcs.GetInteger(gl.UNPACK_ROW_LENGTH), unpack_row_length: b.funcs.GetInteger(gl.UNPACK_ROW_LENGTH),
pack_row_length: b.funcs.GetInteger(gl.PACK_ROW_LENGTH),
} }
s.blend.enable = b.funcs.IsEnabled(gl.BLEND) s.blend.enable = b.funcs.IsEnabled(gl.BLEND)
s.blend.srcRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_RGB)) s.blend.srcRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_RGB))
@@ -372,6 +374,7 @@ func (b *Backend) restoreState(dst glState) {
v := dst.viewport v := dst.viewport
src.setViewport(f, v[0], v[1], v[2], v[3]) src.setViewport(f, v[0], v[1], v[2], v[3])
src.pixelStorei(f, gl.UNPACK_ROW_LENGTH, dst.unpack_row_length) src.pixelStorei(f, gl.UNPACK_ROW_LENGTH, dst.unpack_row_length)
src.pixelStorei(f, gl.PACK_ROW_LENGTH, dst.pack_row_length)
} }
func (s *glState) setVertexAttribArray(f *gl.Functions, idx int, enabled bool) { func (s *glState) setVertexAttribArray(f *gl.Functions, idx int, enabled bool) {
@@ -574,13 +577,21 @@ func (s *glState) bindBuffer(f *gl.Functions, target gl.Enum, buf gl.Buffer) {
} }
func (s *glState) pixelStorei(f *gl.Functions, pname gl.Enum, val int) { func (s *glState) pixelStorei(f *gl.Functions, pname gl.Enum, val int) {
if pname != gl.UNPACK_ROW_LENGTH { switch pname {
case gl.UNPACK_ROW_LENGTH:
if val == s.unpack_row_length {
return
}
s.unpack_row_length = val
case gl.PACK_ROW_LENGTH:
if val == s.pack_row_length {
return
}
s.pack_row_length = val
default:
panic("unsupported PixelStorei pname") panic("unsupported PixelStorei pname")
} }
if val != s.unpack_row_length { f.PixelStorei(pname, val)
f.PixelStorei(pname, val)
s.unpack_row_length = val
}
} }
func (s *glState) setClearColor(f *gl.Functions, r, g, b, a float32) { func (s *glState) setClearColor(f *gl.Functions, r, g, b, a float32) {
@@ -1181,12 +1192,13 @@ func (b *Backend) CopyTexture(dst driver.Texture, dstOrigin image.Point, src dri
b.funcs.CopyTexSubImage2D(gl.TEXTURE_2D, 0, dstOrigin.X, dstOrigin.Y, srcRect.Min.X, srcRect.Min.Y, sz.X, sz.Y) b.funcs.CopyTexSubImage2D(gl.TEXTURE_2D, 0, dstOrigin.X, dstOrigin.Y, srcRect.Min.X, srcRect.Min.Y, sz.X, sz.Y)
} }
func (f *framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error { func (f *framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
glErr(f.backend.funcs) glErr(f.backend.funcs)
f.backend.BindFramebuffer(f, driver.LoadDesc{}) f.backend.BindFramebuffer(f, driver.LoadDesc{})
if len(pixels) < src.Dx()*src.Dy()*4 { if len(pixels) < src.Dx()*src.Dy()*4 {
return errors.New("unexpected RGBA size") return errors.New("unexpected RGBA size")
} }
f.backend.glstate.pixelStorei(f.backend.funcs, gl.PACK_ROW_LENGTH, stride/4)
f.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, src.Dx(), src.Dy(), gl.RGBA, gl.UNSIGNED_BYTE, pixels) f.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, src.Dx(), src.Dy(), gl.RGBA, gl.UNSIGNED_BYTE, pixels)
return glErr(f.backend.funcs) return glErr(f.backend.funcs)
} }
+1
View File
@@ -65,6 +65,7 @@ const (
NUM_EXTENSIONS = 0x821D NUM_EXTENSIONS = 0x821D
ONE = 0x1 ONE = 0x1
ONE_MINUS_SRC_ALPHA = 0x303 ONE_MINUS_SRC_ALPHA = 0x303
PACK_ROW_LENGTH = 0x0D02
PROGRAM_BINARY_LENGTH = 0x8741 PROGRAM_BINARY_LENGTH = 0x8741
QUERY_RESULT = 0x8866 QUERY_RESULT = 0x8866
QUERY_RESULT_AVAILABLE = 0x8867 QUERY_RESULT_AVAILABLE = 0x8867