internal/graphicsdriver: add Blend struct

This is a preparation to specify blend factors and blend operators.

Updates #2382
This commit is contained in:
Hajime Hoshi 2022-10-15 18:58:56 +09:00
parent 2f146d5e4f
commit 09a7d39874
33 changed files with 440 additions and 330 deletions

View File

@ -15,6 +15,8 @@
package ebiten package ebiten
import ( import (
"fmt"
"github.com/hajimehoshi/ebiten/v2/internal/builtinshader" "github.com/hajimehoshi/ebiten/v2/internal/builtinshader"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/ui" "github.com/hajimehoshi/ebiten/v2/internal/ui"
@ -41,50 +43,97 @@ type CompositeMode int
const ( const (
// Regular alpha blending // Regular alpha blending
// c_out = c_src + c_dst × (1 - α_src) // c_out = c_src + c_dst × (1 - α_src)
CompositeModeSourceOver CompositeMode = CompositeMode(graphicsdriver.CompositeModeSourceOver) CompositeModeSourceOver CompositeMode = iota
// c_out = 0 // c_out = 0
CompositeModeClear CompositeMode = CompositeMode(graphicsdriver.CompositeModeClear) CompositeModeClear
// c_out = c_src // c_out = c_src
CompositeModeCopy CompositeMode = CompositeMode(graphicsdriver.CompositeModeCopy) CompositeModeCopy
// c_out = c_dst // c_out = c_dst
CompositeModeDestination CompositeMode = CompositeMode(graphicsdriver.CompositeModeDestination) CompositeModeDestination
// c_out = c_src × (1 - α_dst) + c_dst // c_out = c_src × (1 - α_dst) + c_dst
CompositeModeDestinationOver CompositeMode = CompositeMode(graphicsdriver.CompositeModeDestinationOver) CompositeModeDestinationOver
// c_out = c_src × α_dst // c_out = c_src × α_dst
CompositeModeSourceIn CompositeMode = CompositeMode(graphicsdriver.CompositeModeSourceIn) CompositeModeSourceIn
// c_out = c_dst × α_src // c_out = c_dst × α_src
CompositeModeDestinationIn CompositeMode = CompositeMode(graphicsdriver.CompositeModeDestinationIn) CompositeModeDestinationIn
// c_out = c_src × (1 - α_dst) // c_out = c_src × (1 - α_dst)
CompositeModeSourceOut CompositeMode = CompositeMode(graphicsdriver.CompositeModeSourceOut) CompositeModeSourceOut
// c_out = c_dst × (1 - α_src) // c_out = c_dst × (1 - α_src)
CompositeModeDestinationOut CompositeMode = CompositeMode(graphicsdriver.CompositeModeDestinationOut) CompositeModeDestinationOut
// c_out = c_src × α_dst + c_dst × (1 - α_src) // c_out = c_src × α_dst + c_dst × (1 - α_src)
CompositeModeSourceAtop CompositeMode = CompositeMode(graphicsdriver.CompositeModeSourceAtop) CompositeModeSourceAtop
// c_out = c_src × (1 - α_dst) + c_dst × α_src // c_out = c_src × (1 - α_dst) + c_dst × α_src
CompositeModeDestinationAtop CompositeMode = CompositeMode(graphicsdriver.CompositeModeDestinationAtop) CompositeModeDestinationAtop
// c_out = c_src × (1 - α_dst) + c_dst × (1 - α_src) // c_out = c_src × (1 - α_dst) + c_dst × (1 - α_src)
CompositeModeXor CompositeMode = CompositeMode(graphicsdriver.CompositeModeXor) CompositeModeXor
// Sum of source and destination (a.k.a. 'plus' or 'additive') // Sum of source and destination (a.k.a. 'plus' or 'additive')
// c_out = c_src + c_dst // c_out = c_src + c_dst
CompositeModeLighter CompositeMode = CompositeMode(graphicsdriver.CompositeModeLighter) CompositeModeLighter
// The product of source and destination (a.k.a 'multiply blend mode') // The product of source and destination (a.k.a 'multiply blend mode')
// c_out = c_src * c_dst // c_out = c_src * c_dst
CompositeModeMultiply CompositeMode = CompositeMode(graphicsdriver.CompositeModeMultiply) CompositeModeMultiply
) )
func (c CompositeMode) blend() graphicsdriver.Blend {
src, dst := c.blendFactors()
return graphicsdriver.Blend{
BlendFactorSourceColor: src,
BlendFactorSourceAlpha: src,
BlendFactorDestinationColor: dst,
BlendFactorDestinationAlpha: dst,
BlendOperationColor: graphicsdriver.BlendOperationAdd,
BlendOperationAlpha: graphicsdriver.BlendOperationAdd,
}
}
func (c CompositeMode) blendFactors() (src graphicsdriver.BlendFactor, dst graphicsdriver.BlendFactor) {
switch c {
case CompositeModeSourceOver:
return graphicsdriver.BlendFactorOne, graphicsdriver.BlendFactorOneMinusSourceAlpha
case CompositeModeClear:
return graphicsdriver.BlendFactorZero, graphicsdriver.BlendFactorZero
case CompositeModeCopy:
return graphicsdriver.BlendFactorOne, graphicsdriver.BlendFactorZero
case CompositeModeDestination:
return graphicsdriver.BlendFactorZero, graphicsdriver.BlendFactorOne
case CompositeModeDestinationOver:
return graphicsdriver.BlendFactorOneMinusDestinationAlpha, graphicsdriver.BlendFactorOne
case CompositeModeSourceIn:
return graphicsdriver.BlendFactorDestinationAlpha, graphicsdriver.BlendFactorZero
case CompositeModeDestinationIn:
return graphicsdriver.BlendFactorZero, graphicsdriver.BlendFactorSourceAlpha
case CompositeModeSourceOut:
return graphicsdriver.BlendFactorOneMinusDestinationAlpha, graphicsdriver.BlendFactorZero
case CompositeModeDestinationOut:
return graphicsdriver.BlendFactorZero, graphicsdriver.BlendFactorOneMinusSourceAlpha
case CompositeModeSourceAtop:
return graphicsdriver.BlendFactorDestinationAlpha, graphicsdriver.BlendFactorOneMinusSourceAlpha
case CompositeModeDestinationAtop:
return graphicsdriver.BlendFactorOneMinusDestinationAlpha, graphicsdriver.BlendFactorSourceAlpha
case CompositeModeXor:
return graphicsdriver.BlendFactorOneMinusDestinationAlpha, graphicsdriver.BlendFactorOneMinusSourceAlpha
case CompositeModeLighter:
return graphicsdriver.BlendFactorOne, graphicsdriver.BlendFactorOne
case CompositeModeMultiply:
return graphicsdriver.BlendFactorDestinationColor, graphicsdriver.BlendFactorZero
default:
panic(fmt.Sprintf("ebiten: invalid composite mode: %d", c))
}
}
// GraphicsLibrary represets graphics libraries supported by the engine. // GraphicsLibrary represets graphics libraries supported by the engine.
type GraphicsLibrary = ui.GraphicsLibrary type GraphicsLibrary = ui.GraphicsLibrary

View File

@ -215,7 +215,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
options = &DrawImageOptions{} options = &DrawImageOptions{}
} }
mode := graphicsdriver.CompositeMode(options.CompositeMode) blend := options.CompositeMode.blend()
filter := builtinshader.Filter(options.Filter) filter := builtinshader.Filter(options.Filter)
if offsetX, offsetY := i.adjustPosition(0, 0); offsetX != 0 || offsetY != 0 { if offsetX, offsetY := i.adjustPosition(0, 0); offsetX != 0 || offsetY != 0 {
@ -245,7 +245,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
}) })
} }
i.image.DrawTriangles(srcs, vs, is, mode, i.adjustedRegion(), graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, shader.shader, uniforms, false, canSkipMipmap(options.GeoM, filter)) i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedRegion(), graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, shader.shader, uniforms, false, canSkipMipmap(options.GeoM, filter))
} }
// Vertex represents a vertex passed to DrawTriangles. // Vertex represents a vertex passed to DrawTriangles.
@ -396,7 +396,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
options = &DrawTrianglesOptions{} options = &DrawTrianglesOptions{}
} }
mode := graphicsdriver.CompositeMode(options.CompositeMode) blend := options.CompositeMode.blend()
address := builtinshader.Address(options.Address) address := builtinshader.Address(options.Address)
var sr graphicsdriver.Region var sr graphicsdriver.Region
@ -455,7 +455,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
}) })
} }
i.image.DrawTriangles(srcs, vs, is, mode, i.adjustedRegion(), sr, [graphics.ShaderImageCount - 1][2]float32{}, shader.shader, uniforms, options.FillRule == EvenOdd, filter != builtinshader.FilterLinear) i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedRegion(), sr, [graphics.ShaderImageCount - 1][2]float32{}, shader.shader, uniforms, options.FillRule == EvenOdd, filter != builtinshader.FilterLinear)
} }
// DrawTrianglesShaderOptions represents options for DrawTrianglesShader. // DrawTrianglesShaderOptions represents options for DrawTrianglesShader.
@ -525,7 +525,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
options = &DrawTrianglesShaderOptions{} options = &DrawTrianglesShaderOptions{}
} }
mode := graphicsdriver.CompositeMode(options.CompositeMode) blend := options.CompositeMode.blend()
vs := graphics.Vertices(len(vertices)) vs := graphics.Vertices(len(vertices))
dst := i dst := i
@ -589,7 +589,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
offsets[i][1] = float32(y - sy) offsets[i][1] = float32(y - sy)
} }
i.image.DrawTriangles(imgs, vs, is, mode, i.adjustedRegion(), sr, offsets, shader.shader, shader.convertUniforms(options.Uniforms), options.FillRule == EvenOdd, true) i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedRegion(), sr, offsets, shader.shader, shader.convertUniforms(options.Uniforms), options.FillRule == EvenOdd, true)
} }
// DrawRectShaderOptions represents options for DrawRectShader. // DrawRectShaderOptions represents options for DrawRectShader.
@ -645,7 +645,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
options = &DrawRectShaderOptions{} options = &DrawRectShaderOptions{}
} }
mode := graphicsdriver.CompositeMode(options.CompositeMode) blend := options.CompositeMode.blend()
var imgs [graphics.ShaderImageCount]*ui.Image var imgs [graphics.ShaderImageCount]*ui.Image
for i, img := range options.Images { for i, img := range options.Images {
@ -690,7 +690,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
offsets[i][1] = float32(y - sy) offsets[i][1] = float32(y - sy)
} }
i.image.DrawTriangles(imgs, vs, is, mode, i.adjustedRegion(), sr, offsets, shader.shader, shader.convertUniforms(options.Uniforms), false, true) i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedRegion(), sr, offsets, shader.shader, shader.convertUniforms(options.Uniforms), false, true)
} }
// 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

@ -309,7 +309,7 @@ func (i *Image) ensureIsolated() {
Width: float32(w - 2*i.paddingSize()), Width: float32(w - 2*i.paddingSize()),
Height: float32(h - 2*i.paddingSize()), Height: float32(h - 2*i.paddingSize()),
} }
newImg.DrawTriangles(srcs, offsets, vs, is, graphicsdriver.CompositeModeCopy, dstRegion, graphicsdriver.Region{}, NearestFilterShader.shader, nil, false) newImg.DrawTriangles(srcs, offsets, vs, is, graphicsdriver.BlendCopy, dstRegion, graphicsdriver.Region{}, NearestFilterShader.shader, nil, false)
i.dispose(false) i.dispose(false)
i.backend = &backend{ i.backend = &backend{
@ -348,7 +348,7 @@ func (i *Image) putOnAtlas(graphicsDriver graphicsdriver.Graphics) error {
Width: w, Width: w,
Height: h, Height: h,
} }
newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader, nil, false, true) newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader, nil, false, true)
newI.moveTo(i) newI.moveTo(i)
i.usedAsSourceCount = 0 i.usedAsSourceCount = 0
@ -395,13 +395,13 @@ 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.ShaderImageCount]*Image, vertices []float32, indices []uint16, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool) { func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool) {
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()
i.drawTriangles(srcs, vertices, indices, mode, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd, false) i.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd, false)
} }
func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, keepOnAtlas bool) { func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, keepOnAtlas bool) {
if i.disposed { if i.disposed {
panic("atlas: the drawing target image must not be disposed (DrawTriangles)") panic("atlas: the drawing target image must not be disposed (DrawTriangles)")
} }
@ -472,7 +472,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
imgs[i] = src.backend.restorable imgs[i] = src.backend.restorable
} }
i.backend.restorable.DrawTriangles(imgs, offsets, vertices, indices, mode, dstRegion, srcRegion, shader.shader, uniforms, evenOdd) i.backend.restorable.DrawTriangles(imgs, offsets, vertices, indices, blend, dstRegion, srcRegion, shader.shader, uniforms, evenOdd)
for _, src := range srcs { for _, src := range srcs {
if src == nil { if src == nil {

View File

@ -106,7 +106,7 @@ func TestEnsureIsolated(t *testing.T) {
Width: size, Width: size,
Height: size, Height: size,
} }
img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := img4.IsOnAtlasForTesting(), false; got != want { if got, want := img4.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -119,7 +119,7 @@ func TestEnsureIsolated(t *testing.T) {
Width: size / 2, Width: size / 2,
Height: size / 2, Height: size / 2,
} }
img3.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) img3.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := img3.IsOnAtlasForTesting(), false; got != want { if got, want := img3.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -148,7 +148,7 @@ func TestEnsureIsolated(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.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
} }
func TestReputOnAtlas(t *testing.T) { func TestReputOnAtlas(t *testing.T) {
@ -195,7 +195,7 @@ func TestReputOnAtlas(t *testing.T) {
Width: size, Width: size,
Height: size, Height: size,
} }
img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := img1.IsOnAtlasForTesting(), false; got != want { if got, want := img1.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -207,7 +207,7 @@ func TestReputOnAtlas(t *testing.T) {
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := img1.IsOnAtlasForTesting(), false; got != want { if got, want := img1.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -235,7 +235,7 @@ func TestReputOnAtlas(t *testing.T) {
} }
// img1 is on an atlas again. // img1 is on an atlas again.
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := img1.IsOnAtlasForTesting(), true; got != want { if got, want := img1.IsOnAtlasForTesting(), true; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -259,7 +259,7 @@ func TestReputOnAtlas(t *testing.T) {
} }
// Use img1 as a render target again. // Use img1 as a render target again.
img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := img1.IsOnAtlasForTesting(), false; got != want { if got, want := img1.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -271,7 +271,7 @@ func TestReputOnAtlas(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
img1.WritePixels(make([]byte, 4*size*size), 0, 0, size, size) img1.WritePixels(make([]byte, 4*size*size), 0, 0, size, size)
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := img1.IsOnAtlasForTesting(), false; got != want { if got, want := img1.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -281,7 +281,7 @@ func TestReputOnAtlas(t *testing.T) {
} }
// img1 is not on an atlas due to WritePixels. // img1 is not on an atlas due to WritePixels.
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := img1.IsOnAtlasForTesting(), false; got != want { if got, want := img1.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -291,7 +291,7 @@ func TestReputOnAtlas(t *testing.T) {
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := img3.IsOnAtlasForTesting(), false; got != want { if got, want := img3.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -391,7 +391,7 @@ func TestWritePixelsAfterDrawTriangles(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
dst.WritePixels(pix, 0, 0, w, h) dst.WritePixels(pix, 0, 0, w, h)
pix = make([]byte, 4*w*h) pix = make([]byte, 4*w*h)
@ -439,7 +439,7 @@ func TestSmallImages(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
pix = make([]byte, 4*w*h) pix = make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil { if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
@ -487,7 +487,7 @@ func TestLongImages(t *testing.T) {
Width: dstW, Width: dstW,
Height: dstH, Height: dstH,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
pix = make([]byte, 4*dstW*dstH) pix = make([]byte, 4*dstW*dstH)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil { if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
@ -604,7 +604,7 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
Width: size, Width: size,
Height: size, Height: size,
} }
src.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) src.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := src.IsOnAtlasForTesting(), false; got != want { if got, want := src.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -614,7 +614,7 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := src.IsOnAtlasForTesting(), false; got != want { if got, want := src.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -654,7 +654,7 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
} }
// Use src2 as a rendering target, and make src2 an independent image. // Use src2 as a rendering target, and make src2 an independent image.
src2.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) src2.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := src2.IsOnAtlasForTesting(), false; got != want { if got, want := src2.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -675,7 +675,7 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := src2.IsOnAtlasForTesting(), false; got != want { if got, want := src2.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }

View File

@ -40,12 +40,12 @@ func TestShaderFillTwice(t *testing.T) {
} }
g := ui.GraphicsDriverForTesting() g := ui.GraphicsDriverForTesting()
s0 := atlas.NewShader(etesting.ShaderProgramFill(0xff, 0xff, 0xff, 0xff)) s0 := atlas.NewShader(etesting.ShaderProgramFill(0xff, 0xff, 0xff, 0xff))
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, s0, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, s0, nil, false)
// Vertices must be recreated (#1755) // Vertices must be recreated (#1755)
vs = quadVertices(w, h, 0, 0, 1) vs = quadVertices(w, h, 0, 0, 1)
s1 := atlas.NewShader(etesting.ShaderProgramFill(0x80, 0x80, 0x80, 0xff)) s1 := atlas.NewShader(etesting.ShaderProgramFill(0x80, 0x80, 0x80, 0xff))
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, s1, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, s1, nil, false)
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(g, pix); err != nil { if err := dst.ReadPixels(g, pix); err != nil {
@ -73,11 +73,11 @@ func TestImageDrawTwice(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
// Vertices must be recreated (#1755) // Vertices must be recreated (#1755)
vs = quadVertices(w, h, 0, 0, 1) vs = quadVertices(w, h, 0, 0, 1)
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil { if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {

View File

@ -140,7 +140,7 @@ func (i *Image) WritePixels(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.ShaderImageCount]*Image, vertices []float32, indices []uint16, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool) { func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool) {
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")
@ -150,7 +150,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
if maybeCanAddDelayedCommand() { if maybeCanAddDelayedCommand() {
if tryAddDelayedCommand(func() { if tryAddDelayedCommand(func() {
// 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, mode, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd) i.DrawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd)
}) { }) {
return return
} }
@ -165,7 +165,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
} }
i.invalidatePixels() i.invalidatePixels()
i.img.DrawTriangles(imgs, vertices, indices, mode, dstRegion, srcRegion, subimageOffsets, shader.shader, uniforms, evenOdd) i.img.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader.shader, uniforms, evenOdd)
} }
type Shader struct { type Shader struct {

View File

@ -89,7 +89,7 @@ func mustUseDifferentVertexBuffer(nextNumVertexFloats, nextNumIndices int) bool
} }
// EnqueueDrawTrianglesCommand enqueues a drawing-image command. // EnqueueDrawTrianglesCommand enqueues a drawing-image command.
func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) { func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) {
if len(indices) > graphics.IndicesCount { if len(indices) > graphics.IndicesCount {
panic(fmt.Sprintf("graphicscommand: len(indices) must be <= graphics.IndicesCount but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesCount: %d", len(indices), graphics.IndicesCount)) panic(fmt.Sprintf("graphicscommand: len(indices) must be <= graphics.IndicesCount but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesCount: %d", len(indices), graphics.IndicesCount))
} }
@ -123,7 +123,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) {
if last, ok := q.commands[len(q.commands)-1].(*drawTrianglesCommand); ok { if last, ok := q.commands[len(q.commands)-1].(*drawTrianglesCommand); ok {
if last.CanMergeWithDrawTrianglesCommand(dst, srcs, vertices, mode, dstRegion, srcRegion, shader, uniforms, evenOdd) { if last.CanMergeWithDrawTrianglesCommand(dst, srcs, vertices, blend, dstRegion, srcRegion, shader, uniforms, evenOdd) {
last.setVertices(q.lastVertices(len(vertices) + last.numVertices())) last.setVertices(q.lastVertices(len(vertices) + last.numVertices()))
last.addNumIndices(len(indices)) last.addNumIndices(len(indices))
return return
@ -137,7 +137,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
c.offsets = offsets c.offsets = offsets
c.vertices = q.lastVertices(len(vertices)) c.vertices = q.lastVertices(len(vertices))
c.nindices = len(indices) c.nindices = len(indices)
c.mode = mode c.blend = blend
c.dstRegion = dstRegion c.dstRegion = dstRegion
c.srcRegion = srcRegion c.srcRegion = srcRegion
c.shader = shader c.shader = shader
@ -259,7 +259,7 @@ type drawTrianglesCommand struct {
offsets [graphics.ShaderImageCount - 1][2]float32 offsets [graphics.ShaderImageCount - 1][2]float32
vertices []float32 vertices []float32
nindices int nindices int
mode graphicsdriver.CompositeMode blend graphicsdriver.Blend
dstRegion graphicsdriver.Region dstRegion graphicsdriver.Region
srcRegion graphicsdriver.Region srcRegion graphicsdriver.Region
shader *Shader shader *Shader
@ -268,39 +268,14 @@ type drawTrianglesCommand struct {
} }
func (c *drawTrianglesCommand) String() string { func (c *drawTrianglesCommand) String() string {
mode := "" // TODO: Improve readability
switch c.mode { blend := fmt.Sprintf("{src-color: %d, src-alpha: %d, dst-color: %d, dst-alpha: %d, op-color: %d, op-alpha: %d}",
case graphicsdriver.CompositeModeSourceOver: c.blend.BlendFactorSourceColor,
mode = "source-over" c.blend.BlendFactorSourceAlpha,
case graphicsdriver.CompositeModeClear: c.blend.BlendFactorDestinationColor,
mode = "clear" c.blend.BlendFactorDestinationAlpha,
case graphicsdriver.CompositeModeCopy: c.blend.BlendOperationColor,
mode = "copy" c.blend.BlendOperationAlpha)
case graphicsdriver.CompositeModeDestination:
mode = "destination"
case graphicsdriver.CompositeModeDestinationOver:
mode = "destination-over"
case graphicsdriver.CompositeModeSourceIn:
mode = "source-in"
case graphicsdriver.CompositeModeDestinationIn:
mode = "destination-in"
case graphicsdriver.CompositeModeSourceOut:
mode = "source-out"
case graphicsdriver.CompositeModeDestinationOut:
mode = "destination-out"
case graphicsdriver.CompositeModeSourceAtop:
mode = "source-atop"
case graphicsdriver.CompositeModeDestinationAtop:
mode = "destination-atop"
case graphicsdriver.CompositeModeXor:
mode = "xor"
case graphicsdriver.CompositeModeLighter:
mode = "lighter"
case graphicsdriver.CompositeModeMultiply:
mode = "multiply"
default:
panic(fmt.Sprintf("graphicscommand: invalid composite mode: %d", c.mode))
}
dst := fmt.Sprintf("%d", c.dst.id) dst := fmt.Sprintf("%d", c.dst.id)
if c.dst.screen { if c.dst.screen {
@ -321,7 +296,7 @@ func (c *drawTrianglesCommand) String() string {
r := fmt.Sprintf("(x:%d, y:%d, width:%d, height:%d)", r := fmt.Sprintf("(x:%d, y:%d, width:%d, height:%d)",
int(c.dstRegion.X), int(c.dstRegion.Y), int(c.dstRegion.Width), int(c.dstRegion.Height)) int(c.dstRegion.X), int(c.dstRegion.Y), int(c.dstRegion.Width), int(c.dstRegion.Height))
return fmt.Sprintf("draw-triangles: dst: %s <- src: [%s], dst region: %s, num of indices: %d, mode: %s, even-odd: %t", dst, strings.Join(srcstrs[:], ", "), r, c.nindices, mode, c.evenOdd) return fmt.Sprintf("draw-triangles: dst: %s <- src: [%s], dst region: %s, num of indices: %d, blend: %s, even-odd: %t", dst, strings.Join(srcstrs[:], ", "), r, c.nindices, blend, c.evenOdd)
} }
// Exec executes the drawTrianglesCommand. // Exec executes the drawTrianglesCommand.
@ -340,7 +315,7 @@ func (c *drawTrianglesCommand) Exec(graphicsDriver graphicsdriver.Graphics, inde
imgs[i] = src.image.ID() imgs[i] = src.image.ID()
} }
return graphicsDriver.DrawTriangles(c.dst.image.ID(), imgs, c.offsets, c.shader.shader.ID(), c.nindices, indexOffset, c.mode, c.dstRegion, c.srcRegion, c.uniforms, c.evenOdd) return graphicsDriver.DrawTriangles(c.dst.image.ID(), imgs, c.offsets, c.shader.shader.ID(), c.nindices, indexOffset, c.blend, c.dstRegion, c.srcRegion, c.uniforms, c.evenOdd)
} }
func (c *drawTrianglesCommand) numVertices() int { func (c *drawTrianglesCommand) numVertices() int {
@ -361,7 +336,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.ShaderImageCount]*Image, vertices []float32, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) bool { func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, vertices []float32, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) bool {
if c.shader != shader { if c.shader != shader {
return false return false
} }
@ -384,7 +359,7 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs
if c.srcs != srcs { if c.srcs != srcs {
return false return false
} }
if c.mode != mode { if c.blend != blend {
return false return false
} }
if c.dstRegion != dstRegion { if c.dstRegion != dstRegion {

View File

@ -127,7 +127,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.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) { func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) {
for _, src := range srcs { for _, src := range srcs {
if src == nil { if src == nil {
continue continue
@ -139,7 +139,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [g
} }
i.resolveBufferedWritePixels() i.resolveBufferedWritePixels()
theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, vertices, indices, mode, dstRegion, srcRegion, shader, uniforms, evenOdd) theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, vertices, indices, blend, dstRegion, srcRegion, shader, uniforms, evenOdd)
} }
// ReadPixels reads the image's pixels. // ReadPixels reads the image's pixels.

View File

@ -65,7 +65,7 @@ func TestClear(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeClear, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendClear, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false)
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, w, h); err != nil { if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, w, h); err != nil {
@ -96,8 +96,8 @@ func TestWritePixelsPartAfterDrawTriangles(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeClear, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendClear, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false)
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false)
dst.WritePixels(make([]byte, 4), 0, 0, 1, 1) dst.WritePixels(make([]byte, 4), 0, 0, 1, 1)
// TODO: Check the result. // TODO: Check the result.
@ -115,11 +115,11 @@ func TestShader(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeClear, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendClear, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false)
g := ui.GraphicsDriverForTesting() g := ui.GraphicsDriverForTesting()
s := graphicscommand.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff)) s := graphicscommand.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff))
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, s, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, s, nil, false)
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(g, pix, 0, 0, w, h); err != nil { if err := dst.ReadPixels(g, pix, 0, 0, w, h); err != nil {

View File

@ -0,0 +1,71 @@
// Copyright 2022 The Ebitengine 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.
package graphicsdriver
type Blend struct {
BlendFactorSourceColor BlendFactor
BlendFactorSourceAlpha BlendFactor
BlendFactorDestinationColor BlendFactor
BlendFactorDestinationAlpha BlendFactor
BlendOperationColor BlendOperation
BlendOperationAlpha BlendOperation
}
type BlendFactor int
const (
BlendFactorZero BlendFactor = iota
BlendFactorOne
BlendFactorSourceAlpha
BlendFactorDestinationAlpha
BlendFactorOneMinusSourceAlpha
BlendFactorOneMinusDestinationAlpha
BlendFactorDestinationColor
)
type BlendOperation int
const (
BlendOperationAdd BlendOperation = iota
// TODO: Add more operators
)
var BlendSourceOver = Blend{
BlendFactorSourceColor: BlendFactorOne,
BlendFactorSourceAlpha: BlendFactorOne,
BlendFactorDestinationColor: BlendFactorOneMinusSourceAlpha,
BlendFactorDestinationAlpha: BlendFactorOneMinusSourceAlpha,
BlendOperationColor: BlendOperationAdd,
BlendOperationAlpha: BlendOperationAdd,
}
var BlendClear = Blend{
BlendFactorSourceColor: BlendFactorZero,
BlendFactorSourceAlpha: BlendFactorZero,
BlendFactorDestinationColor: BlendFactorZero,
BlendFactorDestinationAlpha: BlendFactorZero,
BlendOperationColor: BlendOperationAdd,
BlendOperationAlpha: BlendOperationAdd,
}
var BlendCopy = Blend{
BlendFactorSourceColor: BlendFactorOne,
BlendFactorSourceAlpha: BlendFactorOne,
BlendFactorDestinationColor: BlendFactorZero,
BlendFactorDestinationAlpha: BlendFactorZero,
BlendOperationColor: BlendOperationAdd,
BlendOperationAlpha: BlendOperationAdd,
}

View File

@ -1,88 +0,0 @@
// Copyright 2018 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.
package graphicsdriver
import (
"fmt"
)
type CompositeMode int
const (
CompositeModeUnknown CompositeMode = iota - 1
CompositeModeSourceOver // This value must be 0 (= initial value)
CompositeModeClear
CompositeModeCopy
CompositeModeDestination
CompositeModeDestinationOver
CompositeModeSourceIn
CompositeModeDestinationIn
CompositeModeSourceOut
CompositeModeDestinationOut
CompositeModeSourceAtop
CompositeModeDestinationAtop
CompositeModeXor
CompositeModeLighter
CompositeModeMultiply
CompositeModeMax = CompositeModeMultiply
)
type BlendFactor int
const (
BlendFactorZero BlendFactor = iota
BlendFactorOne
BlendFactorSourceAlpha
BlendFactorDestinationAlpha
BlendFactorOneMinusSourceAlpha
BlendFactorOneMinusDestinationAlpha
BlendFactorDestinationColor
)
func (c CompositeMode) BlendFactors() (src BlendFactor, dst BlendFactor) {
switch c {
case CompositeModeSourceOver:
return BlendFactorOne, BlendFactorOneMinusSourceAlpha
case CompositeModeClear:
return BlendFactorZero, BlendFactorZero
case CompositeModeCopy:
return BlendFactorOne, BlendFactorZero
case CompositeModeDestination:
return BlendFactorZero, BlendFactorOne
case CompositeModeDestinationOver:
return BlendFactorOneMinusDestinationAlpha, BlendFactorOne
case CompositeModeSourceIn:
return BlendFactorDestinationAlpha, BlendFactorZero
case CompositeModeDestinationIn:
return BlendFactorZero, BlendFactorSourceAlpha
case CompositeModeSourceOut:
return BlendFactorOneMinusDestinationAlpha, BlendFactorZero
case CompositeModeDestinationOut:
return BlendFactorZero, BlendFactorOneMinusSourceAlpha
case CompositeModeSourceAtop:
return BlendFactorDestinationAlpha, BlendFactorOneMinusSourceAlpha
case CompositeModeDestinationAtop:
return BlendFactorOneMinusDestinationAlpha, BlendFactorSourceAlpha
case CompositeModeXor:
return BlendFactorOneMinusDestinationAlpha, BlendFactorOneMinusSourceAlpha
case CompositeModeLighter:
return BlendFactorOne, BlendFactorOne
case CompositeModeMultiply:
return BlendFactorDestinationColor, BlendFactorZero
default:
panic(fmt.Sprintf("graphicsdriver: invalid composite mode: %d", c))
}
}

View File

@ -1190,7 +1190,7 @@ func (g *Graphics) NewShader(program *shaderir.Program) (graphicsdriver.Shader,
return s, nil return s, nil
} }
func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.ShaderImageCount]graphicsdriver.ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error { func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.ShaderImageCount]graphicsdriver.ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error {
if shaderID == graphicsdriver.InvalidShaderID { if shaderID == graphicsdriver.InvalidShaderID {
return fmt.Errorf("directx: shader ID is invalid") return fmt.Errorf("directx: shader ID is invalid")
} }
@ -1315,7 +1315,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.Sh
}) })
if evenOdd { if evenOdd {
s, err := shader.pipelineState(mode, prepareStencil, dst.screen) s, err := shader.pipelineState(blend, prepareStencil, dst.screen)
if err != nil { if err != nil {
return err return err
} }
@ -1323,7 +1323,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.Sh
return err return err
} }
s, err = shader.pipelineState(mode, drawWithStencil, dst.screen) s, err = shader.pipelineState(blend, drawWithStencil, dst.screen)
if err != nil { if err != nil {
return err return err
} }
@ -1331,7 +1331,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.Sh
return err return err
} }
} else { } else {
s, err := shader.pipelineState(mode, noStencil, dst.screen) s, err := shader.pipelineState(blend, noStencil, dst.screen)
if err != nil { if err != nil {
return err return err
} }
@ -1773,9 +1773,9 @@ const (
) )
type pipelineStateKey struct { type pipelineStateKey struct {
compositeMode graphicsdriver.CompositeMode blend graphicsdriver.Blend
stencilMode stencilMode stencilMode stencilMode
screen bool screen bool
} }
type Shader struct { type Shader struct {
@ -1812,17 +1812,17 @@ func (s *Shader) disposeImpl() {
} }
} }
func (s *Shader) pipelineState(compositeMode graphicsdriver.CompositeMode, stencilMode stencilMode, screen bool) (*_ID3D12PipelineState, error) { func (s *Shader) pipelineState(blend graphicsdriver.Blend, stencilMode stencilMode, screen bool) (*_ID3D12PipelineState, error) {
key := pipelineStateKey{ key := pipelineStateKey{
compositeMode: compositeMode, blend: blend,
stencilMode: stencilMode, stencilMode: stencilMode,
screen: screen, screen: screen,
} }
if state, ok := s.pipelineStates[key]; ok { if state, ok := s.pipelineStates[key]; ok {
return state, nil return state, nil
} }
state, err := s.graphics.pipelineStates.newPipelineState(s.graphics.device, s.vertexShader, s.pixelShader, compositeMode, stencilMode, screen) state, err := s.graphics.pipelineStates.newPipelineState(s.graphics.device, s.vertexShader, s.pixelShader, blend, stencilMode, screen)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -25,8 +25,8 @@ import (
const numDescriptorsPerFrame = 32 const numDescriptorsPerFrame = 32
func blendFactorToBlend(c graphicsdriver.BlendFactor, alpha bool) _D3D12_BLEND { func blendFactorToBlend(f graphicsdriver.BlendFactor, alpha bool) _D3D12_BLEND {
switch c { switch f {
case graphicsdriver.BlendFactorZero: case graphicsdriver.BlendFactorZero:
return _D3D12_BLEND_ZERO return _D3D12_BLEND_ZERO
case graphicsdriver.BlendFactorOne: case graphicsdriver.BlendFactorOne:
@ -40,12 +40,23 @@ func blendFactorToBlend(c graphicsdriver.BlendFactor, alpha bool) _D3D12_BLEND {
case graphicsdriver.BlendFactorOneMinusDestinationAlpha: case graphicsdriver.BlendFactorOneMinusDestinationAlpha:
return _D3D12_BLEND_INV_DEST_ALPHA return _D3D12_BLEND_INV_DEST_ALPHA
case graphicsdriver.BlendFactorDestinationColor: case graphicsdriver.BlendFactorDestinationColor:
// D3D12_RENDER_TARGET_BLEND_DESC's *BlendAlpha members don't allow *_COLOR values.
// See https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_render_target_blend_desc.
if alpha { if alpha {
return _D3D12_BLEND_DEST_ALPHA return _D3D12_BLEND_DEST_ALPHA
} }
return _D3D12_BLEND_DEST_COLOR return _D3D12_BLEND_DEST_COLOR
default: default:
panic(fmt.Sprintf("directx: invalid blend factor: %d", c)) panic(fmt.Sprintf("directx: invalid blend factor: %d", f))
}
}
func blendOperationToBlendOp(o graphicsdriver.BlendOperation) _D3D12_BLEND_OP {
switch o {
case graphicsdriver.BlendOperationAdd:
return _D3D12_BLEND_OP_ADD
default:
panic(fmt.Sprintf("directx: invalid blend operation: %d", o))
} }
} }
@ -336,7 +347,7 @@ func newShader(source []byte, defs []_D3D_SHADER_MACRO) (vsh, psh *_ID3DBlob, fe
return v, p, nil return v, p, nil
} }
func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3DBlob, compositeMode graphicsdriver.CompositeMode, stencilMode stencilMode, screen bool) (state *_ID3D12PipelineState, ferr error) { func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3DBlob, blend graphicsdriver.Blend, stencilMode stencilMode, screen bool) (state *_ID3D12PipelineState, ferr error) {
rootSignature, err := p.ensureRootSignature(device) rootSignature, err := p.ensureRootSignature(device)
if err != nil { if err != nil {
return nil, err return nil, err
@ -391,7 +402,6 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D
} }
// Create a pipeline state. // Create a pipeline state.
srcOp, dstOp := compositeMode.BlendFactors()
psoDesc := _D3D12_GRAPHICS_PIPELINE_STATE_DESC{ psoDesc := _D3D12_GRAPHICS_PIPELINE_STATE_DESC{
pRootSignature: rootSignature, pRootSignature: rootSignature,
VS: _D3D12_SHADER_BYTECODE{ VS: _D3D12_SHADER_BYTECODE{
@ -409,12 +419,12 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D
{ {
BlendEnable: 1, BlendEnable: 1,
LogicOpEnable: 0, LogicOpEnable: 0,
SrcBlend: blendFactorToBlend(srcOp, false), SrcBlend: blendFactorToBlend(blend.BlendFactorSourceColor, false),
DestBlend: blendFactorToBlend(dstOp, false), DestBlend: blendFactorToBlend(blend.BlendFactorDestinationColor, false),
BlendOp: _D3D12_BLEND_OP_ADD, BlendOp: blendOperationToBlendOp(blend.BlendOperationColor),
SrcBlendAlpha: blendFactorToBlend(srcOp, true), SrcBlendAlpha: blendFactorToBlend(blend.BlendFactorSourceAlpha, true),
DestBlendAlpha: blendFactorToBlend(dstOp, true), DestBlendAlpha: blendFactorToBlend(blend.BlendFactorDestinationAlpha, true),
BlendOpAlpha: _D3D12_BLEND_OP_ADD, BlendOpAlpha: blendOperationToBlendOp(blend.BlendOperationAlpha),
LogicOp: _D3D12_LOGIC_OP_NOOP, LogicOp: _D3D12_LOGIC_OP_NOOP,
RenderTargetWriteMask: writeMask, RenderTargetWriteMask: writeMask,
}, },

View File

@ -52,7 +52,7 @@ type Graphics interface {
NewShader(program *shaderir.Program) (Shader, error) NewShader(program *shaderir.Program) (Shader, error)
// DrawTriangles draws an image onto another image with the given parameters. // DrawTriangles draws an image onto another image with the given parameters.
DrawTriangles(dst ImageID, srcs [graphics.ShaderImageCount]ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shader ShaderID, indexLen int, indexOffset int, mode CompositeMode, dstRegion, srcRegion Region, uniforms [][]float32, evenOdd bool) error DrawTriangles(dst ImageID, srcs [graphics.ShaderImageCount]ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shader ShaderID, indexLen int, indexOffset int, blend Blend, dstRegion, srcRegion Region, uniforms [][]float32, evenOdd bool) 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

@ -317,7 +317,16 @@ func blendFactorToMetalBlendFactor(c graphicsdriver.BlendFactor) mtl.BlendFactor
case graphicsdriver.BlendFactorDestinationColor: case graphicsdriver.BlendFactorDestinationColor:
return mtl.BlendFactorDestinationColor return mtl.BlendFactorDestinationColor
default: default:
panic(fmt.Sprintf("metal: invalid operation: %d", c)) panic(fmt.Sprintf("metal: invalid blend factor: %d", c))
}
}
func blendOperationToMetalBlendOperation(o graphicsdriver.BlendOperation) mtl.BlendOperation {
switch o {
case graphicsdriver.BlendOperationAdd:
return mtl.BlendOperationAdd
default:
panic(fmt.Sprintf("metal: invalid blend operation: %d", o))
} }
} }
@ -478,7 +487,7 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, dstRegion graph
return nil return nil
} }
func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error { func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error {
if shaderID == graphicsdriver.InvalidShaderID { if shaderID == graphicsdriver.InvalidShaderID {
return fmt.Errorf("metal: shader ID is invalid") return fmt.Errorf("metal: shader ID is invalid")
} }
@ -575,14 +584,14 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
} }
if evenOdd { if evenOdd {
prepareStencilRpss, err := g.shaders[shaderID].RenderPipelineState(&g.view, mode, prepareStencil, dst.screen) prepareStencilRpss, err := g.shaders[shaderID].RenderPipelineState(&g.view, blend, prepareStencil, dst.screen)
if err != nil { if err != nil {
return err return err
} }
if err := g.draw(prepareStencilRpss, dst, dstRegion, srcs, indexLen, indexOffset, uniformVars, prepareStencil); err != nil { if err := g.draw(prepareStencilRpss, dst, dstRegion, srcs, indexLen, indexOffset, uniformVars, prepareStencil); err != nil {
return err return err
} }
drawWithStencilRpss, err := g.shaders[shaderID].RenderPipelineState(&g.view, mode, drawWithStencil, dst.screen) drawWithStencilRpss, err := g.shaders[shaderID].RenderPipelineState(&g.view, blend, drawWithStencil, dst.screen)
if err != nil { if err != nil {
return err return err
} }
@ -590,7 +599,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
return err return err
} }
} else { } else {
rpss, err := g.shaders[shaderID].RenderPipelineState(&g.view, mode, noStencil, dst.screen) rpss, err := g.shaders[shaderID].RenderPipelineState(&g.view, blend, noStencil, dst.screen)
if err != nil { if err != nil {
return err return err
} }

View File

@ -290,6 +290,16 @@ const (
BlendFactorOneMinusSource1Alpha BlendFactor = 18 BlendFactorOneMinusSource1Alpha BlendFactor = 18
) )
type BlendOperation uint8
const (
BlendOperationAdd BlendOperation = 0
BlendOperationSubtract BlendOperation = 1
BlendOperationReverseSubtract BlendOperation = 2
BlendOperationMin BlendOperation = 3
BlendOperationMax BlendOperation = 4
)
type ColorWriteMask uint8 type ColorWriteMask uint8
const ( const (
@ -383,6 +393,8 @@ type RenderPipelineColorAttachmentDescriptor struct {
DestinationRGBBlendFactor BlendFactor DestinationRGBBlendFactor BlendFactor
SourceAlphaBlendFactor BlendFactor SourceAlphaBlendFactor BlendFactor
SourceRGBBlendFactor BlendFactor SourceRGBBlendFactor BlendFactor
AlphaBlendOperation BlendOperation
RGBBlendOperation BlendOperation
WriteMask ColorWriteMask WriteMask ColorWriteMask
} }
@ -492,6 +504,8 @@ var (
sel_setDestinationRGBBlendFactor = objc.RegisterName("setDestinationRGBBlendFactor:") sel_setDestinationRGBBlendFactor = objc.RegisterName("setDestinationRGBBlendFactor:")
sel_setSourceAlphaBlendFactor = objc.RegisterName("setSourceAlphaBlendFactor:") sel_setSourceAlphaBlendFactor = objc.RegisterName("setSourceAlphaBlendFactor:")
sel_setSourceRGBBlendFactor = objc.RegisterName("setSourceRGBBlendFactor:") sel_setSourceRGBBlendFactor = objc.RegisterName("setSourceRGBBlendFactor:")
sel_setAlphaBlendOperation = objc.RegisterName("setAlphaBlendOperation:")
sel_setRgbBlendOperation = objc.RegisterName("setRgbBlendOperation:")
sel_setWriteMask = objc.RegisterName("setWriteMask:") sel_setWriteMask = objc.RegisterName("setWriteMask:")
sel_setStencilAttachmentPixelFormat = objc.RegisterName("setStencilAttachmentPixelFormat:") sel_setStencilAttachmentPixelFormat = objc.RegisterName("setStencilAttachmentPixelFormat:")
sel_newRenderPipelineStateWithDescriptor_error = objc.RegisterName("newRenderPipelineStateWithDescriptor:error:") sel_newRenderPipelineStateWithDescriptor_error = objc.RegisterName("newRenderPipelineStateWithDescriptor:error:")
@ -624,6 +638,8 @@ func (d Device) MakeRenderPipelineState(rpd RenderPipelineDescriptor) (RenderPip
colorAttachments0.Send(sel_setDestinationRGBBlendFactor, uintptr(rpd.ColorAttachments[0].DestinationRGBBlendFactor)) colorAttachments0.Send(sel_setDestinationRGBBlendFactor, uintptr(rpd.ColorAttachments[0].DestinationRGBBlendFactor))
colorAttachments0.Send(sel_setSourceAlphaBlendFactor, uintptr(rpd.ColorAttachments[0].SourceAlphaBlendFactor)) colorAttachments0.Send(sel_setSourceAlphaBlendFactor, uintptr(rpd.ColorAttachments[0].SourceAlphaBlendFactor))
colorAttachments0.Send(sel_setSourceRGBBlendFactor, uintptr(rpd.ColorAttachments[0].SourceRGBBlendFactor)) colorAttachments0.Send(sel_setSourceRGBBlendFactor, uintptr(rpd.ColorAttachments[0].SourceRGBBlendFactor))
colorAttachments0.Send(sel_setAlphaBlendOperation, uintptr(rpd.ColorAttachments[0].AlphaBlendOperation))
colorAttachments0.Send(sel_setRgbBlendOperation, uintptr(rpd.ColorAttachments[0].RGBBlendOperation))
colorAttachments0.Send(sel_setWriteMask, uintptr(rpd.ColorAttachments[0].WriteMask)) colorAttachments0.Send(sel_setWriteMask, uintptr(rpd.ColorAttachments[0].WriteMask))
renderPipelineDescriptor.Send(sel_setStencilAttachmentPixelFormat, uintptr(rpd.StencilAttachmentPixelFormat)) renderPipelineDescriptor.Send(sel_setStencilAttachmentPixelFormat, uintptr(rpd.StencilAttachmentPixelFormat))
var err cocoa.NSError var err cocoa.NSError

View File

@ -24,9 +24,9 @@ import (
) )
type shaderRpsKey struct { type shaderRpsKey struct {
compositeMode graphicsdriver.CompositeMode blend graphicsdriver.Blend
stencilMode stencilMode stencilMode stencilMode
screen bool screen bool
} }
type Shader struct { type Shader struct {
@ -86,11 +86,11 @@ func (s *Shader) init(device mtl.Device) error {
return nil return nil
} }
func (s *Shader) RenderPipelineState(view *view, compositeMode graphicsdriver.CompositeMode, stencilMode stencilMode, screen bool) (mtl.RenderPipelineState, error) { func (s *Shader) RenderPipelineState(view *view, blend graphicsdriver.Blend, stencilMode stencilMode, screen bool) (mtl.RenderPipelineState, error) {
key := shaderRpsKey{ key := shaderRpsKey{
compositeMode: compositeMode, blend: blend,
stencilMode: stencilMode, stencilMode: stencilMode,
screen: screen, screen: screen,
} }
if rps, ok := s.rpss[key]; ok { if rps, ok := s.rpss[key]; ok {
return rps, nil return rps, nil
@ -112,11 +112,13 @@ func (s *Shader) RenderPipelineState(view *view, compositeMode graphicsdriver.Co
rpld.ColorAttachments[0].PixelFormat = pix rpld.ColorAttachments[0].PixelFormat = pix
rpld.ColorAttachments[0].BlendingEnabled = true rpld.ColorAttachments[0].BlendingEnabled = true
src, dst := compositeMode.BlendFactors() rpld.ColorAttachments[0].DestinationAlphaBlendFactor = blendFactorToMetalBlendFactor(blend.BlendFactorDestinationAlpha)
rpld.ColorAttachments[0].DestinationAlphaBlendFactor = blendFactorToMetalBlendFactor(dst) rpld.ColorAttachments[0].DestinationRGBBlendFactor = blendFactorToMetalBlendFactor(blend.BlendFactorDestinationColor)
rpld.ColorAttachments[0].DestinationRGBBlendFactor = blendFactorToMetalBlendFactor(dst) rpld.ColorAttachments[0].SourceAlphaBlendFactor = blendFactorToMetalBlendFactor(blend.BlendFactorSourceAlpha)
rpld.ColorAttachments[0].SourceAlphaBlendFactor = blendFactorToMetalBlendFactor(src) rpld.ColorAttachments[0].SourceRGBBlendFactor = blendFactorToMetalBlendFactor(blend.BlendFactorSourceColor)
rpld.ColorAttachments[0].SourceRGBBlendFactor = blendFactorToMetalBlendFactor(src) rpld.ColorAttachments[0].AlphaBlendOperation = blendOperationToMetalBlendOperation(blend.BlendOperationAlpha)
rpld.ColorAttachments[0].RGBBlendOperation = blendOperationToMetalBlendOperation(blend.BlendOperationColor)
if stencilMode == prepareStencil { if stencilMode == prepareStencil {
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskNone rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskNone
} else { } else {

View File

@ -33,8 +33,14 @@ const (
glZero blendFactor = 0 glZero blendFactor = 0
) )
func convertBlendFactor(op graphicsdriver.BlendFactor) blendFactor { type blendOperation int
switch op {
const (
glAdd blendOperation = 0x104
)
func convertBlendFactor(f graphicsdriver.BlendFactor) blendFactor {
switch f {
case graphicsdriver.BlendFactorZero: case graphicsdriver.BlendFactorZero:
return glZero return glZero
case graphicsdriver.BlendFactorOne: case graphicsdriver.BlendFactorOne:
@ -50,7 +56,16 @@ func convertBlendFactor(op graphicsdriver.BlendFactor) blendFactor {
case graphicsdriver.BlendFactorDestinationColor: case graphicsdriver.BlendFactorDestinationColor:
return glDstColor return glDstColor
default: default:
panic(fmt.Sprintf("opengl: invalid blend factor %d at convertBlendFactor", op)) panic(fmt.Sprintf("opengl: invalid blend factor %d", f))
}
}
func convertBlendOperation(o graphicsdriver.BlendOperation) blendOperation {
switch o {
case graphicsdriver.BlendOperationAdd:
return glAdd
default:
panic(fmt.Sprintf("opengl: invalid blend operation %d", o))
} }
} }
@ -62,7 +77,7 @@ type context struct {
lastRenderbuffer renderbufferNative lastRenderbuffer renderbufferNative
lastViewportWidth int lastViewportWidth int
lastViewportHeight int lastViewportHeight int
lastCompositeMode graphicsdriver.CompositeMode lastBlend graphicsdriver.Blend
maxTextureSize int maxTextureSize int
maxTextureSizeOnce sync.Once maxTextureSizeOnce sync.Once
highp bool highp bool

View File

@ -100,11 +100,11 @@ func (c *context) reset() error {
c.lastFramebuffer = invalidFramebuffer c.lastFramebuffer = invalidFramebuffer
c.lastViewportWidth = 0 c.lastViewportWidth = 0
c.lastViewportHeight = 0 c.lastViewportHeight = 0
c.lastCompositeMode = graphicsdriver.CompositeModeUnknown
gl.Enable(gl.BLEND) gl.Enable(gl.BLEND)
gl.Enable(gl.SCISSOR_TEST) gl.Enable(gl.SCISSOR_TEST)
c.blendFunc(graphicsdriver.CompositeModeSourceOver) // Set the source over blending.
c.blend(graphicsdriver.BlendSourceOver)
f := int32(0) f := int32(0)
gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &f) gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &f)
@ -112,14 +112,21 @@ func (c *context) reset() error {
return nil return nil
} }
func (c *context) blendFunc(mode graphicsdriver.CompositeMode) { func (c *context) blend(blend graphicsdriver.Blend) {
if c.lastCompositeMode == mode { if c.lastBlend == blend {
return return
} }
c.lastCompositeMode = mode c.lastBlend = blend
s, d := mode.BlendFactors() gl.BlendFuncSeparate(
s2, d2 := convertBlendFactor(s), convertBlendFactor(d) uint32(convertBlendFactor(blend.BlendFactorSourceColor)),
gl.BlendFunc(uint32(s2), uint32(d2)) uint32(convertBlendFactor(blend.BlendFactorDestinationColor)),
uint32(convertBlendFactor(blend.BlendFactorSourceAlpha)),
uint32(convertBlendFactor(blend.BlendFactorDestinationAlpha)),
)
gl.BlendEquationSeparate(
uint32(convertBlendOperation(blend.BlendOperationColor)),
uint32(convertBlendOperation(blend.BlendOperationAlpha)),
)
} }
func (c *context) scissor(x, y, width, height int) { func (c *context) scissor(x, y, width, height int) {

View File

@ -93,10 +93,9 @@ func (c *context) reset() error {
c.lastFramebuffer = invalidFramebuffer c.lastFramebuffer = invalidFramebuffer
c.lastViewportWidth = 0 c.lastViewportWidth = 0
c.lastViewportHeight = 0 c.lastViewportHeight = 0
c.lastCompositeMode = graphicsdriver.CompositeModeUnknown
c.ctx.Enable(gles.BLEND) c.ctx.Enable(gles.BLEND)
c.ctx.Enable(gles.SCISSOR_TEST) c.ctx.Enable(gles.SCISSOR_TEST)
c.blendFunc(graphicsdriver.CompositeModeSourceOver) c.blend(graphicsdriver.BlendSourceOver)
f := make([]int32, 1) f := make([]int32, 1)
c.ctx.GetIntegerv(f, gles.FRAMEBUFFER_BINDING) c.ctx.GetIntegerv(f, gles.FRAMEBUFFER_BINDING)
c.screenFramebuffer = framebufferNative(f[0]) c.screenFramebuffer = framebufferNative(f[0])
@ -104,14 +103,20 @@ func (c *context) reset() error {
return nil return nil
} }
func (c *context) blendFunc(mode graphicsdriver.CompositeMode) { func (c *context) blend(blend graphicsdriver.Blend) {
if c.lastCompositeMode == mode { if c.lastBlend == blend {
return return
} }
c.lastCompositeMode = mode c.ctx.BlendFuncSeparate(
s, d := mode.BlendFactors() uint32(convertBlendFactor(blend.BlendFactorSourceColor)),
s2, d2 := convertBlendFactor(s), convertBlendFactor(d) uint32(convertBlendFactor(blend.BlendFactorDestinationColor)),
c.ctx.BlendFunc(uint32(s2), uint32(d2)) uint32(convertBlendFactor(blend.BlendFactorSourceAlpha)),
uint32(convertBlendFactor(blend.BlendFactorDestinationAlpha)),
)
c.ctx.BlendEquationSeparate(
uint32(convertBlendOperation(blend.BlendOperationColor)),
uint32(convertBlendOperation(blend.BlendOperationAlpha)),
)
} }
func (c *context) scissor(x, y, width, height int) { func (c *context) scissor(x, y, width, height int) {

View File

@ -163,7 +163,6 @@ func (c *context) reset() error {
c.lastFramebuffer = framebufferNative(js.Null()) c.lastFramebuffer = framebufferNative(js.Null())
c.lastViewportWidth = 0 c.lastViewportWidth = 0
c.lastViewportHeight = 0 c.lastViewportHeight = 0
c.lastCompositeMode = graphicsdriver.CompositeModeUnknown
if err := c.initGL(); err != nil { if err := c.initGL(); err != nil {
return err return err
@ -175,7 +174,7 @@ func (c *context) reset() error {
gl := c.gl gl := c.gl
gl.enable.Invoke(gles.BLEND) gl.enable.Invoke(gles.BLEND)
gl.enable.Invoke(gles.SCISSOR_TEST) gl.enable.Invoke(gles.SCISSOR_TEST)
c.blendFunc(graphicsdriver.CompositeModeSourceOver) c.blend(graphicsdriver.BlendSourceOver)
f := gl.getParameter.Invoke(gles.FRAMEBUFFER_BINDING) f := gl.getParameter.Invoke(gles.FRAMEBUFFER_BINDING)
c.screenFramebuffer = framebufferNative(f) c.screenFramebuffer = framebufferNative(f)
@ -185,15 +184,22 @@ func (c *context) reset() error {
return nil return nil
} }
func (c *context) blendFunc(mode graphicsdriver.CompositeMode) { func (c *context) blend(blend graphicsdriver.Blend) {
if c.lastCompositeMode == mode { if c.lastBlend == blend {
return return
} }
c.lastCompositeMode = mode c.lastBlend = blend
s, d := mode.BlendFactors()
s2, d2 := convertBlendFactor(s), convertBlendFactor(d)
gl := c.gl gl := c.gl
gl.blendFunc.Invoke(int(s2), int(d2)) gl.blendFuncSeparate.Invoke(
int(convertBlendFactor(blend.BlendFactorSourceColor)),
int(convertBlendFactor(blend.BlendFactorDestinationColor)),
int(convertBlendFactor(blend.BlendFactorSourceAlpha)),
int(convertBlendFactor(blend.BlendFactorDestinationAlpha)),
)
gl.blendEquationSeparate.Invoke(
int(convertBlendOperation(blend.BlendOperationColor)),
int(convertBlendOperation(blend.BlendOperationAlpha)),
)
} }
func (c *context) scissor(x, y, width, height int) { func (c *context) scissor(x, y, width, height int) {

View File

@ -102,7 +102,8 @@ package gl
// typedef void (APIENTRYP GPBINDFRAMEBUFFEREXT)(GLenum target, GLuint framebuffer); // typedef void (APIENTRYP GPBINDFRAMEBUFFEREXT)(GLenum target, GLuint framebuffer);
// typedef void (APIENTRYP GPBINDRENDERBUFFEREXT)(GLenum target, GLuint renderbuffer); // typedef void (APIENTRYP GPBINDRENDERBUFFEREXT)(GLenum target, GLuint renderbuffer);
// typedef void (APIENTRYP GPBINDTEXTURE)(GLenum target, GLuint texture); // typedef void (APIENTRYP GPBINDTEXTURE)(GLenum target, GLuint texture);
// typedef void (APIENTRYP GPBLENDFUNC)(GLenum sfactor, GLenum dfactor); // typedef void (APIENTRYP GPBLENDEQUATIONSEPARATE)(GLenum modeRGB, GLenum modeAlpha);
// typedef void (APIENTRYP GPBLENDFUNCSEPARATE)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
// typedef void (APIENTRYP GPBUFFERDATA)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); // typedef void (APIENTRYP GPBUFFERDATA)(GLenum target, GLsizeiptr size, const void * data, GLenum usage);
// typedef void (APIENTRYP GPBUFFERSUBDATA)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); // typedef void (APIENTRYP GPBUFFERSUBDATA)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data);
// typedef GLenum (APIENTRYP GPCHECKFRAMEBUFFERSTATUSEXT)(GLenum target); // typedef GLenum (APIENTRYP GPCHECKFRAMEBUFFERSTATUSEXT)(GLenum target);
@ -197,8 +198,11 @@ package gl
// static void glowBindTexture(GPBINDTEXTURE fnptr, GLenum target, GLuint texture) { // static void glowBindTexture(GPBINDTEXTURE fnptr, GLenum target, GLuint texture) {
// (*fnptr)(target, texture); // (*fnptr)(target, texture);
// } // }
// static void glowBlendFunc(GPBLENDFUNC fnptr, GLenum sfactor, GLenum dfactor) { // static void glowBlendEquationSeparate(GPBLENDEQUATIONSEPARATE fnptr, GLenum modeRGB, GLenum modeAlpha) {
// (*fnptr)(sfactor, dfactor); // (*fnptr)(modeRGB, modeAlpha);
// }
// static void glowBlendFuncSeparate(GPBLENDFUNCSEPARATE fnptr, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
// (*fnptr)(srcRGB, dstRGB, srcAlpha, dstAlpha);
// } // }
// static void glowBufferData(GPBUFFERDATA fnptr, GLenum target, GLsizeiptr size, const void * data, GLenum usage) { // static void glowBufferData(GPBUFFERDATA fnptr, GLenum target, GLsizeiptr size, const void * data, GLenum usage) {
// (*fnptr)(target, size, data, usage); // (*fnptr)(target, size, data, usage);
@ -431,7 +435,8 @@ var (
gpBindFramebufferEXT C.GPBINDFRAMEBUFFEREXT gpBindFramebufferEXT C.GPBINDFRAMEBUFFEREXT
gpBindRenderbufferEXT C.GPBINDRENDERBUFFEREXT gpBindRenderbufferEXT C.GPBINDRENDERBUFFEREXT
gpBindTexture C.GPBINDTEXTURE gpBindTexture C.GPBINDTEXTURE
gpBlendFunc C.GPBLENDFUNC gpBlendEquationSeparate C.GPBLENDEQUATIONSEPARATE
gpBlendFuncSeparate C.GPBLENDFUNCSEPARATE
gpBufferData C.GPBUFFERDATA gpBufferData C.GPBUFFERDATA
gpBufferSubData C.GPBUFFERSUBDATA gpBufferSubData C.GPBUFFERSUBDATA
gpCheckFramebufferStatusEXT C.GPCHECKFRAMEBUFFERSTATUSEXT gpCheckFramebufferStatusEXT C.GPCHECKFRAMEBUFFERSTATUSEXT
@ -541,8 +546,12 @@ func BindTexture(target uint32, texture uint32) {
C.glowBindTexture(gpBindTexture, (C.GLenum)(target), (C.GLuint)(texture)) C.glowBindTexture(gpBindTexture, (C.GLenum)(target), (C.GLuint)(texture))
} }
func BlendFunc(sfactor uint32, dfactor uint32) { func BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
C.glowBlendFunc(gpBlendFunc, (C.GLenum)(sfactor), (C.GLenum)(dfactor)) C.glowBlendEquationSeparate(gpBlendEquationSeparate, (C.GLenum)(modeRGB), (C.GLenum)(modeAlpha))
}
func BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
C.glowBlendFuncSeparate(gpBlendFuncSeparate, (C.GLenum)(srcRGB), (C.GLenum)(dstRGB), (C.GLenum)(srcAlpha), (C.GLenum)(dstAlpha))
} }
func BufferData(target uint32, size int, data unsafe.Pointer, usage uint32) { func BufferData(target uint32, size int, data unsafe.Pointer, usage uint32) {
@ -861,9 +870,13 @@ func InitWithProcAddrFunc(getProcAddr func(name string) unsafe.Pointer) error {
if gpBindTexture == nil { if gpBindTexture == nil {
return errors.New("gl: glBindTexture is missing") return errors.New("gl: glBindTexture is missing")
} }
gpBlendFunc = (C.GPBLENDFUNC)(getProcAddr("glBlendFunc")) gpBlendEquationSeparate = (C.GPBLENDEQUATIONSEPARATE)(getProcAddr("glBlendEquationSeparate"))
if gpBlendFunc == nil { if gpBlendEquationSeparate == nil {
return errors.New("gl: glBlendFunc is missing") return errors.New("gl: glBlendEquationSeparate is missing")
}
gpBlendFuncSeparate = (C.GPBLENDFUNCSEPARATE)(getProcAddr("glBlendFuncSeparate"))
if gpBlendFuncSeparate == nil {
return errors.New("gl: glBlendFuncSeparate is missing")
} }
gpBufferData = (C.GPBUFFERDATA)(getProcAddr("glBufferData")) gpBufferData = (C.GPBUFFERDATA)(getProcAddr("glBufferData"))
if gpBufferData == nil { if gpBufferData == nil {

View File

@ -20,7 +20,8 @@ var (
gpBindFramebufferEXT uintptr gpBindFramebufferEXT uintptr
gpBindRenderbufferEXT uintptr gpBindRenderbufferEXT uintptr
gpBindTexture uintptr gpBindTexture uintptr
gpBlendFunc uintptr gpBlendEquationSeparate uintptr
gpBlendFuncSeparate uintptr
gpBufferData uintptr gpBufferData uintptr
gpBufferSubData uintptr gpBufferSubData uintptr
gpCheckFramebufferStatusEXT uintptr gpCheckFramebufferStatusEXT uintptr
@ -130,8 +131,12 @@ func BindTexture(target uint32, texture uint32) {
purego.SyscallN(gpBindTexture, uintptr(target), uintptr(texture)) purego.SyscallN(gpBindTexture, uintptr(target), uintptr(texture))
} }
func BlendFunc(sfactor uint32, dfactor uint32) { func BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
purego.SyscallN(gpBlendFunc, uintptr(sfactor), uintptr(dfactor)) purego.SyscallN(gpBlendEquationSeparate, uintptr(modeRGB), uintptr(modeAlpha))
}
func BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
purego.SyscallN(gpBlendFuncSeparate, uintptr(srcRGB), uintptr(dstRGB), uintptr(srcAlpha), uintptr(dstAlpha))
} }
func BufferData(target uint32, size int, data unsafe.Pointer, usage uint32) { func BufferData(target uint32, size int, data unsafe.Pointer, usage uint32) {
@ -450,9 +455,13 @@ func InitWithProcAddrFunc(getProcAddr func(name string) uintptr) error {
if gpBindTexture == 0 { if gpBindTexture == 0 {
return errors.New("gl: glBindTexture is missing") return errors.New("gl: glBindTexture is missing")
} }
gpBlendFunc = getProcAddr("glBlendFunc") gpBlendEquationSeparate = getProcAddr("glBlendEquationSeparate")
if gpBlendFunc == 0 { if gpBlendEquationSeparate == 0 {
return errors.New("gl: glBlendFunc is missing") return errors.New("gl: glBlendEquationSeparate is missing")
}
gpBlendFuncSeparate = getProcAddr("glBlendFuncSeparate")
if gpBlendFuncSeparate == 0 {
return errors.New("gl: glBlendFuncSeparate is missing")
} }
gpBufferData = getProcAddr("glBufferData") gpBufferData = getProcAddr("glBufferData")
if gpBufferData == 0 { if gpBufferData == 0 {

View File

@ -26,7 +26,8 @@ type gl struct {
bindFramebuffer js.Value bindFramebuffer js.Value
bindRenderbuffer js.Value bindRenderbuffer js.Value
bindTexture js.Value bindTexture js.Value
blendFunc js.Value blendEquationSeparate js.Value
blendFuncSeparate js.Value
bufferData js.Value bufferData js.Value
bufferSubData js.Value bufferSubData js.Value
checkFramebufferStatus js.Value checkFramebufferStatus js.Value
@ -103,7 +104,8 @@ func (c *context) newGL(v js.Value) *gl {
bindFramebuffer: v.Get("bindFramebuffer").Call("bind", v), bindFramebuffer: v.Get("bindFramebuffer").Call("bind", v),
bindRenderbuffer: v.Get("bindRenderbuffer").Call("bind", v), bindRenderbuffer: v.Get("bindRenderbuffer").Call("bind", v),
bindTexture: v.Get("bindTexture").Call("bind", v), bindTexture: v.Get("bindTexture").Call("bind", v),
blendFunc: v.Get("blendFunc").Call("bind", v), blendEquationSeparate: v.Get("blendEquationSeparate").Call("bind", v),
blendFuncSeparate: v.Get("blendFuncSeparate").Call("bind", v),
bufferData: v.Get("bufferData").Call("bind", v), bufferData: v.Get("bufferData").Call("bind", v),
bufferSubData: v.Get("bufferSubData").Call("bind", v), bufferSubData: v.Get("bufferSubData").Call("bind", v),
checkFramebufferStatus: v.Get("checkFramebufferStatus").Call("bind", v), checkFramebufferStatus: v.Get("checkFramebufferStatus").Call("bind", v),

View File

@ -78,8 +78,12 @@ func (DefaultContext) BindTexture(target uint32, texture uint32) {
C.glBindTexture(C.GLenum(target), C.GLuint(texture)) C.glBindTexture(C.GLenum(target), C.GLuint(texture))
} }
func (DefaultContext) BlendFunc(sfactor uint32, dfactor uint32) { func (DefaultContext) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
C.glBlendFunc(C.GLenum(sfactor), C.GLenum(dfactor)) C.glBlendEquationSeparate(C.GLenum(modeRGB), C.GLenum(modeAlpha))
}
func (DefaultContext) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
C.glBlendFuncSeparate(C.GLenum(srcRGB), C.GLenum(dstRGB), C.GLenum(srcAlpha), C.GLenum(dstAlpha))
} }
func (DefaultContext) BufferData(target uint32, size int, data []byte, usage uint32) { func (DefaultContext) BufferData(target uint32, size int, data []byte, usage uint32) {

View File

@ -64,8 +64,12 @@ func (g *GomobileContext) BindTexture(target uint32, texture uint32) {
g.ctx.BindTexture(gl.Enum(target), gl.Texture{Value: texture}) g.ctx.BindTexture(gl.Enum(target), gl.Texture{Value: texture})
} }
func (g *GomobileContext) BlendFunc(sfactor uint32, dfactor uint32) { func (g *GomobileContext) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
g.ctx.BlendFunc(gl.Enum(sfactor), gl.Enum(dfactor)) g.ctx.BlendEquationSeparate(gl.Enum(modeRGB), gl.Enum(modeAlpha))
}
func (g *GomobileContext) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
g.ctx.BlendFuncSeparate(gl.Enum(srcRGB), gl.Enum(dstRGB), gl.Enum(srcAlpha), gl.Enum(dstAlpha))
} }
func (g *GomobileContext) BufferData(target uint32, size int, data []byte, usage uint32) { func (g *GomobileContext) BufferData(target uint32, size int, data []byte, usage uint32) {

View File

@ -22,7 +22,8 @@ type Context interface {
BindFramebuffer(target uint32, framebuffer uint32) BindFramebuffer(target uint32, framebuffer uint32)
BindRenderbuffer(target uint32, renderbuffer uint32) BindRenderbuffer(target uint32, renderbuffer uint32)
BindTexture(target uint32, texture uint32) BindTexture(target uint32, texture uint32)
BlendFunc(sfactor uint32, dfactor uint32) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32)
BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32)
BufferData(target uint32, size int, data []byte, usage uint32) BufferData(target uint32, size int, data []byte, usage uint32)
BufferSubData(target uint32, offset int, data []byte) BufferSubData(target uint32, offset int, data []byte)
CheckFramebufferStatus(target uint32) uint32 CheckFramebufferStatus(target uint32) uint32

View File

@ -180,7 +180,7 @@ func (g *Graphics) uniformVariableName(idx int) string {
return name return name
} }
func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error { func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, offsets [graphics.ShaderImageCount - 1][2]float32, shaderID graphicsdriver.ShaderID, indexLen int, indexOffset int, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, uniforms [][]float32, evenOdd bool) error {
if shaderID == graphicsdriver.InvalidShaderID { if shaderID == graphicsdriver.InvalidShaderID {
return fmt.Errorf("opengl: shader ID is invalid") return fmt.Errorf("opengl: shader ID is invalid")
} }
@ -198,7 +198,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
int(dstRegion.Width), int(dstRegion.Width),
int(dstRegion.Height), int(dstRegion.Height),
) )
g.context.blendFunc(mode) g.context.blend(blend)
shader := g.shaders[shaderID] shader := g.shaders[shaderID]
program := shader.p program := shader.p

View File

@ -65,7 +65,7 @@ func (m *Mipmap) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byt
return m.orig.ReadPixels(graphicsDriver, pixels, x, y, width, height) return m.orig.ReadPixels(graphicsDriver, pixels, x, y, width, height)
} }
func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageCount]*Mipmap, vertices []float32, indices []uint16, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, canSkipMipmap bool) { func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageCount]*Mipmap, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, canSkipMipmap bool) {
if len(indices) == 0 { if len(indices) == 0 {
return return
} }
@ -123,7 +123,7 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageCount]*Mipmap, vertices
imgs[i] = src.orig imgs[i] = src.orig
} }
m.orig.DrawTriangles(imgs, vertices, indices, mode, dstRegion, srcRegion, subimageOffsets, shader.shader, uniforms, evenOdd) m.orig.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader.shader, uniforms, evenOdd)
m.disposeMipmaps() m.disposeMipmaps()
} }
@ -192,7 +192,7 @@ func (m *Mipmap) level(level int) *buffered.Image {
Width: float32(w2), Width: float32(w2),
Height: float32(h2), Height: float32(h2),
} }
s.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.CompositeModeCopy, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, shader.shader, nil, false) s.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, shader.shader, nil, false)
m.setImg(level, s) m.setImg(level, s)
return m.imgs[level] return m.imgs[level]

View File

@ -76,7 +76,7 @@ type drawTrianglesHistoryItem struct {
offsets [graphics.ShaderImageCount - 1][2]float32 offsets [graphics.ShaderImageCount - 1][2]float32
vertices []float32 vertices []float32
indices []uint16 indices []uint16
mode graphicsdriver.CompositeMode blend graphicsdriver.Blend
dstRegion graphicsdriver.Region dstRegion graphicsdriver.Region
srcRegion graphicsdriver.Region srcRegion graphicsdriver.Region
shader *Shader shader *Shader
@ -218,7 +218,7 @@ func (i *Image) Extend(width, height int) *Image {
Width: float32(sw), Width: float32(sw),
Height: float32(sh), Height: float32(sh),
} }
newImg.DrawTriangles(srcs, offsets, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, NearestFilterShader, nil, false) newImg.DrawTriangles(srcs, offsets, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, NearestFilterShader, nil, false)
// Overwrite the history as if the image newImg is created only by WritePixels. // Overwrite the history as if the image newImg is created only by WritePixels.
newImg.clearDrawTrianglesHistory() newImg.clearDrawTrianglesHistory()
@ -264,7 +264,7 @@ func clearImage(i *graphicscommand.Image) {
Width: float32(dw), Width: float32(dw),
Height: float32(dh), Height: float32(dh),
} }
i.DrawTriangles(srcs, offsets, vs, is, graphicsdriver.CompositeModeClear, dstRegion, graphicsdriver.Region{}, NearestFilterShader.shader, nil, false) i.DrawTriangles(srcs, offsets, vs, is, graphicsdriver.BlendClear, dstRegion, graphicsdriver.Region{}, NearestFilterShader.shader, nil, false)
} }
// BasePixelsForTesting returns the image's basePixels for testing. // BasePixelsForTesting returns the image's basePixels for testing.
@ -371,7 +371,7 @@ func (i *Image) WritePixels(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.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) { func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) {
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")
} }
@ -395,7 +395,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [g
if srcstale || !needsRestoring() || !i.needsRestoring() { if srcstale || !needsRestoring() || !i.needsRestoring() {
i.makeStale(image.Rect(0, 0, i.width, i.height)) i.makeStale(image.Rect(0, 0, i.width, i.height))
} else { } else {
i.appendDrawTrianglesHistory(srcs, offsets, vertices, indices, mode, dstRegion, srcRegion, shader, uniforms, evenOdd) i.appendDrawTrianglesHistory(srcs, offsets, vertices, indices, blend, dstRegion, srcRegion, shader, uniforms, evenOdd)
} }
var imgs [graphics.ShaderImageCount]*graphicscommand.Image var imgs [graphics.ShaderImageCount]*graphicscommand.Image
@ -405,11 +405,11 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [g
} }
imgs[i] = src.image imgs[i] = src.image
} }
i.image.DrawTriangles(imgs, offsets, vertices, indices, mode, dstRegion, srcRegion, shader.shader, uniforms, evenOdd) i.image.DrawTriangles(imgs, offsets, vertices, indices, blend, dstRegion, srcRegion, shader.shader, uniforms, evenOdd)
} }
// 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.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) { func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageCount]*Image, offsets [graphics.ShaderImageCount - 1][2]float32, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, shader *Shader, uniforms [][]float32, evenOdd bool) {
if i.stale || !i.needsRestoring() { if i.stale || !i.needsRestoring() {
return return
} }
@ -434,7 +434,7 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageCount]*Imag
offsets: offsets, offsets: offsets,
vertices: vs, vertices: vs,
indices: is, indices: is,
mode: mode, blend: blend,
dstRegion: dstRegion, dstRegion: dstRegion,
srcRegion: srcRegion, srcRegion: srcRegion,
shader: shader, shader: shader,
@ -620,7 +620,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
} }
imgs[i] = img.image imgs[i] = img.image
} }
gimg.DrawTriangles(imgs, c.offsets, c.vertices, c.indices, c.mode, c.dstRegion, c.srcRegion, c.shader.shader, c.uniforms, c.evenOdd) gimg.DrawTriangles(imgs, c.offsets, c.vertices, c.indices, c.blend, c.dstRegion, c.srcRegion, c.shader.shader, c.uniforms, c.evenOdd)
} }
if len(i.drawTrianglesHistory) > 0 { if len(i.drawTrianglesHistory) > 0 {

View File

@ -145,7 +145,7 @@ func TestRestoreChain(t *testing.T) {
Width: 1, Width: 1,
Height: 1, Height: 1,
} }
imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
} }
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err) t.Fatal(err)
@ -193,10 +193,10 @@ func TestRestoreChain2(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
imgs[8].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[7]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[7], w, h, 0, 0), is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) imgs[8].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[7]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[7], w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
imgs[9].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[8]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[8], w, h, 0, 0), is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) imgs[9].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[8]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[8], w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
for i := 0; i < 7; i++ { for i := 0; i < 7; i++ {
imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[i], w, h, 0, 0), is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[i], w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
} }
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
@ -242,10 +242,10 @@ func TestRestoreOverrideSource(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
img2.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img1, w, h, 0, 0), is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img2.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img1, w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img2, w, h, 0, 0), is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img2, w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img0.WritePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h) img0.WritePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h)
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img0, w, h, 0, 0), is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img0, w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -331,23 +331,23 @@ func TestRestoreComplexGraph(t *testing.T) {
} }
var offsets [graphics.ShaderImageCount - 1][2]float32 var offsets [graphics.ShaderImageCount - 1][2]float32
vs := quadVertices(img0, w, h, 0, 0) vs := quadVertices(img0, w, h, 0, 0)
img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, offsets, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
vs = quadVertices(img1, w, h, 1, 0) vs = quadVertices(img1, w, h, 1, 0)
img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, offsets, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
vs = quadVertices(img1, w, h, 1, 0) vs = quadVertices(img1, w, h, 1, 0)
img4.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, offsets, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img4.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
vs = quadVertices(img2, w, h, 2, 0) vs = quadVertices(img2, w, h, 2, 0)
img4.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, offsets, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img4.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
vs = quadVertices(img3, w, h, 0, 0) vs = quadVertices(img3, w, h, 0, 0)
img5.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img3}, offsets, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img5.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img3}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
vs = quadVertices(img3, w, h, 0, 0) vs = quadVertices(img3, w, h, 0, 0)
img6.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img3}, offsets, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img6.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img3}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
vs = quadVertices(img4, w, h, 1, 0) vs = quadVertices(img4, w, h, 1, 0)
img6.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img4}, offsets, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img6.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img4}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
vs = quadVertices(img2, w, h, 0, 0) vs = quadVertices(img2, w, h, 0, 0)
img7.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, offsets, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img7.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
vs = quadVertices(img3, w, h, 2, 0) vs = quadVertices(img3, w, h, 2, 0)
img7.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img3}, offsets, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img7.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img3}, offsets, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -445,8 +445,8 @@ func TestRestoreRecursive(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img0, w, h, 1, 0), is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img0, w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img0.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img1, w, h, 1, 0), is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img0.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img1, w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -549,7 +549,7 @@ func TestDrawTrianglesAndWritePixels(t *testing.T) {
Width: 2, Width: 2,
Height: 1, Height: 1,
} }
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img1.WritePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1) img1.WritePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
@ -592,8 +592,8 @@ func TestDispose(t *testing.T) {
Width: 1, Width: 1,
Height: 1, Height: 1,
} }
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img2, 1, 1, 0, 0), is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img2, 1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img0.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img1, 1, 1, 0, 0), is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img0.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(img1, 1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img1.Dispose() img1.Dispose()
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
@ -707,7 +707,7 @@ func TestWritePixelsOnly(t *testing.T) {
Width: 1, Width: 1,
Height: 1, Height: 1,
} }
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img0.WritePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1) img0.WritePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
// BasePixelsForTesting is available without GPU accessing. // BasePixelsForTesting is available without GPU accessing.
@ -766,7 +766,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
// 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.
@ -795,7 +795,7 @@ func TestAllowWritePixelsAfterDrawTriangles(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
dst.WritePixels(make([]byte, 4*w*h), 0, 0, w, h) dst.WritePixels(make([]byte, 4*w*h), 0, 0, w, h)
// WritePixels for a whole image doesn't panic. // WritePixels for a whole image doesn't panic.
} }
@ -819,7 +819,7 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
dst.WritePixels(make([]byte, 4*2*2), 0, 0, 2, 2) dst.WritePixels(make([]byte, 4*2*2), 0, 0, 2, 2)
// WritePixels for a part of image doesn't panic. // WritePixels for a part of image doesn't panic.
@ -922,7 +922,7 @@ func TestMutateSlices(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
for i := range vs { for i := range vs {
vs[i] = 0 vs[i] = 0
} }
@ -1119,7 +1119,7 @@ func TestDrawTrianglesAndReadPixels(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, w, h); err != nil { if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, w, h); err != nil {

View File

@ -50,7 +50,7 @@ func clearImage(img *restorable.Image, w, h int) {
Width: float32(w), Width: float32(w),
Height: float32(h), Height: float32(h),
} }
img.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{emptyImage}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.CompositeModeClear, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false) img.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{emptyImage}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendClear, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
} }
func TestShader(t *testing.T) { func TestShader(t *testing.T) {
@ -64,7 +64,7 @@ func TestShader(t *testing.T) {
Width: 1, Width: 1,
Height: 1, Height: 1,
} }
img.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(nil, 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, s, nil, false) img.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(nil, 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err) t.Fatal(err)
@ -99,7 +99,7 @@ func TestShaderChain(t *testing.T) {
Width: 1, Width: 1,
Height: 1, Height: 1,
} }
imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[i], 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, s, nil, false) imgs[i+1].DrawTriangles([graphics.ShaderImageCount]*restorable.Image{imgs[i]}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(imgs[i], 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
} }
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
@ -137,7 +137,7 @@ func TestShaderMultipleSources(t *testing.T) {
Width: 1, Width: 1,
Height: 1, Height: 1,
} }
dst.DrawTriangles(srcs, offsets, quadVertices(srcs[0], 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, s, nil, false) dst.DrawTriangles(srcs, offsets, quadVertices(srcs[0], 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
// Clear one of the sources after DrawTriangles. dst should not be affected. // Clear one of the sources after DrawTriangles. dst should not be affected.
clearImage(srcs[0], 1, 1) clearImage(srcs[0], 1, 1)
@ -178,7 +178,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
Width: 1, Width: 1,
Height: 1, Height: 1,
} }
dst.DrawTriangles(srcs, offsets, quadVertices(srcs[0], 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, s, nil, false) dst.DrawTriangles(srcs, offsets, quadVertices(srcs[0], 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
// Clear one of the sources after DrawTriangles. dst should not be affected. // Clear one of the sources after DrawTriangles. dst should not be affected.
clearImage(srcs[0], 3, 1) clearImage(srcs[0], 3, 1)
@ -208,7 +208,7 @@ func TestShaderDispose(t *testing.T) {
Width: 1, Width: 1,
Height: 1, Height: 1,
} }
img.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(nil, 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, s, nil, false) img.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(nil, 1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, s, nil, false)
// 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

@ -56,7 +56,7 @@ func (i *Image) MarkDisposed() {
i.dotsCache = nil i.dotsCache = nil
} }
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, mode graphicsdriver.CompositeMode, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, canSkipMipmap bool) { func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, canSkipMipmap bool) {
i.flushCacheIfNeeded() i.flushCacheIfNeeded()
var srcMipmaps [graphics.ShaderImageCount]*mipmap.Mipmap var srcMipmaps [graphics.ShaderImageCount]*mipmap.Mipmap
@ -68,7 +68,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
srcMipmaps[i] = src.mipmap srcMipmaps[i] = src.mipmap
} }
i.mipmap.DrawTriangles(srcMipmaps, vertices, indices, mode, dstRegion, srcRegion, subimageOffsets, shader.shader, uniforms, evenOdd, canSkipMipmap) i.mipmap.DrawTriangles(srcMipmaps, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader.shader, uniforms, evenOdd, canSkipMipmap)
} }
func (i *Image) WritePixels(pix []byte, x, y, width, height int) { func (i *Image) WritePixels(pix []byte, x, y, width, height int) {
@ -190,7 +190,7 @@ func (i *Image) flushCacheIfNeeded() {
Width: float32(i.width), Width: float32(i.width),
Height: float32(i.height), Height: float32(i.height),
} }
i.mipmap.DrawTriangles(srcs, vs, is, graphicsdriver.CompositeModeCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader.shader, nil, false, true) i.mipmap.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader.shader, nil, false, true)
} }
func DumpImages(dir string) (string, error) { func DumpImages(dir string) (string, error) {
@ -230,5 +230,5 @@ func (i *Image) Fill(r, g, b, a float32, x, y, width, height int) {
srcs := [graphics.ShaderImageCount]*Image{emptyImage} srcs := [graphics.ShaderImageCount]*Image{emptyImage}
i.DrawTriangles(srcs, vs, is, graphicsdriver.CompositeModeCopy, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader, nil, false, true) i.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, NearestFilterShader, nil, false, true)
} }