Compare commits

...

1 Commits

Author SHA1 Message Date
Dmitry Sergeev 1e804897c6 alpha perfomance fix 2026-09-18 10:25:35 +03:00
5 changed files with 81 additions and 12 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ import (
const ( const (
Name = "MXL pattern generator" Name = "MXL pattern generator"
Version = "0.1.0" Version = "0.2.0"
) )
type Config struct { type Config struct {
+12 -2
View File
@@ -9,6 +9,7 @@ type V210AGenerator struct {
width int width int
height int height int
fillSize int fillSize int
fillBase []byte
alphaBase []byte alphaBase []byte
} }
@@ -17,6 +18,7 @@ var _ FrameGenerator = (*V210AGenerator)(nil)
func NewV210AGenerator( func NewV210AGenerator(
fill FrameGenerator, fill FrameGenerator,
width, height uint, width, height uint,
fillDynamic bool,
) (*V210AGenerator, error) { ) (*V210AGenerator, error) {
if fill == nil { if fill == nil {
return nil, fmt.Errorf("v210a: fill generator is nil") return nil, fmt.Errorf("v210a: fill generator is nil")
@@ -38,6 +40,12 @@ func NewV210AGenerator(
if err := fillAlphaPlane(g.alphaBase, g.width, g.height, alphaOpaque); err != nil { if err := fillAlphaPlane(g.alphaBase, g.width, g.height, alphaOpaque); err != nil {
return nil, fmt.Errorf("v210a: initialize alpha plane: %w", err) return nil, fmt.Errorf("v210a: initialize alpha plane: %w", err)
} }
if !fillDynamic {
g.fillBase = make([]byte, g.fillSize)
if err := fill.GenerateFrame(g.fillBase, 0); err != nil {
return nil, fmt.Errorf("v210a: initialize static fill: %w", err)
}
}
return g, nil return g, nil
} }
@@ -47,8 +55,10 @@ func (g *V210AGenerator) GenerateFrame(dst []byte, frameIndex int) error {
return fmt.Errorf("v210a: destination is too small: got %d bytes, need %d", len(dst), need) return fmt.Errorf("v210a: destination is too small: got %d bytes, need %d", len(dst), need)
} }
if err := g.fill.GenerateFrame(dst[:g.fillSize], frameIndex); err != nil { if g.fillBase != nil {
return fmt.Errorf("v210a: generate fill frame %d: %w", frameIndex, err) copy(dst[:g.fillSize], g.fillBase)
} else if err := g.fill.GenerateFrame(dst[:g.fillSize], frameIndex); err != nil {
return fmt.Errorf("v210a: generate dynamic fill frame %d: %w", frameIndex, err)
} }
alpha := dst[g.fillSize:need] alpha := dst[g.fillSize:need]
copy(alpha, g.alphaBase) copy(alpha, g.alphaBase)
+60 -5
View File
@@ -32,7 +32,7 @@ func (g *fakeFrameGenerator) Close() error {
func TestV210AGeneratorLayoutAndAlpha(t *testing.T) { func TestV210AGeneratorLayoutAndAlpha(t *testing.T) {
const width, height = 304, 200 const width, height = 304, 200
fill := &fakeFrameGenerator{} fill := &fakeFrameGenerator{}
g, err := NewV210AGenerator(fill, width, height) g, err := NewV210AGenerator(fill, width, height, true)
if err != nil { if err != nil {
t.Fatalf("NewV210AGenerator: %v", err) t.Fatalf("NewV210AGenerator: %v", err)
} }
@@ -58,7 +58,7 @@ func TestV210AGeneratorLayoutAndAlpha(t *testing.T) {
func TestV210AGeneratorRestoresAlphaBase(t *testing.T) { func TestV210AGeneratorRestoresAlphaBase(t *testing.T) {
const width, height = 304, 200 const width, height = 304, 200
g, err := NewV210AGenerator(&fakeFrameGenerator{}, width, height) g, err := NewV210AGenerator(&fakeFrameGenerator{}, width, height, true)
if err != nil { if err != nil {
t.Fatalf("NewV210AGenerator: %v", err) t.Fatalf("NewV210AGenerator: %v", err)
} }
@@ -82,13 +82,68 @@ func TestV210AGeneratorRestoresAlphaBase(t *testing.T) {
} }
} }
func TestV210AGeneratorCachesStaticFill(t *testing.T) {
const width, height = 100, 20
fill := &fakeFrameGenerator{}
g, err := NewV210AGenerator(fill, width, height, false)
if err != nil {
t.Fatalf("NewV210AGenerator: %v", err)
}
if fill.calls != 1 {
t.Fatalf("fill calls after construction = %d, want 1", fill.calls)
}
dst := make([]byte, V210AFrameSize(width, height))
for _, frameIndex := range []int{7, 19} {
if err := g.GenerateFrame(dst, frameIndex); err != nil {
t.Fatalf("GenerateFrame(%d): %v", frameIndex, err)
}
}
if fill.calls != 1 {
t.Errorf("fill calls after two frames = %d, want 1", fill.calls)
}
for i, b := range dst[:V210FrameSize(width, height)] {
if b != 0 {
t.Fatalf("cached fill byte %d = %#x, want frame-zero value 0", i, b)
}
}
}
func TestV210AGeneratorRegeneratesDynamicFill(t *testing.T) {
const width, height = 100, 20
fill := &fakeFrameGenerator{}
g, err := NewV210AGenerator(fill, width, height, true)
if err != nil {
t.Fatalf("NewV210AGenerator: %v", err)
}
if fill.calls != 0 {
t.Fatalf("fill calls after construction = %d, want 0", fill.calls)
}
dst := make([]byte, V210AFrameSize(width, height))
for _, frameIndex := range []int{7, 19} {
if err := g.GenerateFrame(dst, frameIndex); err != nil {
t.Fatalf("GenerateFrame(%d): %v", frameIndex, err)
}
}
if fill.calls != 2 {
t.Errorf("fill calls after two frames = %d, want 2", fill.calls)
}
if got := dst[0]; got != 19 {
t.Errorf("dynamic fill byte = %#x, want frame-index value 0x13", got)
}
}
func TestV210AGeneratorErrors(t *testing.T) { func TestV210AGeneratorErrors(t *testing.T) {
if _, err := NewV210AGenerator(nil, 1920, 1080); err == nil || !strings.Contains(err.Error(), "nil") { if _, err := NewV210AGenerator(nil, 1920, 1080, false); err == nil || !strings.Contains(err.Error(), "nil") {
t.Fatalf("nil fill error = %v", err) t.Fatalf("nil fill error = %v", err)
} }
fillErr := errors.New("fill failed") fillErr := errors.New("fill failed")
g, err := NewV210AGenerator(&fakeFrameGenerator{generateErr: fillErr}, 100, 20) if _, err := NewV210AGenerator(&fakeFrameGenerator{generateErr: fillErr}, 100, 20, false); !errors.Is(err, fillErr) {
t.Fatalf("static fill initialization error = %v, want wrapped %v", err, fillErr)
}
g, err := NewV210AGenerator(&fakeFrameGenerator{generateErr: fillErr}, 100, 20, true)
if err != nil { if err != nil {
t.Fatalf("NewV210AGenerator: %v", err) t.Fatalf("NewV210AGenerator: %v", err)
} }
@@ -103,7 +158,7 @@ func TestV210AGeneratorErrors(t *testing.T) {
func TestV210AGeneratorClosesFill(t *testing.T) { func TestV210AGeneratorClosesFill(t *testing.T) {
closeErr := errors.New("close failed") closeErr := errors.New("close failed")
fill := &fakeFrameGenerator{closeErr: closeErr} fill := &fakeFrameGenerator{closeErr: closeErr}
g, err := NewV210AGenerator(fill, 100, 20) g, err := NewV210AGenerator(fill, 100, 20, false)
if err != nil { if err != nil {
t.Fatalf("NewV210AGenerator: %v", err) t.Fatalf("NewV210AGenerator: %v", err)
} }
+7 -3
View File
@@ -21,19 +21,23 @@ func newFrameGenerator(cfg Config, pattern pattern) (generator.FrameGenerator, B
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
gen, err := wrapAlphaGenerator(cfg, fill) gen, err := wrapAlphaGenerator(cfg, fill, pattern.dynamic)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
return gen, backend, nil return gen, backend, nil
} }
func wrapAlphaGenerator(cfg Config, fill generator.FrameGenerator) (generator.FrameGenerator, error) { func wrapAlphaGenerator(
cfg Config,
fill generator.FrameGenerator,
fillDynamic bool,
) (generator.FrameGenerator, error) {
if !cfg.HasAlpha() { if !cfg.HasAlpha() {
return fill, nil return fill, nil
} }
gen, err := generator.NewV210AGenerator(fill, cfg.Width(), cfg.Height()) gen, err := generator.NewV210AGenerator(fill, cfg.Width(), cfg.Height(), fillDynamic)
if err != nil { if err != nil {
closeErr := fill.Close() closeErr := fill.Close()
return nil, errors.Join( return nil, errors.Join(
+1 -1
View File
@@ -145,7 +145,7 @@ func TestWrapAlphaGenerator(t *testing.T) {
} }
fill := &stubFrameGenerator{} fill := &stubFrameGenerator{}
gen, err := wrapAlphaGenerator(cfg, fill) gen, err := wrapAlphaGenerator(cfg, fill, false)
if err != nil { if err != nil {
t.Fatalf("wrapAlphaGenerator: %v", err) t.Fatalf("wrapAlphaGenerator: %v", err)
} }