diff --git a/gpu/compute.go b/gpu/compute.go index 177a8560..8612cd95 100644 --- a/gpu/compute.go +++ b/gpu/compute.go @@ -557,9 +557,9 @@ restart: size := img.Bounds().Size() driver.UploadImage(a.tex, pos, img) rightPadding := image.Pt(padding, size.Y) - a.tex.Upload(image.Pt(pos.X+size.X, pos.Y), rightPadding, g.zeros(rightPadding.X*rightPadding.Y*4)) + a.tex.Upload(image.Pt(pos.X+size.X, pos.Y), rightPadding, g.zeros(rightPadding.X*rightPadding.Y*4), 0) bottomPadding := image.Pt(size.X, padding) - a.tex.Upload(image.Pt(pos.X, pos.Y+size.Y), bottomPadding, g.zeros(bottomPadding.X*bottomPadding.Y*4)) + a.tex.Upload(image.Pt(pos.X, pos.Y+size.Y), bottomPadding, g.zeros(bottomPadding.X*bottomPadding.Y*4), 0) } return nil } diff --git a/gpu/internal/d3d11/d3d11_windows.go b/gpu/internal/d3d11/d3d11_windows.go index 802ef677..671cdb74 100644 --- a/gpu/internal/d3d11/d3d11_windows.go +++ b/gpu/internal/d3d11/d3d11_windows.go @@ -556,8 +556,10 @@ func (b *Backend) DispatchCompute(x, y, z int) { panic("not implemented") } -func (t *Texture) Upload(offset, size image.Point, pixels []byte) { - stride := size.X * 4 +func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) { + if stride == 0 { + stride = size.X * 4 + } dst := &d3d11.BOX{ Left: uint32(offset.X), Top: uint32(offset.Y), diff --git a/gpu/internal/driver/driver.go b/gpu/internal/driver/driver.go index a66f9e43..982f8715 100644 --- a/gpu/internal/driver/driver.go +++ b/gpu/internal/driver/driver.go @@ -167,7 +167,7 @@ type Timer interface { } type Texture interface { - Upload(offset, size image.Point, pixels []byte) + Upload(offset, size image.Point, pixels []byte, stride int) Release() } @@ -261,11 +261,8 @@ func flipImageY(stride, height int, pixels []byte) { func UploadImage(t Texture, offset image.Point, img *image.RGBA) { var pixels []byte size := img.Bounds().Size() - if img.Stride != size.X*4 { - panic("unsupported stride") - } start := img.PixOffset(0, 0) end := img.PixOffset(size.X, size.Y-1) pixels = img.Pix[start:end] - t.Upload(offset, size, pixels) + t.Upload(offset, size, pixels, img.Stride) } diff --git a/gpu/internal/opengl/opengl.go b/gpu/internal/opengl/opengl.go index a8305216..e31670f4 100644 --- a/gpu/internal/opengl/opengl.go +++ b/gpu/internal/opengl/opengl.go @@ -75,10 +75,11 @@ type glState struct { srcRGB, dstRGB gl.Enum srcA, dstA gl.Enum } - depthTest bool - clearColor [4]float32 - clearDepth float32 - viewport [4]int + depthTest bool + clearColor [4]float32 + clearDepth float32 + viewport [4]int + unpack_row_length int } type state struct { @@ -270,16 +271,17 @@ func (b *Backend) EndFrame() { func (b *Backend) queryState() glState { s := glState{ - prog: gl.Program(b.funcs.GetBinding(gl.CURRENT_PROGRAM)), - arrayBuf: gl.Buffer(b.funcs.GetBinding(gl.ARRAY_BUFFER_BINDING)), - elemBuf: gl.Buffer(b.funcs.GetBinding(gl.ELEMENT_ARRAY_BUFFER_BINDING)), - drawFBO: gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING)), - depthMask: b.funcs.GetInteger(gl.DEPTH_WRITEMASK) != gl.FALSE, - depthTest: b.funcs.IsEnabled(gl.DEPTH_TEST), - depthFunc: gl.Enum(b.funcs.GetInteger(gl.DEPTH_FUNC)), - clearDepth: b.funcs.GetFloat(gl.DEPTH_CLEAR_VALUE), - clearColor: b.funcs.GetFloat4(gl.COLOR_CLEAR_VALUE), - viewport: b.funcs.GetInteger4(gl.VIEWPORT), + prog: gl.Program(b.funcs.GetBinding(gl.CURRENT_PROGRAM)), + arrayBuf: gl.Buffer(b.funcs.GetBinding(gl.ARRAY_BUFFER_BINDING)), + elemBuf: gl.Buffer(b.funcs.GetBinding(gl.ELEMENT_ARRAY_BUFFER_BINDING)), + drawFBO: gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING)), + depthMask: b.funcs.GetInteger(gl.DEPTH_WRITEMASK) != gl.FALSE, + depthTest: b.funcs.IsEnabled(gl.DEPTH_TEST), + depthFunc: gl.Enum(b.funcs.GetInteger(gl.DEPTH_FUNC)), + clearDepth: b.funcs.GetFloat(gl.DEPTH_CLEAR_VALUE), + clearColor: b.funcs.GetFloat4(gl.COLOR_CLEAR_VALUE), + viewport: b.funcs.GetInteger4(gl.VIEWPORT), + unpack_row_length: b.funcs.GetInteger(gl.UNPACK_ROW_LENGTH), } s.blend.enable = b.funcs.IsEnabled(gl.BLEND) s.blend.srcRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_RGB)) @@ -358,6 +360,7 @@ func (b *Backend) restoreState(dst glState) { src.bindBuffer(f, gl.ARRAY_BUFFER, dst.arrayBuf) v := dst.viewport src.setViewport(f, v[0], v[1], v[2], v[3]) + src.pixelStorei(f, gl.UNPACK_ROW_LENGTH, dst.unpack_row_length) } func (s *glState) setVertexAttribArray(f *gl.Functions, idx int, enabled bool) { @@ -559,6 +562,16 @@ func (s *glState) bindBuffer(f *gl.Functions, target gl.Enum, buf gl.Buffer) { f.BindBuffer(target, buf) } +func (s *glState) pixelStorei(f *gl.Functions, pname gl.Enum, val int) { + if pname != gl.UNPACK_ROW_LENGTH { + panic("unsupported PixelStorei pname") + } + if val != s.unpack_row_length { + f.PixelStorei(pname, val) + s.unpack_row_length = val + } +} + func (s *glState) setClearDepth(f *gl.Functions, d float32) { if d != s.clearDepth { f.ClearDepthf(d) @@ -1250,11 +1263,12 @@ func (t *gpuTexture) Release() { t.backend.glstate.deleteTexture(t.backend.funcs, t.obj) } -func (t *gpuTexture) Upload(offset, size image.Point, pixels []byte) { +func (t *gpuTexture) Upload(offset, size image.Point, pixels []byte, stride int) { if min := size.X * size.Y * 4; min > len(pixels) { panic(fmt.Errorf("size %d larger than data %d", min, len(pixels))) } t.backend.BindTexture(0, t) + t.backend.glstate.pixelStorei(t.backend.funcs, gl.UNPACK_ROW_LENGTH, stride/4) t.backend.funcs.TexSubImage2D(gl.TEXTURE_2D, 0, offset.X, offset.Y, size.X, size.Y, t.triple.format, t.triple.typ, pixels) } diff --git a/internal/gl/gl.go b/internal/gl/gl.go index 6b4a3dd7..40da7203 100644 --- a/internal/gl/gl.go +++ b/internal/gl/gl.go @@ -106,6 +106,7 @@ const ( UNIFORM_BUFFER = 0x8A11 UNIFORM_BUFFER_BINDING = 0x8A28 UNPACK_ALIGNMENT = 0xcf5 + UNPACK_ROW_LENGTH = 0x0CF2 UNSIGNED_BYTE = 0x1401 UNSIGNED_SHORT = 0x1403 VIEWPORT = 0x0BA2 diff --git a/internal/gl/gl_js.go b/internal/gl/gl_js.go index 1ccec79f..f071be9d 100644 --- a/internal/gl/gl_js.go +++ b/internal/gl/gl_js.go @@ -343,7 +343,7 @@ func (f *Functions) IsEnabled(cap Enum) bool { func (f *Functions) LinkProgram(p Program) { f.Ctx.Call("linkProgram", js.Value(p)) } -func (f *Functions) PixelStorei(pname Enum, param int32) { +func (f *Functions) PixelStorei(pname Enum, param int) { f.Ctx.Call("pixelStorei", int(pname), param) } func (f *Functions) MemoryBarrier(barriers Enum) { diff --git a/internal/gl/gl_unix.go b/internal/gl/gl_unix.go index 5992d41e..5a7d61e3 100644 --- a/internal/gl/gl_unix.go +++ b/internal/gl/gl_unix.go @@ -1101,7 +1101,7 @@ func (f *Functions) LinkProgram(p Program) { C.glLinkProgram(&f.f, C.GLuint(p.V)) } -func (f *Functions) PixelStorei(pname Enum, param int32) { +func (f *Functions) PixelStorei(pname Enum, param int) { C.glPixelStorei(&f.f, C.GLenum(pname), C.GLint(param)) } diff --git a/internal/gl/gl_windows.go b/internal/gl/gl_windows.go index 434513e3..518f6081 100644 --- a/internal/gl/gl_windows.go +++ b/internal/gl/gl_windows.go @@ -409,7 +409,7 @@ func (f *Functions) IsEnabled(cap Enum) bool { func (c *Functions) LinkProgram(p Program) { syscall.Syscall(_glLinkProgram.Addr(), 1, uintptr(p.V), 0, 0) } -func (c *Functions) PixelStorei(pname Enum, param int32) { +func (c *Functions) PixelStorei(pname Enum, param int) { syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0) } func (f *Functions) MemoryBarrier(barriers Enum) {