ebiten: Allow rendering on a sub-image by scissor test

Fixes #1255
This commit is contained in:
Hajime Hoshi 2020-11-07 19:14:06 +09:00
parent e4e8e7254e
commit ed028110cf
29 changed files with 610 additions and 174 deletions

95
examples/clip/main.go Normal file
View File

@ -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)
}
}

View File

@ -158,9 +158,12 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
return return
} }
// TODO: Implement this. dstBounds := i.Bounds()
if i.isSubImage() { dstRegion := driver.Region{
panic("ebiten: render to a sub-image is not implemented (DrawImage)") 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 // 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() is := graphics.QuadIndices()
srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap} 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. // Vertex represents a vertex passed to DrawTriangles.
@ -267,10 +270,6 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
return return
} }
if i.isSubImage() {
panic("ebiten: render to a sub-image is not implemented (DrawTriangles)")
}
if len(indices)%3 != 0 { if len(indices)%3 != 0 {
panic("ebiten: len(indices) % 3 must be 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)? // 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 { if options == nil {
options = &DrawTrianglesOptions{} options = &DrawTrianglesOptions{}
} }
@ -315,7 +322,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap} 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. // DrawTrianglesShaderOptions represents options for DrawTrianglesShader.
@ -366,10 +373,6 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
return return
} }
if i.isSubImage() {
panic("ebiten: render to a sub-image is not implemented (DrawTrianglesShader)")
}
if len(indices)%3 != 0 { if len(indices)%3 != 0 {
panic("ebiten: len(indices) % 3 must be 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)? // 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 { if options == nil {
options = &DrawTrianglesShaderOptions{} options = &DrawTrianglesShaderOptions{}
} }
@ -447,7 +458,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
} }
us := shader.convertUniforms(options.Uniforms) 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. // DrawRectShaderOptions represents options for DrawRectShader.
@ -498,9 +509,12 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
return return
} }
// TODO: Implement this. dstBounds := i.Bounds()
if i.isSubImage() { dstRegion := driver.Region{
panic("ebiten: rendering to a sub-image is not implemented (DrawRectShader)") X: float32(dstBounds.Min.X),
Y: float32(dstBounds.Min.Y),
Width: float32(dstBounds.Dx()),
Height: float32(dstBounds.Dy()),
} }
if options == nil { if options == nil {
@ -556,7 +570,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
} }
us := shader.convertUniforms(options.Uniforms) 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. // SubImage returns an image representing the portion of the image p visible through r.

View File

@ -2181,3 +2181,29 @@ func TestImageNewImageFromImageWithZeroSize(t *testing.T) {
img := image.NewRGBA(image.Rect(0, 0, 0, 1)) img := image.NewRGBA(image.Rect(0, 0, 0, 1))
_ = NewImageFromImage(img) _ = 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)
}
}
}
}

View File

@ -249,7 +249,7 @@ func (i *Image) replacePendingPixels(pix []byte, x, y, width, height int) {
// DrawTriangles draws the src image with the given vertices. // DrawTriangles draws the src image with the given vertices.
// //
// Copying vertices and indices is the caller's responsibility. // 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 { for _, src := range srcs {
if i == src { if i == src {
panic("buffered: Image.DrawTriangles: source images must be different from the receiver") 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 maybeCanAddDelayedCommand() {
if tryAddDelayedCommand(func() error { if tryAddDelayedCommand(func() error {
// Arguments are not copied. Copying is the caller's responsibility. // 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 nil
}) { }) {
return return
@ -285,7 +285,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
} }
i.resolvePendingPixels(false) 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() i.invalidatePendingPixels()
} }

View File

@ -50,7 +50,7 @@ type Graphics interface {
// Draw draws an image onto another image. // Draw draws an image onto another image.
// //
// TODO: Merge this into DrawShader. // 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. // DrawShader draws the shader.
// //
@ -58,7 +58,7 @@ type Graphics interface {
// //
// * float32 // * float32
// * []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. // GraphicsNotReady represents that the graphics driver is not ready for recovering from the context lost.

View File

@ -54,7 +54,7 @@ type command interface {
NumIndices() int NumIndices() int
AddNumVertices(n int) AddNumVertices(n int)
AddNumIndices(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 { type size struct {
@ -132,7 +132,7 @@ func (q *commandQueue) appendIndices(indices []uint16, offset uint16) {
} }
// EnqueueDrawTrianglesCommand enqueues a drawing-image command. // 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 { 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)) 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 { if srcs[0] != nil {
w, h := srcs[0].InternalSize() w, h := srcs[0].InternalSize()
sourceRegion.X /= float32(w) srcRegion.X /= float32(w)
sourceRegion.Y /= float32(h) srcRegion.Y /= float32(h)
sourceRegion.Width /= float32(w) srcRegion.Width /= float32(w)
sourceRegion.Height /= float32(h) srcRegion.Height /= float32(h)
for i := range offsets { for i := range offsets {
offsets[i][0] /= float32(w) offsets[i][0] /= float32(w)
offsets[i][1] /= float32(h) 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. // TODO: If dst is the screen, reorder the command to be the last.
if !split && 0 < len(q.commands) { if !split && 0 < len(q.commands) {
// TODO: Pass offsets and uniforms when merging considers the shader. // 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.AddNumVertices(len(vertices))
last.AddNumIndices(len(indices)) last.AddNumIndices(len(indices))
return return
@ -174,18 +174,19 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
} }
c := &drawTrianglesCommand{ c := &drawTrianglesCommand{
dst: dst, dst: dst,
srcs: srcs, srcs: srcs,
offsets: offsets, offsets: offsets,
nvertices: len(vertices), nvertices: len(vertices),
nindices: len(indices), nindices: len(indices),
color: color, color: color,
mode: mode, mode: mode,
filter: filter, filter: filter,
address: address, address: address,
sourceRegion: sourceRegion, dstRegion: dstRegion,
shader: shader, srcRegion: srcRegion,
uniforms: uniforms, shader: shader,
uniforms: uniforms,
} }
q.commands = append(q.commands, c) 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. // drawTrianglesCommand represents a drawing command to draw an image on another image.
type drawTrianglesCommand struct { type drawTrianglesCommand struct {
dst *Image dst *Image
srcs [graphics.ShaderImageNum]*Image srcs [graphics.ShaderImageNum]*Image
offsets [graphics.ShaderImageNum - 1][2]float32 offsets [graphics.ShaderImageNum - 1][2]float32
nvertices int nvertices int
nindices int nindices int
color *affine.ColorM color *affine.ColorM
mode driver.CompositeMode mode driver.CompositeMode
filter driver.Filter filter driver.Filter
address driver.Address address driver.Address
sourceRegion driver.Region dstRegion driver.Region
shader *Shader srcRegion driver.Region
uniforms []interface{} shader *Shader
uniforms []interface{}
} }
func (c *drawTrianglesCommand) String() string { func (c *drawTrianglesCommand) String() string {
@ -420,9 +422,9 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
imgs[i] = src.image.ID() 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 { 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 // CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged
// with the drawTrianglesCommand c. // 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. // If a shader is used, commands are not merged.
// //
// TODO: Merge shader commands considering uniform variables. // TODO: Merge shader commands considering uniform variables.
@ -468,7 +470,10 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs
if c.address != address { if c.address != address {
return false return false
} }
if c.sourceRegion != sourceRegion { if c.dstRegion != dstRegion {
return false
}
if c.srcRegion != srcRegion {
return false return false
} }
return true return true
@ -504,7 +509,7 @@ func (c *replacePixelsCommand) AddNumVertices(n int) {
func (c *replacePixelsCommand) AddNumIndices(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 return false
} }
@ -532,7 +537,7 @@ func (c *syncCommand) AddNumVertices(n int) {
func (c *syncCommand) AddNumIndices(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 return false
} }
@ -573,7 +578,7 @@ func (c *pixelsCommand) AddNumVertices(n int) {
func (c *pixelsCommand) AddNumIndices(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 return false
} }
@ -606,7 +611,7 @@ func (c *disposeImageCommand) AddNumVertices(n int) {
func (c *disposeImageCommand) AddNumIndices(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 return false
} }
@ -639,7 +644,7 @@ func (c *disposeShaderCommand) AddNumVertices(n int) {
func (c *disposeShaderCommand) AddNumIndices(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 return false
} }
@ -678,7 +683,7 @@ func (c *newImageCommand) AddNumVertices(n int) {
func (c *newImageCommand) AddNumIndices(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 return false
} }
@ -714,7 +719,7 @@ func (c *newScreenFramebufferImageCommand) AddNumVertices(n int) {
func (c *newScreenFramebufferImageCommand) AddNumIndices(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 return false
} }
@ -749,7 +754,7 @@ func (c *newShaderCommand) AddNumVertices(n int) {
func (c *newShaderCommand) AddNumIndices(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 return false
} }

View File

@ -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 // 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. // 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.lastCommand == lastCommandNone {
if !i.screen && mode != driver.CompositeModeClear { if !i.screen && mode != driver.CompositeModeClear {
panic("graphicscommand: the image must be cleared first") panic("graphicscommand: the image must be cleared first")
@ -177,7 +177,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
} }
i.resolveBufferedReplacePixels() 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 { if i.lastCommand == lastCommandNone && !i.screen {
i.lastCommand = lastCommandClear i.lastCommand = lastCommandClear

View File

@ -44,7 +44,13 @@ func TestClear(t *testing.T) {
vs := quadVertices(w/2, h/2) vs := quadVertices(w/2, h/2)
is := graphics.QuadIndices() 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() pix, err := dst.Pixels()
if err != nil { if err != nil {
@ -74,8 +80,14 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) {
dst := NewImage(w, h) dst := NewImage(w, h)
vs := quadVertices(w/2, h/2) vs := quadVertices(w/2, h/2)
is := graphics.QuadIndices() 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{
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) 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) dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
} }
@ -85,11 +97,17 @@ func TestShader(t *testing.T) {
dst := NewImage(w, h) dst := NewImage(w, h)
vs := quadVertices(w, h) vs := quadVertices(w, h)
is := graphics.QuadIndices() 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) ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir) 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() pix, err := dst.Pixels()
if err != nil { if err != nil {

View File

@ -611,7 +611,7 @@ func (g *Graphics) Reset() error {
return nil 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() g.view.update()
rpd := mtl.RenderPassDescriptor{} rpd := mtl.RenderPassDescriptor{}
@ -653,6 +653,12 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, srcs [graphics.
ZNear: -1, ZNear: -1,
ZFar: 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) rce.SetVertexBuffer(g.vb, 0, 0)
for i, u := range uniforms { for i, u := range uniforms {
@ -680,7 +686,7 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, srcs [graphics.
return nil 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 := g.images[dstID]
dst.waitUntilSyncFinishes() dst.waitUntilSyncFinishes()
@ -718,13 +724,13 @@ func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset i
esTranslate, esTranslate,
scale, scale,
[]float32{ []float32{
sourceRegion.X, srcRegion.X,
sourceRegion.Y, srcRegion.Y,
sourceRegion.X + sourceRegion.Width, srcRegion.X + srcRegion.Width,
sourceRegion.Y + sourceRegion.Height, 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 err
} }
return nil return nil
@ -947,7 +953,7 @@ func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
bce.EndEncoding() 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 := g.images[dstID]
dst.waitUntilSyncFinishes() dst.waitUntilSyncFinishes()
@ -987,11 +993,11 @@ func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImage
us[graphics.TextureSourceOffsetsUniformVariableIndex] = uoffsets us[graphics.TextureSourceOffsetsUniformVariableIndex] = uoffsets
// Set the source region's origin of texture0. // 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 us[graphics.TextureSourceRegionOriginUniformVariableIndex] = uorigin
// Set the source region's size of texture0. // 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 us[graphics.TextureSourceRegionSizeUniformVariableIndex] = ussize
// Set the additional uniform variables. // Set the additional uniform variables.
@ -1000,7 +1006,7 @@ func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImage
us[offset+i] = v 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 err
} }
return nil return nil

View File

@ -650,6 +650,13 @@ func (rce RenderCommandEncoder) SetViewport(viewport Viewport) {
C.RenderCommandEncoder_SetViewport(rce.commandEncoder, viewport.c()) 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 // 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. // 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), 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),
}
}

View File

@ -103,6 +103,13 @@ struct Viewport {
double ZFar; double ZFar;
}; };
struct ScissorRect {
uint_t X;
uint_t Y;
uint_t Width;
uint_t Height;
};
struct Device CreateSystemDefaultDevice(); struct Device CreateSystemDefaultDevice();
struct Devices CopyAllDevices(); struct Devices CopyAllDevices();
@ -139,6 +146,8 @@ void RenderCommandEncoder_SetRenderPipelineState(void *renderCommandEncoder,
void *renderPipelineState); void *renderPipelineState);
void RenderCommandEncoder_SetViewport(void *renderCommandEncoder, void RenderCommandEncoder_SetViewport(void *renderCommandEncoder,
struct Viewport viewport); struct Viewport viewport);
void RenderCommandEncoder_SetScissorRect(void *renderCommandEncoder,
struct ScissorRect scissorRect);
void RenderCommandEncoder_SetVertexBuffer(void *renderCommandEncoder, void RenderCommandEncoder_SetVertexBuffer(void *renderCommandEncoder,
void *buffer, uint_t offset, void *buffer, uint_t offset,
uint_t index); uint_t index);

View File

@ -209,6 +209,17 @@ void RenderCommandEncoder_SetViewport(void *renderCommandEncoder,
}]; }];
} }
void RenderCommandEncoder_SetScissorRect(void *renderCommandEncoder,
struct ScissorRect scissorRect) {
[(id<MTLRenderCommandEncoder>)renderCommandEncoder
setScissorRect:(MTLScissorRect){
.x = scissorRect.X,
.y = scissorRect.Y,
.width = scissorRect.Width,
.height = scissorRect.Height,
}];
}
void RenderCommandEncoder_SetVertexBuffer(void *renderCommandEncoder, void RenderCommandEncoder_SetVertexBuffer(void *renderCommandEncoder,
void *buffer, uint_t offset, void *buffer, uint_t offset,
uint_t index) { uint_t index) {

View File

@ -116,6 +116,7 @@ func (c *context) reset() error {
c.lastViewportHeight = 0 c.lastViewportHeight = 0
c.lastCompositeMode = driver.CompositeModeUnknown c.lastCompositeMode = driver.CompositeModeUnknown
gl.Enable(gl.BLEND) gl.Enable(gl.BLEND)
gl.Enable(gl.SCISSOR_TEST)
c.blendFunc(driver.CompositeModeSourceOver) c.blendFunc(driver.CompositeModeSourceOver)
@ -135,6 +136,10 @@ func (c *context) blendFunc(mode driver.CompositeMode) {
gl.BlendFunc(uint32(s2), uint32(d2)) 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) { func (c *context) newTexture(width, height int) (textureNative, error) {
var t uint32 var t uint32
gl.GenTextures(1, &t) gl.GenTextures(1, &t)

View File

@ -104,6 +104,7 @@ var (
nearest js.Value nearest js.Value
noError js.Value noError js.Value
rgba js.Value rgba js.Value
scissorTest js.Value
texture2d js.Value texture2d js.Value
textureMagFilter js.Value textureMagFilter js.Value
textureMinFilter js.Value textureMinFilter js.Value
@ -159,6 +160,7 @@ func init() {
nearest = contextPrototype.Get("NEAREST") nearest = contextPrototype.Get("NEAREST")
noError = contextPrototype.Get("NO_ERROR") noError = contextPrototype.Get("NO_ERROR")
rgba = contextPrototype.Get("RGBA") rgba = contextPrototype.Get("RGBA")
scissorTest = contextPrototype.Get("SCISSOR_TEST")
texture0 = contextPrototype.Get("TEXTURE0").Int() texture0 = contextPrototype.Get("TEXTURE0").Int()
texture2d = contextPrototype.Get("TEXTURE_2D") texture2d = contextPrototype.Get("TEXTURE_2D")
textureMagFilter = contextPrototype.Get("TEXTURE_MAG_FILTER") textureMagFilter = contextPrototype.Get("TEXTURE_MAG_FILTER")
@ -222,6 +224,7 @@ func (c *context) reset() error {
} }
gl := c.gl gl := c.gl
gl.Call("enable", blend) gl.Call("enable", blend)
gl.Call("enable", scissorTest)
c.blendFunc(driver.CompositeModeSourceOver) c.blendFunc(driver.CompositeModeSourceOver)
f := gl.Call("getParameter", framebufferBinding) f := gl.Call("getParameter", framebufferBinding)
c.screenFramebuffer = framebufferNative(f) c.screenFramebuffer = framebufferNative(f)
@ -240,6 +243,11 @@ func (c *context) blendFunc(mode driver.CompositeMode) {
gl.Call("blendFunc", int(s2), int(d2)) 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) { func (c *context) newTexture(width, height int) (textureNative, error) {
c.ensureGL() c.ensureGL()
gl := c.gl gl := c.gl

View File

@ -106,6 +106,7 @@ func (c *context) reset() error {
c.lastViewportHeight = 0 c.lastViewportHeight = 0
c.lastCompositeMode = driver.CompositeModeUnknown c.lastCompositeMode = driver.CompositeModeUnknown
c.ctx.Enable(gles.BLEND) c.ctx.Enable(gles.BLEND)
c.ctx.Enable(gles.SCISSOR_TEST)
c.blendFunc(driver.CompositeModeSourceOver) c.blendFunc(driver.CompositeModeSourceOver)
f := make([]int32, 1) f := make([]int32, 1)
c.ctx.GetIntegerv(f, gles.FRAMEBUFFER_BINDING) 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)) 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) { func (c *context) newTexture(width, height int) (textureNative, error) {
t := c.ctx.GenTextures(1)[0] t := c.ctx.GenTextures(1)[0]
if t <= 0 { if t <= 0 {

View File

@ -57,6 +57,7 @@ const (
TEXTURE_WRAP_S = 0x2802 TEXTURE_WRAP_S = 0x2802
TEXTURE_WRAP_T = 0x2803 TEXTURE_WRAP_T = 0x2803
TRIANGLES = 0x0004 TRIANGLES = 0x0004
SCISSOR_TEST = 0x0C11
UNPACK_ALIGNMENT = 0x0CF5 UNPACK_ALIGNMENT = 0x0CF5
UNSIGNED_BYTE = 0x1401 UNSIGNED_BYTE = 0x1401
UNSIGNED_SHORT = 0x1403 UNSIGNED_SHORT = 0x1403

View File

@ -147,6 +147,7 @@ package gl
// typedef void (APIENTRYP GPLINKPROGRAM)(GLuint program); // typedef void (APIENTRYP GPLINKPROGRAM)(GLuint program);
// typedef void (APIENTRYP GPPIXELSTOREI)(GLenum pname, GLint param); // 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 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 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 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); // 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) { // 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); // (*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) { // static void glowShaderSource(GPSHADERSOURCE fnptr, GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length) {
// (*fnptr)(shader, count, string, length); // (*fnptr)(shader, count, string, length);
// } // }
@ -428,6 +432,7 @@ var (
gpLinkProgram C.GPLINKPROGRAM gpLinkProgram C.GPLINKPROGRAM
gpPixelStorei C.GPPIXELSTOREI gpPixelStorei C.GPPIXELSTOREI
gpReadPixels C.GPREADPIXELS gpReadPixels C.GPREADPIXELS
gpScissor C.GPSCISSOR
gpShaderSource C.GPSHADERSOURCE gpShaderSource C.GPSHADERSOURCE
gpTexImage2D C.GPTEXIMAGE2D gpTexImage2D C.GPTEXIMAGE2D
gpTexParameteri C.GPTEXPARAMETERI 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) 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) { 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))) 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 { if gpReadPixels == nil {
return errors.New("glReadPixels") return errors.New("glReadPixels")
} }
gpScissor = (C.GPSCISSOR)(getProcAddr("glScissor"))
if gpScissor == nil {
return errors.New("glScissor")
}
gpShaderSource = (C.GPSHADERSOURCE)(getProcAddr("glShaderSource")) gpShaderSource = (C.GPSHADERSOURCE)(getProcAddr("glShaderSource"))
if gpShaderSource == nil { if gpShaderSource == nil {
return errors.New("glShaderSource") return errors.New("glShaderSource")

View File

@ -62,6 +62,7 @@ var (
gpLinkProgram uintptr gpLinkProgram uintptr
gpPixelStorei uintptr gpPixelStorei uintptr
gpReadPixels uintptr gpReadPixels uintptr
gpScissor uintptr
gpShaderSource uintptr gpShaderSource uintptr
gpTexImage2D uintptr gpTexImage2D uintptr
gpTexParameteri 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) 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) { 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) 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 { if gpReadPixels == 0 {
return errors.New("glReadPixels") return errors.New("glReadPixels")
} }
gpScissor = getProcAddr("glScissor")
if gpScissor == 0 {
return errors.New("glScissor")
}
gpShaderSource = getProcAddr("glShaderSource") gpShaderSource = getProcAddr("glShaderSource")
if gpShaderSource == 0 { if gpShaderSource == 0 {
return errors.New("glShaderSource") return errors.New("glShaderSource")

View File

@ -54,6 +54,7 @@ const (
NO_ERROR = 0 NO_ERROR = 0
READ_WRITE = 0x88BA READ_WRITE = 0x88BA
RGBA = 0x1908 RGBA = 0x1908
SCISSOR_TEST = 0x0C11
TEXTURE0 = 0x84C0 TEXTURE0 = 0x84C0
TEXTURE_2D = 0x0DE1 TEXTURE_2D = 0x0DE1
TEXTURE_MAG_FILTER = 0x2800 TEXTURE_MAG_FILTER = 0x2800

View File

@ -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])) 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) { func (DefaultContext) ShaderSource(shader uint32, xstring string) {
s, free := cStringPtr(xstring) s, free := cStringPtr(xstring)
defer free() defer free()

View File

@ -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)) 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) { func (g *GomobileContext) ShaderSource(shader uint32, xstring string) {
g.ctx.ShaderSource(gl.Shader{Value: shader}, xstring) g.ctx.ShaderSource(gl.Shader{Value: shader}, xstring)
} }

View File

@ -58,6 +58,7 @@ type Context interface {
LinkProgram(program uint32) LinkProgram(program uint32)
PixelStorei(pname uint32, param int32) PixelStorei(pname uint32, param int32)
ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32) 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) ShaderSource(shader uint32, xstring string)
TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte)
TexParameteri(target uint32, pname uint32, param int32) TexParameteri(target uint32, pname uint32, param int32)

View File

@ -148,7 +148,7 @@ func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
g.context.elementArrayBufferSubData(indices) 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] destination := g.images[dst]
source := g.images[src] 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 { if err := destination.setViewport(); err != nil {
return err return err
} }
g.context.scissor(
int(dstRegion.X),
int(dstRegion.Y),
int(dstRegion.Width),
int(dstRegion.Height),
)
g.context.blendFunc(mode) g.context.blendFunc(mode)
program := g.state.programs[programKey{ program := g.state.programs[programKey{
@ -176,10 +182,10 @@ func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int,
}, uniformVariable{ }, uniformVariable{
name: "source_region", name: "source_region",
value: []float32{ value: []float32{
sourceRegion.X, srcRegion.X,
sourceRegion.Y, srcRegion.Y,
sourceRegion.X + sourceRegion.Width, srcRegion.X + srcRegion.Width,
sourceRegion.Y + sourceRegion.Height, srcRegion.Y + srcRegion.Height,
}, },
typ: shaderir.Type{Main: shaderir.Vec4}, typ: shaderir.Type{Main: shaderir.Vec4},
}) })
@ -284,7 +290,7 @@ func (g *Graphics) removeShader(shader *Shader) {
delete(g.shaders, shader.id) 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] d := g.images[dst]
s := g.shaders[shader] s := g.shaders[shader]
@ -293,6 +299,12 @@ func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]
if err := d.setViewport(); err != nil { if err := d.setViewport(); err != nil {
return err return err
} }
g.context.scissor(
int(dstRegion.X),
int(dstRegion.Y),
int(dstRegion.Width),
int(dstRegion.Height),
)
g.context.blendFunc(mode) g.context.blendFunc(mode)
us := make([]uniformVariable, graphics.PreservedUniformVariablesNum+len(uniforms)) 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] 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 const idx = graphics.TextureSourceRegionOriginUniformVariableIndex
us[idx].name = fmt.Sprintf("U%d", idx) us[idx].name = fmt.Sprintf("U%d", idx)
us[idx].value = origin us[idx].value = origin
us[idx].typ = s.ir.Uniforms[idx] 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 const idx = graphics.TextureSourceRegionSizeUniformVariableIndex
us[idx].name = fmt.Sprintf("U%d", idx) us[idx].name = fmt.Sprintf("U%d", idx)
us[idx].value = size us[idx].value = size

View File

@ -91,7 +91,7 @@ func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
return m.orig.Pixels(x, y, width, height) 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 { if len(indices) == 0 {
return return
} }
@ -170,7 +170,7 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices [
imgs[i] = src.orig 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() m.disposeMipmaps()
} }
@ -225,7 +225,14 @@ func (m *Mipmap) level(level int) *buffered.Image {
} }
s := buffered.NewImage(w2, h2) s := buffered.NewImage(w2, h2)
s.SetVolatile(m.volatile) 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 m.imgs[level] = s
return m.imgs[level] return m.imgs[level]

View File

@ -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. // drawTrianglesHistoryItem is an item for history of draw-image commands.
type drawTrianglesHistoryItem struct { type drawTrianglesHistoryItem struct {
images [graphics.ShaderImageNum]*Image images [graphics.ShaderImageNum]*Image
offsets [graphics.ShaderImageNum - 1][2]float32 offsets [graphics.ShaderImageNum - 1][2]float32
vertices []float32 vertices []float32
indices []uint16 indices []uint16
colorm *affine.ColorM colorm *affine.ColorM
mode driver.CompositeMode mode driver.CompositeMode
filter driver.Filter filter driver.Filter
address driver.Address address driver.Address
sourceRegion driver.Region dstRegion driver.Region
shader *Shader srcRegion driver.Region
uniforms []interface{} shader *Shader
uniforms []interface{}
} }
// Image represents an image that can be restored when GL context is lost. // 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() is := graphics.QuadIndices()
srcs := [graphics.ShaderImageNum]*graphicscommand.Image{emptyImage.image} srcs := [graphics.ShaderImageNum]*graphicscommand.Image{emptyImage.image}
var offsets [graphics.ShaderImageNum - 1][2]float32 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. // 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 // 5: Color G
// 6: Color B // 6: Color B
// 7: Color Y // 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 { if i.priority {
panic("restorable: DrawTriangles cannot be called on a priority image") 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 { if srcstale || i.screen || !needsRestoring() || i.volatile {
i.makeStale() i.makeStale()
} else { } 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 var s *graphicscommand.Shader
@ -402,11 +409,11 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
} }
s = shader.shader 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. // 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 { if i.stale || i.volatile || i.screen {
return return
} }
@ -425,17 +432,18 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image,
copy(is, indices) copy(is, indices)
item := &drawTrianglesHistoryItem{ item := &drawTrianglesHistoryItem{
images: srcs, images: srcs,
offsets: offsets, offsets: offsets,
vertices: vs, vertices: vs,
indices: is, indices: is,
colorm: colorm, colorm: colorm,
mode: mode, mode: mode,
filter: filter, filter: filter,
address: address, address: address,
sourceRegion: sourceRegion, dstRegion: dstRegion,
shader: shader, srcRegion: srcRegion,
uniforms: uniforms, shader: shader,
uniforms: uniforms,
} }
i.drawTrianglesHistory = append(i.drawTrianglesHistory, item) i.drawTrianglesHistory = append(i.drawTrianglesHistory, item)
} }
@ -625,7 +633,7 @@ func (i *Image) restore() error {
} }
imgs[i] = img.image 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 { if len(i.drawTrianglesHistory) > 0 {

View File

@ -131,7 +131,13 @@ func TestRestoreChain(t *testing.T) {
for i := 0; i < num-1; i++ { for i := 0; i < num-1; i++ {
vs := quadVertices(1, 1, 0, 0) vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices() 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 { if err := ResolveStaleImages(); err != nil {
t.Fatal(err) 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) imgs[8].ReplacePixels([]byte{clr8.R, clr8.G, clr8.B, clr8.A}, 0, 0, w, h)
is := graphics.QuadIndices() 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) dr := driver.Region{
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) 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++ { 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 { if err := ResolveStaleImages(); err != nil {
@ -216,10 +228,16 @@ func TestRestoreOverrideSource(t *testing.T) {
clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff} clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff}
img1.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h) img1.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h)
is := graphics.QuadIndices() 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) dr := driver.Region{
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) 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) 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 { if err := ResolveStaleImages(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -298,24 +316,30 @@ func TestRestoreComplexGraph(t *testing.T) {
}() }()
vs := quadVertices(w, h, 0, 0) vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
var offsets [graphics.ShaderImageNum - 1][2]float32 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) 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) 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) 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) 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) 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) 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) 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) 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 { if err := ResolveStaleImages(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -407,8 +431,14 @@ func TestRestoreRecursive(t *testing.T) {
img0.Dispose() img0.Dispose()
}() }()
is := graphics.QuadIndices() 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) dr := driver.Region{
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) 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 { if err := ResolveStaleImages(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -502,7 +532,13 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) {
vs := quadVertices(1, 1, 0, 0) vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices() 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) img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
if err := ResolveStaleImages(); err != nil { if err := ResolveStaleImages(); err != nil {
@ -539,8 +575,14 @@ func TestDispose(t *testing.T) {
defer img2.Dispose() defer img2.Dispose()
is := graphics.QuadIndices() 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) dr := driver.Region{
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) 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() img1.Dispose()
if err := ResolveStaleImages(); err != nil { if err := ResolveStaleImages(); err != nil {
@ -648,7 +690,13 @@ func TestReplacePixelsOnly(t *testing.T) {
vs := quadVertices(1, 1, 0, 0) vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices() 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) img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
// BasePixelsForTesting is available without GPU accessing. // BasePixelsForTesting is available without GPU accessing.
@ -702,7 +750,13 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
src.ReplacePixels(pix, 0, 0, w, h) src.ReplacePixels(pix, 0, 0, w, h)
vs := quadVertices(1, 1, 0, 0) vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices() 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 // Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being
// stale. // stale.
@ -723,7 +777,13 @@ func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w, h, 0, 0) vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices() 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) dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h)
// ReplacePixels for a whole image doesn't panic. // ReplacePixels for a whole image doesn't panic.
} }
@ -741,7 +801,13 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w, h, 0, 0) vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices() 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) dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
} }
@ -830,7 +896,13 @@ func TestFill2(t *testing.T) {
dst := NewImage(w, h) dst := NewImage(w, h)
vs := quadVertices(w, h, 0, 0) vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices() 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. // Fill src with a different color. This should not affect dst.
src.Fill(color.RGBA{0, 0xff, 0, 0xff}) src.Fill(color.RGBA{0, 0xff, 0, 0xff})
@ -869,7 +941,13 @@ func TestMutateSlices(t *testing.T) {
vs := quadVertices(w, h, 0, 0) vs := quadVertices(w, h, 0, 0)
is := make([]uint16, len(graphics.QuadIndices())) is := make([]uint16, len(graphics.QuadIndices()))
copy(is, 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 { for i := range vs {
vs[i] = 0 vs[i] = 0
} }

View File

@ -30,7 +30,13 @@ func TestShader(t *testing.T) {
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir) 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 { if err := ResolveStaleImages(); err != nil {
t.Fatal(err) t.Fatal(err)
@ -60,7 +66,13 @@ func TestShaderChain(t *testing.T) {
ir := etesting.ShaderProgramImages(1) ir := etesting.ShaderProgramImages(1)
s := NewShader(&ir) s := NewShader(&ir)
for i := 0; i < num-1; i++ { 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 { if err := ResolveStaleImages(); err != nil {
@ -93,7 +105,13 @@ func TestShaderMultipleSources(t *testing.T) {
ir := etesting.ShaderProgramImages(3) ir := etesting.ShaderProgramImages(3)
s := NewShader(&ir) s := NewShader(&ir)
var offsets [graphics.ShaderImageNum - 1][2]float32 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. // Clear one of the sources after DrawTriangles. dst should not be affected.
srcs[0].Fill(color.RGBA{}) srcs[0].Fill(color.RGBA{})
@ -129,7 +147,13 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
{1, 0}, {1, 0},
{2, 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. // Clear one of the sources after DrawTriangles. dst should not be affected.
srcs[0].Fill(color.RGBA{}) srcs[0].Fill(color.RGBA{})
@ -154,7 +178,13 @@ func TestShaderDispose(t *testing.T) {
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir) 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 // Dispose the shader. This should invalidates all the images using this shader i.e., all the images become
// stale. // stale.

View File

@ -241,7 +241,13 @@ func (i *Image) ensureNotShared() {
is := graphics.QuadIndices() is := graphics.QuadIndices()
srcs := [graphics.ShaderImageNum]*restorable.Image{i.backend.restorable} srcs := [graphics.ShaderImageNum]*restorable.Image{i.backend.restorable}
var offsets [graphics.ShaderImageNum - 1][2]float32 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.dispose(false)
i.backend = &backend{ i.backend = &backend{
@ -325,7 +331,7 @@ func (i *Image) processSrc(src *Image) {
// 5: Color G // 5: Color G
// 6: Color B // 6: Color B
// 7: Color Y // 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() backendsM.Lock()
// Do not use defer for performance. // Do not use defer for performance.
@ -345,6 +351,9 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
dy = paddingSize dy = paddingSize
} }
dstRegion.X += dx
dstRegion.Y += dx
var oxf, oyf float32 var oxf, oyf float32
if srcs[0] != nil { if srcs[0] != nil {
ox, oy, _, _ := srcs[0].regionWithPadding() 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+2] += oxf
vertices[i*graphics.VertexFloatNum+3] += oyf 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). // performance issue (#1293).
if sourceRegion.Width != 0 && sourceRegion.Height != 0 { if srcRegion.Width != 0 && srcRegion.Height != 0 {
sourceRegion.X += oxf srcRegion.X += oxf
sourceRegion.Y += oyf srcRegion.Y += oyf
} }
} else { } else {
n := len(vertices) / graphics.VertexFloatNum 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 { for _, src := range srcs {
if src == nil { if src == nil {
@ -428,6 +437,7 @@ func (i *Image) Fill(clr color.RGBA) {
i.ensureNotShared() i.ensureNotShared()
// As *restorable.Image is an independent image, it is fine to fill the entire image. // 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) i.backend.restorable.Fill(clr)
} }

View File

@ -96,7 +96,13 @@ func TestEnsureNotShared(t *testing.T) {
// img4.ensureNotShared() should be called. // img4.ensureNotShared() should be called.
vs := quadVertices(size/2, size/2, size/4, size/4, 1) vs := quadVertices(size/2, size/2, size/4, size/4, 1)
is := graphics.QuadIndices() 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 want := false
if got := img4.IsSharedForTesting(); got != want { if got := img4.IsSharedForTesting(); got != want {
t.Errorf("got: %v, want: %v", 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. // Check further drawing doesn't cause panic.
// This bug was fixed by 03dcd948. // 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) { func TestReshared(t *testing.T) {
@ -167,7 +173,13 @@ func TestReshared(t *testing.T) {
// Use img1 as a render target. // Use img1 as a render target.
vs := quadVertices(size, size, 0, 0, 1) vs := quadVertices(size, size, 0, 0, 1)
is := graphics.QuadIndices() 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 { if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -177,7 +189,7 @@ func TestReshared(t *testing.T) {
if err := MakeImagesSharedForTesting(); err != nil { if err := MakeImagesSharedForTesting(); err != nil {
t.Fatal(err) 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 { if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", 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. // 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 { if got, want := img1.IsSharedForTesting(), true; got != want {
t.Errorf("got: %v, want: %v", 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. // 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 { if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -240,7 +252,7 @@ func TestReshared(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
img1.ReplacePixels(make([]byte, 4*size*size)) 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 { if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", 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. // 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 { if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -260,7 +272,7 @@ func TestReshared(t *testing.T) {
if err := MakeImagesSharedForTesting(); err != nil { if err := MakeImagesSharedForTesting(); err != nil {
t.Fatal(err) 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 { if got, want := img3.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", 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) vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices() 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) dst.ReplacePixels(pix)
pix, err := dst.Pixels(0, 0, w, h) pix, err := dst.Pixels(0, 0, w, h)
@ -397,7 +415,13 @@ func TestSmallImages(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1) vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices() 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) pix, err := dst.Pixels(0, 0, w, h)
if err != nil { if err != nil {
@ -439,7 +463,13 @@ func TestLongImages(t *testing.T) {
const scale = 120 const scale = 120
vs := quadVertices(w, h, 0, 0, scale) vs := quadVertices(w, h, 0, 0, scale)
is := graphics.QuadIndices() 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) pix, err := dst.Pixels(0, 0, dstW, dstH)
if err != nil { if err != nil {