From ed028110cfbe0b0b45e520e14c8ef7448dc2d473 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 7 Nov 2020 19:14:06 +0900 Subject: [PATCH] ebiten: Allow rendering on a sub-image by scissor test Fixes #1255 --- examples/clip/main.go | 95 +++++++++++++ image.go | 50 ++++--- image_test.go | 26 ++++ internal/buffered/image.go | 6 +- internal/driver/graphics.go | 4 +- internal/graphicscommand/command.go | 91 ++++++------ internal/graphicscommand/image.go | 4 +- internal/graphicscommand/image_test.go | 28 +++- internal/graphicsdriver/metal/graphics.go | 28 ++-- internal/graphicsdriver/metal/mtl/mtl.go | 26 ++++ internal/graphicsdriver/metal/mtl/mtl.h | 9 ++ internal/graphicsdriver/metal/mtl/mtl.m | 11 ++ .../graphicsdriver/opengl/context_desktop.go | 5 + internal/graphicsdriver/opengl/context_js.go | 8 ++ .../graphicsdriver/opengl/context_mobile.go | 5 + internal/graphicsdriver/opengl/gl/package.go | 1 + .../opengl/gl/package_notwindows.go | 13 ++ .../opengl/gl/package_windows.go | 9 ++ internal/graphicsdriver/opengl/gles/const.go | 1 + .../graphicsdriver/opengl/gles/default.go | 4 + .../graphicsdriver/opengl/gles/gomobile.go | 4 + .../graphicsdriver/opengl/gles/interface.go | 1 + internal/graphicsdriver/opengl/graphics.go | 28 ++-- internal/mipmap/mipmap.go | 13 +- internal/restorable/image.go | 64 +++++---- internal/restorable/images_test.go | 132 ++++++++++++++---- internal/restorable/shader_test.go | 40 +++++- internal/shareable/image.go | 24 +++- internal/shareable/image_test.go | 54 +++++-- 29 files changed, 610 insertions(+), 174 deletions(-) create mode 100644 examples/clip/main.go diff --git a/examples/clip/main.go b/examples/clip/main.go new file mode 100644 index 000000000..9949c63b8 --- /dev/null +++ b/examples/clip/main.go @@ -0,0 +1,95 @@ +// Copyright 2020 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build example + +package main + +import ( + "bytes" + "image" + _ "image/jpeg" + "log" + "math" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/examples/resources/images" +) + +const ( + screenWidth = 640 + screenHeight = 480 +) + +var ( + gophersImage *ebiten.Image +) + +type Game struct { + count int +} + +func (g *Game) Update() error { + g.count++ + return nil +} + +func (g *Game) Draw(screen *ebiten.Image) { + w, h := gophersImage.Size() + op := &ebiten.DrawImageOptions{} + + // Move the image's center to the screen's upper-left corner. + // This is a preparation for rotating. When geometry matrices are applied, + // the origin point is the upper-left corner. + op.GeoM.Translate(-float64(w)/2, -float64(h)/2) + + // Rotate the image. As a result, the anchor point of this rotate is + // the center of the image. + op.GeoM.Rotate(float64(g.count%360) * 2 * math.Pi / 360) + + // Move the image to the screen's center. + op.GeoM.Translate(screenWidth/2, screenHeight/2) + + // Use SubImage to clip the rendering region. + const size = 60 + r := image.Rect(320-size, 240-size, 320+size, 240+size) + screen.SubImage(r).(*ebiten.Image).DrawImage(gophersImage, op) +} + +func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { + return screenWidth, screenHeight +} + +func main() { + // Decode image from a byte slice instead of a file so that + // this example works in any working directory. + // If you want to use a file, there are some options: + // 1) Use os.Open and pass the file to the image decoder. + // This is a very regular way, but doesn't work on browsers. + // 2) Use ebitenutil.OpenFile and pass the file to the image decoder. + // This works even on browsers. + // 3) Use ebitenutil.NewImageFromFile to create an ebiten.Image directly from a file. + // This also works on browsers. + img, _, err := image.Decode(bytes.NewReader(images.Gophers_jpg)) + if err != nil { + log.Fatal(err) + } + gophersImage = ebiten.NewImageFromImage(img) + + ebiten.SetWindowSize(screenWidth, screenHeight) + ebiten.SetWindowTitle("Clip (Ebiten Demo)") + if err := ebiten.RunGame(&Game{}); err != nil { + log.Fatal(err) + } +} diff --git a/image.go b/image.go index c3eeb5544..9ab5bcc54 100644 --- a/image.go +++ b/image.go @@ -158,9 +158,12 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { return } - // TODO: Implement this. - if i.isSubImage() { - panic("ebiten: render to a sub-image is not implemented (DrawImage)") + dstBounds := i.Bounds() + dstRegion := driver.Region{ + X: float32(dstBounds.Min.X), + Y: float32(dstBounds.Min.Y), + Width: float32(dstBounds.Dx()), + Height: float32(dstBounds.Dy()), } // Calculate vertices before locking because the user can do anything in @@ -183,7 +186,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { is := graphics.QuadIndices() srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap} - i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, canSkipMipmap(options.GeoM, filter)) + i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, canSkipMipmap(options.GeoM, filter)) } // Vertex represents a vertex passed to DrawTriangles. @@ -267,10 +270,6 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o return } - if i.isSubImage() { - panic("ebiten: render to a sub-image is not implemented (DrawTriangles)") - } - if len(indices)%3 != 0 { panic("ebiten: len(indices) % 3 must be 0") } @@ -279,6 +278,14 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o } // TODO: Check the maximum value of indices and len(vertices)? + dstBounds := i.Bounds() + dstRegion := driver.Region{ + X: float32(dstBounds.Min.X), + Y: float32(dstBounds.Min.Y), + Width: float32(dstBounds.Dx()), + Height: float32(dstBounds.Dy()), + } + if options == nil { options = &DrawTrianglesOptions{} } @@ -315,7 +322,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap} - i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, address, sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) + i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, address, dstRegion, sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) } // DrawTrianglesShaderOptions represents options for DrawTrianglesShader. @@ -366,10 +373,6 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader return } - if i.isSubImage() { - panic("ebiten: render to a sub-image is not implemented (DrawTrianglesShader)") - } - if len(indices)%3 != 0 { panic("ebiten: len(indices) % 3 must be 0") } @@ -378,6 +381,14 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader } // TODO: Check the maximum value of indices and len(vertices)? + dstBounds := i.Bounds() + dstRegion := driver.Region{ + X: float32(dstBounds.Min.X), + Y: float32(dstBounds.Min.Y), + Width: float32(dstBounds.Dx()), + Height: float32(dstBounds.Dy()), + } + if options == nil { options = &DrawTrianglesShaderOptions{} } @@ -447,7 +458,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader } us := shader.convertUniforms(options.Uniforms) - i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, sr, offsets, shader.shader, us, false) + i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, false) } // DrawRectShaderOptions represents options for DrawRectShader. @@ -498,9 +509,12 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR return } - // TODO: Implement this. - if i.isSubImage() { - panic("ebiten: rendering to a sub-image is not implemented (DrawRectShader)") + dstBounds := i.Bounds() + dstRegion := driver.Region{ + X: float32(dstBounds.Min.X), + Y: float32(dstBounds.Min.Y), + Width: float32(dstBounds.Dx()), + Height: float32(dstBounds.Dy()), } if options == nil { @@ -556,7 +570,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR } us := shader.convertUniforms(options.Uniforms) - i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, sr, offsets, shader.shader, us, canSkipMipmap(options.GeoM, driver.FilterNearest)) + i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, canSkipMipmap(options.GeoM, driver.FilterNearest)) } // SubImage returns an image representing the portion of the image p visible through r. diff --git a/image_test.go b/image_test.go index f60602784..7ee8c7843 100644 --- a/image_test.go +++ b/image_test.go @@ -2181,3 +2181,29 @@ func TestImageNewImageFromImageWithZeroSize(t *testing.T) { img := image.NewRGBA(image.Rect(0, 0, 0, 1)) _ = NewImageFromImage(img) } + +func TestImageClip(t *testing.T) { + const ( + w = 16 + h = 16 + ) + dst := NewImage(w, h) + src := NewImage(w, h) + + dst.Fill(color.RGBA{0xff, 0, 0, 0xff}) + src.Fill(color.RGBA{0, 0xff, 0, 0xff}) + + dst.SubImage(image.Rect(4, 5, 12, 14)).(*Image).DrawImage(src, nil) + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + got := dst.At(i, j).(color.RGBA) + want := color.RGBA{0xff, 0, 0, 0xff} + if 4 <= i && i < 12 && 5 <= j && j < 14 { + want = color.RGBA{0, 0xff, 0, 0xff} + } + if got != want { + t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } +} diff --git a/internal/buffered/image.go b/internal/buffered/image.go index cf09eccb0..a0a17f4d1 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -249,7 +249,7 @@ func (i *Image) replacePendingPixels(pix []byte, x, y, width, height int) { // DrawTriangles draws the src image with the given vertices. // // Copying vertices and indices is the caller's responsibility. -func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}) { for _, src := range srcs { if i == src { panic("buffered: Image.DrawTriangles: source images must be different from the receiver") @@ -259,7 +259,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f if maybeCanAddDelayedCommand() { if tryAddDelayedCommand(func() error { // Arguments are not copied. Copying is the caller's responsibility. - i.DrawTriangles(srcs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, shader, uniforms) + i.DrawTriangles(srcs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, shader, uniforms) return nil }) { return @@ -285,7 +285,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f } i.resolvePendingPixels(false) - i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, s, uniforms) + i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, s, uniforms) i.invalidatePendingPixels() } diff --git a/internal/driver/graphics.go b/internal/driver/graphics.go index 2d4a155cf..9b05248a1 100644 --- a/internal/driver/graphics.go +++ b/internal/driver/graphics.go @@ -50,7 +50,7 @@ type Graphics interface { // Draw draws an image onto another image. // // TODO: Merge this into DrawShader. - Draw(dst, src ImageID, indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address, sourceRegion Region) error + Draw(dst, src ImageID, indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address, dstRegion, srcRegion Region) error // DrawShader draws the shader. // @@ -58,7 +58,7 @@ type Graphics interface { // // * float32 // * []float32 - DrawShader(dst ImageID, srcs [graphics.ShaderImageNum]ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader ShaderID, indexLen int, indexOffset int, sourceRegion Region, mode CompositeMode, uniforms []interface{}) error + DrawShader(dst ImageID, srcs [graphics.ShaderImageNum]ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader ShaderID, indexLen int, indexOffset int, dstRegion, srcRegion Region, mode CompositeMode, uniforms []interface{}) error } // GraphicsNotReady represents that the graphics driver is not ready for recovering from the context lost. diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index 38165f77d..2ed45cd58 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -54,7 +54,7 @@ type command interface { NumIndices() int AddNumVertices(n int) AddNumIndices(n int) - CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool + CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool } type size struct { @@ -132,7 +132,7 @@ func (q *commandQueue) appendIndices(indices []uint16, offset uint16) { } // EnqueueDrawTrianglesCommand enqueues a drawing-image command. -func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) { +func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}) { if len(indices) > graphics.IndicesNum { panic(fmt.Sprintf("graphicscommand: len(indices) must be <= graphics.IndicesNum but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesNum: %d", len(indices), graphics.IndicesNum)) } @@ -153,10 +153,10 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh if srcs[0] != nil { w, h := srcs[0].InternalSize() - sourceRegion.X /= float32(w) - sourceRegion.Y /= float32(h) - sourceRegion.Width /= float32(w) - sourceRegion.Height /= float32(h) + srcRegion.X /= float32(w) + srcRegion.Y /= float32(h) + srcRegion.Width /= float32(w) + srcRegion.Height /= float32(h) for i := range offsets { offsets[i][0] /= float32(w) offsets[i][1] /= float32(h) @@ -166,7 +166,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh // TODO: If dst is the screen, reorder the command to be the last. if !split && 0 < len(q.commands) { // TODO: Pass offsets and uniforms when merging considers the shader. - if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, srcs, color, mode, filter, address, sourceRegion, shader) { + if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, srcs, color, mode, filter, address, dstRegion, srcRegion, shader) { last.AddNumVertices(len(vertices)) last.AddNumIndices(len(indices)) return @@ -174,18 +174,19 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh } c := &drawTrianglesCommand{ - dst: dst, - srcs: srcs, - offsets: offsets, - nvertices: len(vertices), - nindices: len(indices), - color: color, - mode: mode, - filter: filter, - address: address, - sourceRegion: sourceRegion, - shader: shader, - uniforms: uniforms, + dst: dst, + srcs: srcs, + offsets: offsets, + nvertices: len(vertices), + nindices: len(indices), + color: color, + mode: mode, + filter: filter, + address: address, + dstRegion: dstRegion, + srcRegion: srcRegion, + shader: shader, + uniforms: uniforms, } q.commands = append(q.commands, c) } @@ -308,18 +309,19 @@ func FlushCommands() error { // drawTrianglesCommand represents a drawing command to draw an image on another image. type drawTrianglesCommand struct { - dst *Image - srcs [graphics.ShaderImageNum]*Image - offsets [graphics.ShaderImageNum - 1][2]float32 - nvertices int - nindices int - color *affine.ColorM - mode driver.CompositeMode - filter driver.Filter - address driver.Address - sourceRegion driver.Region - shader *Shader - uniforms []interface{} + dst *Image + srcs [graphics.ShaderImageNum]*Image + offsets [graphics.ShaderImageNum - 1][2]float32 + nvertices int + nindices int + color *affine.ColorM + mode driver.CompositeMode + filter driver.Filter + address driver.Address + dstRegion driver.Region + srcRegion driver.Region + shader *Shader + uniforms []interface{} } func (c *drawTrianglesCommand) String() string { @@ -420,9 +422,9 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error { imgs[i] = src.image.ID() } - return theGraphicsDriver.DrawShader(c.dst.image.ID(), imgs, c.offsets, c.shader.shader.ID(), c.nindices, indexOffset, c.sourceRegion, c.mode, c.uniforms) + return theGraphicsDriver.DrawShader(c.dst.image.ID(), imgs, c.offsets, c.shader.shader.ID(), c.nindices, indexOffset, c.dstRegion, c.srcRegion, c.mode, c.uniforms) } - return theGraphicsDriver.Draw(c.dst.image.ID(), c.srcs[0].image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.sourceRegion) + return theGraphicsDriver.Draw(c.dst.image.ID(), c.srcs[0].image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.dstRegion, c.srcRegion) } func (c *drawTrianglesCommand) NumVertices() int { @@ -443,7 +445,7 @@ func (c *drawTrianglesCommand) AddNumIndices(n int) { // CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged // with the drawTrianglesCommand c. -func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { +func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool { // If a shader is used, commands are not merged. // // TODO: Merge shader commands considering uniform variables. @@ -468,7 +470,10 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs if c.address != address { return false } - if c.sourceRegion != sourceRegion { + if c.dstRegion != dstRegion { + return false + } + if c.srcRegion != srcRegion { return false } return true @@ -504,7 +509,7 @@ func (c *replacePixelsCommand) AddNumVertices(n int) { func (c *replacePixelsCommand) AddNumIndices(n int) { } -func (c *replacePixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { +func (c *replacePixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool { return false } @@ -532,7 +537,7 @@ func (c *syncCommand) AddNumVertices(n int) { func (c *syncCommand) AddNumIndices(n int) { } -func (c *syncCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { +func (c *syncCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool { return false } @@ -573,7 +578,7 @@ func (c *pixelsCommand) AddNumVertices(n int) { func (c *pixelsCommand) AddNumIndices(n int) { } -func (c *pixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { +func (c *pixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool { return false } @@ -606,7 +611,7 @@ func (c *disposeImageCommand) AddNumVertices(n int) { func (c *disposeImageCommand) AddNumIndices(n int) { } -func (c *disposeImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { +func (c *disposeImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool { return false } @@ -639,7 +644,7 @@ func (c *disposeShaderCommand) AddNumVertices(n int) { func (c *disposeShaderCommand) AddNumIndices(n int) { } -func (c *disposeShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { +func (c *disposeShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool { return false } @@ -678,7 +683,7 @@ func (c *newImageCommand) AddNumVertices(n int) { func (c *newImageCommand) AddNumIndices(n int) { } -func (c *newImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { +func (c *newImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool { return false } @@ -714,7 +719,7 @@ func (c *newScreenFramebufferImageCommand) AddNumVertices(n int) { func (c *newScreenFramebufferImageCommand) AddNumIndices(n int) { } -func (c *newScreenFramebufferImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { +func (c *newScreenFramebufferImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool { return false } @@ -749,7 +754,7 @@ func (c *newShaderCommand) AddNumVertices(n int) { func (c *newShaderCommand) AddNumIndices(n int) { } -func (c *newShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool { +func (c *newShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool { return false } diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index 8e6e40cf5..7ac3f12e5 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -150,7 +150,7 @@ func (i *Image) InternalSize() (int, int) { // // If the source image is not specified, i.e., src is nil and there is no image in the uniform variables, the // elements for the source image are not used. -func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, clr *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, clr *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}) { if i.lastCommand == lastCommandNone { if !i.screen && mode != driver.CompositeModeClear { panic("graphicscommand: the image must be cleared first") @@ -177,7 +177,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra } i.resolveBufferedReplacePixels() - theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, vertices, indices, clr, mode, filter, address, sourceRegion, shader, uniforms) + theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, vertices, indices, clr, mode, filter, address, dstRegion, srcRegion, shader, uniforms) if i.lastCommand == lastCommandNone && !i.screen { i.lastCommand = lastCommandClear diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index 886588fba..55d66d990 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -44,7 +44,13 @@ func TestClear(t *testing.T) { vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() - dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) pix, err := dst.Pixels() if err != nil { @@ -74,8 +80,14 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) { dst := NewImage(w, h) vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() - dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) - dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) + dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1) } @@ -85,11 +97,17 @@ func TestShader(t *testing.T) { dst := NewImage(w, h) vs := quadVertices(w, h) is := graphics.QuadIndices() - dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) s := NewShader(&ir) - dst.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil) + dst.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil) pix, err := dst.Pixels() if err != nil { diff --git a/internal/graphicsdriver/metal/graphics.go b/internal/graphicsdriver/metal/graphics.go index 4c7ee5352..2a191ada1 100644 --- a/internal/graphicsdriver/metal/graphics.go +++ b/internal/graphicsdriver/metal/graphics.go @@ -611,7 +611,7 @@ func (g *Graphics) Reset() error { return nil } -func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, srcs [graphics.ShaderImageNum]*Image, indexLen int, indexOffset int, uniforms []interface{}) error { +func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, dstRegion driver.Region, srcs [graphics.ShaderImageNum]*Image, indexLen int, indexOffset int, uniforms []interface{}) error { g.view.update() rpd := mtl.RenderPassDescriptor{} @@ -653,6 +653,12 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, srcs [graphics. ZNear: -1, ZFar: 1, }) + rce.SetScissorRect(mtl.ScissorRect{ + X: int(dstRegion.X), + Y: int(dstRegion.Y), + Width: int(dstRegion.Width), + Height: int(dstRegion.Height), + }) rce.SetVertexBuffer(g.vb, 0, 0) for i, u := range uniforms { @@ -680,7 +686,7 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, srcs [graphics. return nil } -func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, sourceRegion driver.Region) error { +func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region) error { dst := g.images[dstID] dst.waitUntilSyncFinishes() @@ -718,13 +724,13 @@ func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset i esTranslate, scale, []float32{ - sourceRegion.X, - sourceRegion.Y, - sourceRegion.X + sourceRegion.Width, - sourceRegion.Y + sourceRegion.Height, + srcRegion.X, + srcRegion.Y, + srcRegion.X + srcRegion.Width, + srcRegion.Y + srcRegion.Height, }, } - if err := g.draw(rps, dst, srcs, indexLen, indexOffset, uniforms); err != nil { + if err := g.draw(rps, dst, dstRegion, srcs, indexLen, indexOffset, uniforms); err != nil { return err } return nil @@ -947,7 +953,7 @@ func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) { bce.EndEncoding() } -func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader driver.ShaderID, indexLen int, indexOffset int, sourceRegion driver.Region, mode driver.CompositeMode, uniforms []interface{}) error { +func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader driver.ShaderID, indexLen int, indexOffset int, dstRegion, srcRegion driver.Region, mode driver.CompositeMode, uniforms []interface{}) error { dst := g.images[dstID] dst.waitUntilSyncFinishes() @@ -987,11 +993,11 @@ func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImage us[graphics.TextureSourceOffsetsUniformVariableIndex] = uoffsets // Set the source region's origin of texture0. - uorigin := []float32{float32(sourceRegion.X), float32(sourceRegion.Y)} + uorigin := []float32{float32(srcRegion.X), float32(srcRegion.Y)} us[graphics.TextureSourceRegionOriginUniformVariableIndex] = uorigin // Set the source region's size of texture0. - ussize := []float32{float32(sourceRegion.Width), float32(sourceRegion.Height)} + ussize := []float32{float32(srcRegion.Width), float32(srcRegion.Height)} us[graphics.TextureSourceRegionSizeUniformVariableIndex] = ussize // Set the additional uniform variables. @@ -1000,7 +1006,7 @@ func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImage us[offset+i] = v } - if err := g.draw(rps, dst, srcs, indexLen, indexOffset, us); err != nil { + if err := g.draw(rps, dst, dstRegion, srcs, indexLen, indexOffset, us); err != nil { return err } return nil diff --git a/internal/graphicsdriver/metal/mtl/mtl.go b/internal/graphicsdriver/metal/mtl/mtl.go index 01093a2ae..8dfef3f1f 100644 --- a/internal/graphicsdriver/metal/mtl/mtl.go +++ b/internal/graphicsdriver/metal/mtl/mtl.go @@ -650,6 +650,13 @@ func (rce RenderCommandEncoder) SetViewport(viewport Viewport) { C.RenderCommandEncoder_SetViewport(rce.commandEncoder, viewport.c()) } +// SetScissorRect sets the scissor rectangle for a fragment scissor test. +// +// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515583-setscissorrect +func (rce RenderCommandEncoder) SetScissorRect(scissorRect ScissorRect) { + C.RenderCommandEncoder_SetScissorRect(rce.commandEncoder, scissorRect.c()) +} + // SetVertexBuffer sets a buffer for the vertex shader function at an index // in the buffer argument table with an offset that specifies the start of the data. // @@ -909,3 +916,22 @@ func (v *Viewport) c() C.struct_Viewport { ZFar: C.double(v.ZFar), } } + +// ScissorRect represents a rectangle for the scissor fragment test. +// +// Reference: https://developer.apple.com/documentation/metal/mtlscissorrect +type ScissorRect struct { + X int + Y int + Width int + Height int +} + +func (s *ScissorRect) c() C.struct_ScissorRect { + return C.struct_ScissorRect{ + X: C.uint_t(s.X), + Y: C.uint_t(s.Y), + Width: C.uint_t(s.Width), + Height: C.uint_t(s.Height), + } +} diff --git a/internal/graphicsdriver/metal/mtl/mtl.h b/internal/graphicsdriver/metal/mtl/mtl.h index 03f2f03f5..6fbd804eb 100644 --- a/internal/graphicsdriver/metal/mtl/mtl.h +++ b/internal/graphicsdriver/metal/mtl/mtl.h @@ -103,6 +103,13 @@ struct Viewport { double ZFar; }; +struct ScissorRect { + uint_t X; + uint_t Y; + uint_t Width; + uint_t Height; +}; + struct Device CreateSystemDefaultDevice(); struct Devices CopyAllDevices(); @@ -139,6 +146,8 @@ void RenderCommandEncoder_SetRenderPipelineState(void *renderCommandEncoder, void *renderPipelineState); void RenderCommandEncoder_SetViewport(void *renderCommandEncoder, struct Viewport viewport); +void RenderCommandEncoder_SetScissorRect(void *renderCommandEncoder, + struct ScissorRect scissorRect); void RenderCommandEncoder_SetVertexBuffer(void *renderCommandEncoder, void *buffer, uint_t offset, uint_t index); diff --git a/internal/graphicsdriver/metal/mtl/mtl.m b/internal/graphicsdriver/metal/mtl/mtl.m index 3d8694b8e..a7720ef3d 100644 --- a/internal/graphicsdriver/metal/mtl/mtl.m +++ b/internal/graphicsdriver/metal/mtl/mtl.m @@ -209,6 +209,17 @@ void RenderCommandEncoder_SetViewport(void *renderCommandEncoder, }]; } +void RenderCommandEncoder_SetScissorRect(void *renderCommandEncoder, + struct ScissorRect scissorRect) { + [(id)renderCommandEncoder + setScissorRect:(MTLScissorRect){ + .x = scissorRect.X, + .y = scissorRect.Y, + .width = scissorRect.Width, + .height = scissorRect.Height, + }]; +} + void RenderCommandEncoder_SetVertexBuffer(void *renderCommandEncoder, void *buffer, uint_t offset, uint_t index) { diff --git a/internal/graphicsdriver/opengl/context_desktop.go b/internal/graphicsdriver/opengl/context_desktop.go index 4701dc8dc..f4564e6b4 100644 --- a/internal/graphicsdriver/opengl/context_desktop.go +++ b/internal/graphicsdriver/opengl/context_desktop.go @@ -116,6 +116,7 @@ func (c *context) reset() error { c.lastViewportHeight = 0 c.lastCompositeMode = driver.CompositeModeUnknown gl.Enable(gl.BLEND) + gl.Enable(gl.SCISSOR_TEST) c.blendFunc(driver.CompositeModeSourceOver) @@ -135,6 +136,10 @@ func (c *context) blendFunc(mode driver.CompositeMode) { gl.BlendFunc(uint32(s2), uint32(d2)) } +func (c *context) scissor(x, y, width, height int) { + gl.Scissor(int32(x), int32(y), int32(width), int32(height)) +} + func (c *context) newTexture(width, height int) (textureNative, error) { var t uint32 gl.GenTextures(1, &t) diff --git a/internal/graphicsdriver/opengl/context_js.go b/internal/graphicsdriver/opengl/context_js.go index 676b5d13f..e618dae31 100644 --- a/internal/graphicsdriver/opengl/context_js.go +++ b/internal/graphicsdriver/opengl/context_js.go @@ -104,6 +104,7 @@ var ( nearest js.Value noError js.Value rgba js.Value + scissorTest js.Value texture2d js.Value textureMagFilter js.Value textureMinFilter js.Value @@ -159,6 +160,7 @@ func init() { nearest = contextPrototype.Get("NEAREST") noError = contextPrototype.Get("NO_ERROR") rgba = contextPrototype.Get("RGBA") + scissorTest = contextPrototype.Get("SCISSOR_TEST") texture0 = contextPrototype.Get("TEXTURE0").Int() texture2d = contextPrototype.Get("TEXTURE_2D") textureMagFilter = contextPrototype.Get("TEXTURE_MAG_FILTER") @@ -222,6 +224,7 @@ func (c *context) reset() error { } gl := c.gl gl.Call("enable", blend) + gl.Call("enable", scissorTest) c.blendFunc(driver.CompositeModeSourceOver) f := gl.Call("getParameter", framebufferBinding) c.screenFramebuffer = framebufferNative(f) @@ -240,6 +243,11 @@ func (c *context) blendFunc(mode driver.CompositeMode) { gl.Call("blendFunc", int(s2), int(d2)) } +func (c *context) scissor(x, y, width, height int) { + gl := c.gl + gl.Call("scissor", x, y, width, height) +} + func (c *context) newTexture(width, height int) (textureNative, error) { c.ensureGL() gl := c.gl diff --git a/internal/graphicsdriver/opengl/context_mobile.go b/internal/graphicsdriver/opengl/context_mobile.go index a4658fb6f..d46efbb27 100644 --- a/internal/graphicsdriver/opengl/context_mobile.go +++ b/internal/graphicsdriver/opengl/context_mobile.go @@ -106,6 +106,7 @@ func (c *context) reset() error { c.lastViewportHeight = 0 c.lastCompositeMode = driver.CompositeModeUnknown c.ctx.Enable(gles.BLEND) + c.ctx.Enable(gles.SCISSOR_TEST) c.blendFunc(driver.CompositeModeSourceOver) f := make([]int32, 1) c.ctx.GetIntegerv(f, gles.FRAMEBUFFER_BINDING) @@ -124,6 +125,10 @@ func (c *context) blendFunc(mode driver.CompositeMode) { c.ctx.BlendFunc(uint32(s2), uint32(d2)) } +func (c *context) scissor(x, y, width, height int) { + c.ctx.Scissor(int32(x), int32(y), int32(width), int32(height)) +} + func (c *context) newTexture(width, height int) (textureNative, error) { t := c.ctx.GenTextures(1)[0] if t <= 0 { diff --git a/internal/graphicsdriver/opengl/gl/package.go b/internal/graphicsdriver/opengl/gl/package.go index 9859b8b69..516e4f89f 100644 --- a/internal/graphicsdriver/opengl/gl/package.go +++ b/internal/graphicsdriver/opengl/gl/package.go @@ -57,6 +57,7 @@ const ( TEXTURE_WRAP_S = 0x2802 TEXTURE_WRAP_T = 0x2803 TRIANGLES = 0x0004 + SCISSOR_TEST = 0x0C11 UNPACK_ALIGNMENT = 0x0CF5 UNSIGNED_BYTE = 0x1401 UNSIGNED_SHORT = 0x1403 diff --git a/internal/graphicsdriver/opengl/gl/package_notwindows.go b/internal/graphicsdriver/opengl/gl/package_notwindows.go index b85c08ded..9df4dc340 100644 --- a/internal/graphicsdriver/opengl/gl/package_notwindows.go +++ b/internal/graphicsdriver/opengl/gl/package_notwindows.go @@ -147,6 +147,7 @@ package gl // typedef void (APIENTRYP GPLINKPROGRAM)(GLuint program); // typedef void (APIENTRYP GPPIXELSTOREI)(GLenum pname, GLint param); // typedef void (APIENTRYP GPREADPIXELS)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels); +// typedef void (APIENTRYP GPSCISSOR)(GLint x, GLint y, GLsizei width, GLsizei height); // typedef void (APIENTRYP GPSHADERSOURCE)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); // typedef void (APIENTRYP GPTEXIMAGE2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); // typedef void (APIENTRYP GPTEXPARAMETERI)(GLenum target, GLenum pname, GLint param); @@ -320,6 +321,9 @@ package gl // static void glowReadPixels(GPREADPIXELS fnptr, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels) { // (*fnptr)(x, y, width, height, format, type, pixels); // } +// static void glowScissor(GPSCISSOR fnptr, GLint x, GLint y, GLsizei width, GLsizei height) { +// (*fnptr)(x, y, width, height); +// } // static void glowShaderSource(GPSHADERSOURCE fnptr, GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length) { // (*fnptr)(shader, count, string, length); // } @@ -428,6 +432,7 @@ var ( gpLinkProgram C.GPLINKPROGRAM gpPixelStorei C.GPPIXELSTOREI gpReadPixels C.GPREADPIXELS + gpScissor C.GPSCISSOR gpShaderSource C.GPSHADERSOURCE gpTexImage2D C.GPTEXIMAGE2D gpTexParameteri C.GPTEXPARAMETERI @@ -661,6 +666,10 @@ func ReadPixels(x int32, y int32, width int32, height int32, format uint32, xtyp C.glowReadPixels(gpReadPixels, (C.GLint)(x), (C.GLint)(y), (C.GLsizei)(width), (C.GLsizei)(height), (C.GLenum)(format), (C.GLenum)(xtype), pixels) } +func Scissor(x int32, y int32, width int32, height int32) { + C.glowScissor(gpScissor, (C.GLint)(x), (C.GLint)(y), (C.GLsizei)(width), (C.GLsizei)(height)) +} + func ShaderSource(shader uint32, count int32, xstring **uint8, length *int32) { C.glowShaderSource(gpShaderSource, (C.GLuint)(shader), (C.GLsizei)(count), (**C.GLchar)(unsafe.Pointer(xstring)), (*C.GLint)(unsafe.Pointer(length))) } @@ -884,6 +893,10 @@ func InitWithProcAddrFunc(getProcAddr func(name string) unsafe.Pointer) error { if gpReadPixels == nil { return errors.New("glReadPixels") } + gpScissor = (C.GPSCISSOR)(getProcAddr("glScissor")) + if gpScissor == nil { + return errors.New("glScissor") + } gpShaderSource = (C.GPSHADERSOURCE)(getProcAddr("glShaderSource")) if gpShaderSource == nil { return errors.New("glShaderSource") diff --git a/internal/graphicsdriver/opengl/gl/package_windows.go b/internal/graphicsdriver/opengl/gl/package_windows.go index 082c73272..00a167220 100644 --- a/internal/graphicsdriver/opengl/gl/package_windows.go +++ b/internal/graphicsdriver/opengl/gl/package_windows.go @@ -62,6 +62,7 @@ var ( gpLinkProgram uintptr gpPixelStorei uintptr gpReadPixels uintptr + gpScissor uintptr gpShaderSource uintptr gpTexImage2D uintptr gpTexParameteri uintptr @@ -295,6 +296,10 @@ func ReadPixels(x int32, y int32, width int32, height int32, format uint32, xtyp syscall.Syscall9(gpReadPixels, 7, uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(xtype), uintptr(pixels), 0, 0) } +func Scissor(x int32, y int32, width int32, height int32) { + syscall.Syscall6(gpScissor, 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0) +} + func ShaderSource(shader uint32, count int32, xstring **uint8, length *int32) { syscall.Syscall6(gpShaderSource, 4, uintptr(shader), uintptr(count), uintptr(unsafe.Pointer(xstring)), uintptr(unsafe.Pointer(length)), 0, 0) } @@ -518,6 +523,10 @@ func InitWithProcAddrFunc(getProcAddr func(name string) uintptr) error { if gpReadPixels == 0 { return errors.New("glReadPixels") } + gpScissor = getProcAddr("glScissor") + if gpScissor == 0 { + return errors.New("glScissor") + } gpShaderSource = getProcAddr("glShaderSource") if gpShaderSource == 0 { return errors.New("glShaderSource") diff --git a/internal/graphicsdriver/opengl/gles/const.go b/internal/graphicsdriver/opengl/gles/const.go index 1bf507c7b..0f7bd76da 100644 --- a/internal/graphicsdriver/opengl/gles/const.go +++ b/internal/graphicsdriver/opengl/gles/const.go @@ -54,6 +54,7 @@ const ( NO_ERROR = 0 READ_WRITE = 0x88BA RGBA = 0x1908 + SCISSOR_TEST = 0x0C11 TEXTURE0 = 0x84C0 TEXTURE_2D = 0x0DE1 TEXTURE_MAG_FILTER = 0x2800 diff --git a/internal/graphicsdriver/opengl/gles/default.go b/internal/graphicsdriver/opengl/gles/default.go index 6e0c462cb..44bd90e82 100644 --- a/internal/graphicsdriver/opengl/gles/default.go +++ b/internal/graphicsdriver/opengl/gles/default.go @@ -242,6 +242,10 @@ func (DefaultContext) ReadPixels(dst []byte, x int32, y int32, width int32, heig C.glReadPixels(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(xtype), unsafe.Pointer(&dst[0])) } +func (DefaultContext) Scissor(x, y, width, height int32) { + C.glScissor(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) +} + func (DefaultContext) ShaderSource(shader uint32, xstring string) { s, free := cStringPtr(xstring) defer free() diff --git a/internal/graphicsdriver/opengl/gles/gomobile.go b/internal/graphicsdriver/opengl/gles/gomobile.go index e5fdc2593..544eee4e7 100644 --- a/internal/graphicsdriver/opengl/gles/gomobile.go +++ b/internal/graphicsdriver/opengl/gles/gomobile.go @@ -224,6 +224,10 @@ func (g *GomobileContext) ReadPixels(dst []byte, x int32, y int32, width int32, g.ctx.ReadPixels(dst, int(x), int(y), int(width), int(height), gl.Enum(format), gl.Enum(xtype)) } +func (g *GomobileContext) Scissor(x, y, width, height int32) { + g.ctx.Scissor(x, y, width, height) +} + func (g *GomobileContext) ShaderSource(shader uint32, xstring string) { g.ctx.ShaderSource(gl.Shader{Value: shader}, xstring) } diff --git a/internal/graphicsdriver/opengl/gles/interface.go b/internal/graphicsdriver/opengl/gles/interface.go index 5572fdfbe..dc3d53ca0 100644 --- a/internal/graphicsdriver/opengl/gles/interface.go +++ b/internal/graphicsdriver/opengl/gles/interface.go @@ -58,6 +58,7 @@ type Context interface { LinkProgram(program uint32) PixelStorei(pname uint32, param int32) ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32) + Scissor(x, y, width, height int32) ShaderSource(shader uint32, xstring string) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) TexParameteri(target uint32, pname uint32, param int32) diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index 029cd732e..9d43da135 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -148,7 +148,7 @@ func (g *Graphics) SetVertices(vertices []float32, indices []uint16) { g.context.elementArrayBufferSubData(indices) } -func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, sourceRegion driver.Region) error { +func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region) error { destination := g.images[dst] source := g.images[src] @@ -157,6 +157,12 @@ func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int, if err := destination.setViewport(); err != nil { return err } + g.context.scissor( + int(dstRegion.X), + int(dstRegion.Y), + int(dstRegion.Width), + int(dstRegion.Height), + ) g.context.blendFunc(mode) program := g.state.programs[programKey{ @@ -176,10 +182,10 @@ func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int, }, uniformVariable{ name: "source_region", value: []float32{ - sourceRegion.X, - sourceRegion.Y, - sourceRegion.X + sourceRegion.Width, - sourceRegion.Y + sourceRegion.Height, + srcRegion.X, + srcRegion.Y, + srcRegion.X + srcRegion.Width, + srcRegion.Y + srcRegion.Height, }, typ: shaderir.Type{Main: shaderir.Vec4}, }) @@ -284,7 +290,7 @@ func (g *Graphics) removeShader(shader *Shader) { delete(g.shaders, shader.id) } -func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader driver.ShaderID, indexLen int, indexOffset int, sourceRegion driver.Region, mode driver.CompositeMode, uniforms []interface{}) error { +func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader driver.ShaderID, indexLen int, indexOffset int, dstRegion, srcRegion driver.Region, mode driver.CompositeMode, uniforms []interface{}) error { d := g.images[dst] s := g.shaders[shader] @@ -293,6 +299,12 @@ func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum] if err := d.setViewport(); err != nil { return err } + g.context.scissor( + int(dstRegion.X), + int(dstRegion.Y), + int(dstRegion.Width), + int(dstRegion.Height), + ) g.context.blendFunc(mode) us := make([]uniformVariable, graphics.PreservedUniformVariablesNum+len(uniforms)) @@ -331,14 +343,14 @@ func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum] us[idx].typ = s.ir.Uniforms[idx] } { - origin := []float32{float32(sourceRegion.X), float32(sourceRegion.Y)} + origin := []float32{float32(srcRegion.X), float32(srcRegion.Y)} const idx = graphics.TextureSourceRegionOriginUniformVariableIndex us[idx].name = fmt.Sprintf("U%d", idx) us[idx].value = origin us[idx].typ = s.ir.Uniforms[idx] } { - size := []float32{float32(sourceRegion.Width), float32(sourceRegion.Height)} + size := []float32{float32(srcRegion.Width), float32(srcRegion.Height)} const idx = graphics.TextureSourceRegionSizeUniformVariableIndex us[idx].name = fmt.Sprintf("U%d", idx) us[idx].value = size diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 76c9e48cf..8226fc8e6 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -91,7 +91,7 @@ func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) { return m.orig.Pixels(x, y, width, height) } -func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, canSkipMipmap bool) { +func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, canSkipMipmap bool) { if len(indices) == 0 { return } @@ -170,7 +170,7 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices [ imgs[i] = src.orig } - m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, s, uniforms) + m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, s, uniforms) m.disposeMipmaps() } @@ -225,7 +225,14 @@ func (m *Mipmap) level(level int) *buffered.Image { } s := buffered.NewImage(w2, h2) s.SetVolatile(m.volatile) - s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + + dstRegion := driver.Region{ + X: 0, + Y: 0, + Width: float32(w2), + Height: float32(h2), + } + s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) m.imgs[level] = s return m.imgs[level] diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 9de48dfc7..714d0af08 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -69,17 +69,18 @@ func (p *Pixels) At(i, j int) (byte, byte, byte, byte) { // drawTrianglesHistoryItem is an item for history of draw-image commands. type drawTrianglesHistoryItem struct { - images [graphics.ShaderImageNum]*Image - offsets [graphics.ShaderImageNum - 1][2]float32 - vertices []float32 - indices []uint16 - colorm *affine.ColorM - mode driver.CompositeMode - filter driver.Filter - address driver.Address - sourceRegion driver.Region - shader *Shader - uniforms []interface{} + images [graphics.ShaderImageNum]*Image + offsets [graphics.ShaderImageNum - 1][2]float32 + vertices []float32 + indices []uint16 + colorm *affine.ColorM + mode driver.CompositeMode + filter driver.Filter + address driver.Address + dstRegion driver.Region + srcRegion driver.Region + shader *Shader + uniforms []interface{} } // Image represents an image that can be restored when GL context is lost. @@ -269,7 +270,13 @@ func fillImage(i *graphicscommand.Image, clr color.RGBA) { is := graphics.QuadIndices() srcs := [graphics.ShaderImageNum]*graphicscommand.Image{emptyImage.image} var offsets [graphics.ShaderImageNum - 1][2]float32 - i.DrawTriangles(srcs, offsets, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dstRegion := driver.Region{ + X: 0, + Y: 0, + Width: float32(dw), + Height: float32(dh), + } + i.DrawTriangles(srcs, offsets, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil) } // BasePixelsForTesting returns the image's basePixels for testing. @@ -361,7 +368,7 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) { // 5: Color G // 6: Color B // 7: Color Y -func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}) { if i.priority { panic("restorable: DrawTriangles cannot be called on a priority image") } @@ -385,7 +392,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra if srcstale || i.screen || !needsRestoring() || i.volatile { i.makeStale() } else { - i.appendDrawTrianglesHistory(srcs, offsets, vertices, indices, colorm, mode, filter, address, sourceRegion, shader, uniforms) + i.appendDrawTrianglesHistory(srcs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, shader, uniforms) } var s *graphicscommand.Shader @@ -402,11 +409,11 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra } s = shader.shader } - i.image.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms) + i.image.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, s, uniforms) } // appendDrawTrianglesHistory appends a draw-image history item to the image. -func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) { +func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}) { if i.stale || i.volatile || i.screen { return } @@ -425,17 +432,18 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image, copy(is, indices) item := &drawTrianglesHistoryItem{ - images: srcs, - offsets: offsets, - vertices: vs, - indices: is, - colorm: colorm, - mode: mode, - filter: filter, - address: address, - sourceRegion: sourceRegion, - shader: shader, - uniforms: uniforms, + images: srcs, + offsets: offsets, + vertices: vs, + indices: is, + colorm: colorm, + mode: mode, + filter: filter, + address: address, + dstRegion: dstRegion, + srcRegion: srcRegion, + shader: shader, + uniforms: uniforms, } i.drawTrianglesHistory = append(i.drawTrianglesHistory, item) } @@ -625,7 +633,7 @@ func (i *Image) restore() error { } imgs[i] = img.image } - gimg.DrawTriangles(imgs, c.offsets, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address, c.sourceRegion, s, c.uniforms) + gimg.DrawTriangles(imgs, c.offsets, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address, c.dstRegion, c.srcRegion, s, c.uniforms) } if len(i.drawTrianglesHistory) > 0 { diff --git a/internal/restorable/images_test.go b/internal/restorable/images_test.go index 84929ce6e..1824615af 100644 --- a/internal/restorable/images_test.go +++ b/internal/restorable/images_test.go @@ -131,7 +131,13 @@ func TestRestoreChain(t *testing.T) { for i := 0; i < num-1; i++ { vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() - imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: 1, + Height: 1, + } + imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) } if err := ResolveStaleImages(); err != nil { t.Fatal(err) @@ -173,10 +179,16 @@ func TestRestoreChain2(t *testing.T) { imgs[8].ReplacePixels([]byte{clr8.R, clr8.G, clr8.B, clr8.A}, 0, 0, w, h) is := graphics.QuadIndices() - imgs[8].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[7]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) - imgs[9].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[8]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + imgs[8].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[7]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) + imgs[9].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[8]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) for i := 0; i < 7; i++ { - imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) } if err := ResolveStaleImages(); err != nil { @@ -216,10 +228,16 @@ func TestRestoreOverrideSource(t *testing.T) { clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff} img1.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h) is := graphics.QuadIndices() - img2.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) - img3.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + img2.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) + img3.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) img0.ReplacePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h) - img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) if err := ResolveStaleImages(); err != nil { t.Fatal(err) } @@ -298,24 +316,30 @@ func TestRestoreComplexGraph(t *testing.T) { }() vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } var offsets [graphics.ShaderImageNum - 1][2]float32 - img3.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + img3.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) vs = quadVertices(w, h, 1, 0) - img3.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + img3.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) vs = quadVertices(w, h, 1, 0) - img4.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + img4.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) vs = quadVertices(w, h, 2, 0) - img4.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + img4.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) vs = quadVertices(w, h, 0, 0) - img5.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + img5.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) vs = quadVertices(w, h, 0, 0) - img6.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + img6.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) vs = quadVertices(w, h, 1, 0) - img6.DrawTriangles([graphics.ShaderImageNum]*Image{img4}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + img6.DrawTriangles([graphics.ShaderImageNum]*Image{img4}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) vs = quadVertices(w, h, 0, 0) - img7.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + img7.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) vs = quadVertices(w, h, 2, 0) - img7.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + img7.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) if err := ResolveStaleImages(); err != nil { t.Fatal(err) } @@ -407,8 +431,14 @@ func TestRestoreRecursive(t *testing.T) { img0.Dispose() }() is := graphics.QuadIndices() - img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) - img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) + img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) if err := ResolveStaleImages(); err != nil { t.Fatal(err) } @@ -502,7 +532,13 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) { vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() - img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: 2, + Height: 1, + } + img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1) if err := ResolveStaleImages(); err != nil { @@ -539,8 +575,14 @@ func TestDispose(t *testing.T) { defer img2.Dispose() is := graphics.QuadIndices() - img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) - img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: 1, + Height: 1, + } + img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) + img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) img1.Dispose() if err := ResolveStaleImages(); err != nil { @@ -648,7 +690,13 @@ func TestReplacePixelsOnly(t *testing.T) { vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() - img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: 1, + Height: 1, + } + img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1) // BasePixelsForTesting is available without GPU accessing. @@ -702,7 +750,13 @@ func TestReadPixelsFromVolatileImage(t *testing.T) { src.ReplacePixels(pix, 0, 0, w, h) vs := quadVertices(1, 1, 0, 0) is := graphics.QuadIndices() - dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) // Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being // stale. @@ -723,7 +777,13 @@ func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() - dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h) // ReplacePixels for a whole image doesn't panic. } @@ -741,7 +801,13 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() - dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1) } @@ -830,7 +896,13 @@ func TestFill2(t *testing.T) { dst := NewImage(w, h) vs := quadVertices(w, h, 0, 0) is := graphics.QuadIndices() - dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) // Fill src with a different color. This should not affect dst. src.Fill(color.RGBA{0, 0xff, 0, 0xff}) @@ -869,7 +941,13 @@ func TestMutateSlices(t *testing.T) { vs := quadVertices(w, h, 0, 0) is := make([]uint16, len(graphics.QuadIndices())) copy(is, graphics.QuadIndices()) - dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil) for i := range vs { vs[i] = 0 } diff --git a/internal/restorable/shader_test.go b/internal/restorable/shader_test.go index c2315ec8f..2eda4cbc6 100644 --- a/internal/restorable/shader_test.go +++ b/internal/restorable/shader_test.go @@ -30,7 +30,13 @@ func TestShader(t *testing.T) { ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) s := NewShader(&ir) - img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: 1, + Height: 1, + } + img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil) if err := ResolveStaleImages(); err != nil { t.Fatal(err) @@ -60,7 +66,13 @@ func TestShaderChain(t *testing.T) { ir := etesting.ShaderProgramImages(1) s := NewShader(&ir) for i := 0; i < num-1; i++ { - imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: 1, + Height: 1, + } + imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil) } if err := ResolveStaleImages(); err != nil { @@ -93,7 +105,13 @@ func TestShaderMultipleSources(t *testing.T) { ir := etesting.ShaderProgramImages(3) s := NewShader(&ir) var offsets [graphics.ShaderImageNum - 1][2]float32 - dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: 1, + Height: 1, + } + dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil) // Clear one of the sources after DrawTriangles. dst should not be affected. srcs[0].Fill(color.RGBA{}) @@ -129,7 +147,13 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) { {1, 0}, {2, 0}, } - dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: 1, + Height: 1, + } + dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil) // Clear one of the sources after DrawTriangles. dst should not be affected. srcs[0].Fill(color.RGBA{}) @@ -154,7 +178,13 @@ func TestShaderDispose(t *testing.T) { ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) s := NewShader(&ir) - img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: 1, + Height: 1, + } + img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil) // Dispose the shader. This should invalidates all the images using this shader i.e., all the images become // stale. diff --git a/internal/shareable/image.go b/internal/shareable/image.go index 6d79954f2..ff4182e8c 100644 --- a/internal/shareable/image.go +++ b/internal/shareable/image.go @@ -241,7 +241,13 @@ func (i *Image) ensureNotShared() { is := graphics.QuadIndices() srcs := [graphics.ShaderImageNum]*restorable.Image{i.backend.restorable} var offsets [graphics.ShaderImageNum - 1][2]float32 - newImg.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) + dstRegion := driver.Region{ + X: paddingSize, + Y: paddingSize, + Width: float32(w - 2*paddingSize), + Height: float32(h - 2*paddingSize), + } + newImg.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil) i.dispose(false) i.backend = &backend{ @@ -325,7 +331,7 @@ func (i *Image) processSrc(src *Image) { // 5: Color G // 6: Color B // 7: Color Y -func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}) { +func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}) { backendsM.Lock() // Do not use defer for performance. @@ -345,6 +351,9 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f dy = paddingSize } + dstRegion.X += dx + dstRegion.Y += dx + var oxf, oyf float32 if srcs[0] != nil { ox, oy, _, _ := srcs[0].regionWithPadding() @@ -358,11 +367,11 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f vertices[i*graphics.VertexFloatNum+2] += oxf vertices[i*graphics.VertexFloatNum+3] += oyf } - // sourceRegion can be delibarately empty when this is not needed in order to avoid unexpected + // srcRegion can be delibarately empty when this is not needed in order to avoid unexpected // performance issue (#1293). - if sourceRegion.Width != 0 && sourceRegion.Height != 0 { - sourceRegion.X += oxf - sourceRegion.Y += oyf + if srcRegion.Width != 0 && srcRegion.Height != 0 { + srcRegion.X += oxf + srcRegion.Y += oyf } } else { n := len(vertices) / graphics.VertexFloatNum @@ -397,7 +406,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f } } - i.backend.restorable.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms) + i.backend.restorable.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, s, uniforms) for _, src := range srcs { if src == nil { @@ -428,6 +437,7 @@ func (i *Image) Fill(clr color.RGBA) { i.ensureNotShared() // As *restorable.Image is an independent image, it is fine to fill the entire image. + // TODO: Is it OK not to consider paddings? i.backend.restorable.Fill(clr) } diff --git a/internal/shareable/image_test.go b/internal/shareable/image_test.go index 5514a0492..f5fac4d2f 100644 --- a/internal/shareable/image_test.go +++ b/internal/shareable/image_test.go @@ -96,7 +96,13 @@ func TestEnsureNotShared(t *testing.T) { // img4.ensureNotShared() should be called. vs := quadVertices(size/2, size/2, size/4, size/4, 1) is := graphics.QuadIndices() - img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: size, + Height: size, + } + img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) want := false if got := img4.IsSharedForTesting(); got != want { t.Errorf("got: %v, want: %v", got, want) @@ -126,7 +132,7 @@ func TestEnsureNotShared(t *testing.T) { // Check further drawing doesn't cause panic. // This bug was fixed by 03dcd948. - img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) } func TestReshared(t *testing.T) { @@ -167,7 +173,13 @@ func TestReshared(t *testing.T) { // Use img1 as a render target. vs := quadVertices(size, size, 0, 0, 1) is := graphics.QuadIndices() - img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: size, + Height: size, + } + img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) if got, want := img1.IsSharedForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -177,7 +189,7 @@ func TestReshared(t *testing.T) { if err := MakeImagesSharedForTesting(); err != nil { t.Fatal(err) } - img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) if got, want := img1.IsSharedForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -205,7 +217,7 @@ func TestReshared(t *testing.T) { } // img1 is on a shared image again. - img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) if got, want := img1.IsSharedForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -229,7 +241,7 @@ func TestReshared(t *testing.T) { } // Use img1 as a render target again. - img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) if got, want := img1.IsSharedForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -240,7 +252,7 @@ func TestReshared(t *testing.T) { t.Fatal(err) } img1.ReplacePixels(make([]byte, 4*size*size)) - img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) if got, want := img1.IsSharedForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -250,7 +262,7 @@ func TestReshared(t *testing.T) { } // img1 is not on a shared image due to ReplacePixels. - img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) if got, want := img1.IsSharedForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -260,7 +272,7 @@ func TestReshared(t *testing.T) { if err := MakeImagesSharedForTesting(); err != nil { t.Fatal(err) } - img0.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + img0.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) if got, want := img3.IsSharedForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -355,7 +367,13 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() - dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) dst.ReplacePixels(pix) pix, err := dst.Pixels(0, 0, w, h) @@ -397,7 +415,13 @@ func TestSmallImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() - dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: w, + Height: h, + } + dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) pix, err := dst.Pixels(0, 0, w, h) if err != nil { @@ -439,7 +463,13 @@ func TestLongImages(t *testing.T) { const scale = 120 vs := quadVertices(w, h, 0, 0, scale) is := graphics.QuadIndices() - dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) + dr := driver.Region{ + X: 0, + Y: 0, + Width: dstW, + Height: dstH, + } + dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil) pix, err := dst.Pixels(0, 0, dstW, dstH) if err != nil {