CPU patterns fallback

This commit is contained in:
Dmitry Sergeev
2026-09-18 00:41:38 +03:00
parent b85293b5d2
commit 723bef6342
8 changed files with 616 additions and 98 deletions
+74 -43
View File
@@ -1,27 +1,37 @@
package generator
import (
"errors"
"strings"
"testing"
)
func fillFrame(value byte) FrameRenderer {
return func(dst []byte, _, _, _ int) error {
for i := range dst {
dst[i] = value
}
return nil
}
}
func TestNewCPUGeneratorValidation(t *testing.T) {
sampler := func(_, _, _, _, _ int) YCbCr10 { return YCbCr10{} }
renderer := fillFrame(0)
tests := []struct {
name string
width uint
height uint
sampler PixelSampler
renderer FrameRenderer
wantErrSub string
}{
{name: "zero width", height: 1, sampler: sampler, wantErrSub: "greater than 0"},
{name: "zero height", width: 6, sampler: sampler, wantErrSub: "greater than 0"},
{name: "unaligned width", width: 7, height: 1, sampler: sampler, wantErrSub: "divisible by 6"},
{name: "nil sampler", width: 6, height: 1, wantErrSub: "sampler is nil"},
{name: "zero width", height: 1, renderer: renderer, wantErrSub: "greater than zero"},
{name: "zero height", width: 6, renderer: renderer, wantErrSub: "greater than zero"},
{name: "unaligned width", width: 7, height: 1, renderer: renderer, wantErrSub: "divisible by 6"},
{name: "nil renderer", width: 6, height: 1, wantErrSub: "renderer is nil"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
_, err := NewCPUGenerator(tc.width, tc.height, tc.sampler)
_, err := NewCPUGenerator(tc.width, tc.height, tc.renderer, nil)
if err == nil || !strings.Contains(err.Error(), tc.wantErrSub) {
t.Fatalf("error = %v, want substring %q", err, tc.wantErrSub)
}
@@ -29,63 +39,84 @@ func TestNewCPUGeneratorValidation(t *testing.T) {
}
}
func TestCPUGeneratorGenerateFrame(t *testing.T) {
const width, height = 12, 2
const frameIndex = 7
g, err := NewCPUGenerator(width, height, func(x, y, _, _ int, tick int) YCbCr10 {
return YCbCr10{
Y: uint32(100 + x + 10*y + tick),
Cb: uint32(200 + x + 10*y + tick),
Cr: uint32(300 + x + 10*y + tick),
func TestCPUGeneratorCopiesBaseAndRestoresBeforePatch(t *testing.T) {
patch := func(dst []byte, _, _, frameIndex int) error {
switch frameIndex {
case 1:
dst[0] = 0x22
case 2:
dst[16] = 0x33
}
})
return nil
}
g, err := NewCPUGenerator(12, 1, fillFrame(0x11), patch)
if err != nil {
t.Fatalf("NewCPUGenerator: %v", err)
}
frame := make([]byte, width*height*8/3)
if err := g.GenerateFrame(frame, frameIndex); err != nil {
t.Fatalf("GenerateFrame: %v", err)
frame := make([]byte, 32)
if err := g.GenerateFrame(frame, 1); err != nil {
t.Fatalf("GenerateFrame(1): %v", err)
}
if frame[0] != 0x22 {
t.Fatalf("frame 1 patch byte = %#x, want 0x22", frame[0])
}
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
gotY, gotCb, gotCr := sampleV210(frame, width, x, y)
chromaX := x - x%2
wantY := uint32(100 + x + 10*y + frameIndex)
wantCb := uint32(200 + chromaX + 10*y + frameIndex)
wantCr := uint32(300 + chromaX + 10*y + frameIndex)
if gotY != wantY || gotCb != wantCb || gotCr != wantCr {
t.Fatalf("pixel (%d,%d): got %d/%d/%d, want %d/%d/%d",
x, y, gotY, gotCb, gotCr, wantY, wantCb, wantCr)
}
if err := g.GenerateFrame(frame, 2); err != nil {
t.Fatalf("GenerateFrame(2): %v", err)
}
if frame[0] != 0x11 {
t.Fatalf("old patch byte = %#x, want restored base 0x11", frame[0])
}
if frame[16] != 0x33 {
t.Fatalf("frame 2 patch byte = %#x, want 0x33", frame[16])
}
}
func TestPackV210Block(t *testing.T) {
var pixels [6]YCbCr10
for i := range pixels {
pixels[i] = YCbCr10{
Y: uint32(100 + i),
Cb: uint32(200 + i),
Cr: uint32(300 + i),
}
}
frame := make([]byte, 16)
packV210Block(frame, pixels)
for x := range pixels {
y, cb, cr := sampleV210(frame, 6, x, 0)
chromaX := x - x%2
if y != pixels[x].Y || cb != pixels[chromaX].Cb || cr != pixels[chromaX].Cr {
t.Fatalf("pixel %d: got %d/%d/%d, want %d/%d/%d",
x, y, cb, cr, pixels[x].Y, pixels[chromaX].Cb, pixels[chromaX].Cr)
}
}
}
func TestCPUGeneratorMasksComponentsToTenBits(t *testing.T) {
g, err := NewCPUGenerator(6, 1, func(_, _, _, _, _ int) YCbCr10 {
return YCbCr10{Y: 0xC01, Cb: 0xC02, Cr: 0xC03}
})
if err != nil {
t.Fatalf("NewCPUGenerator: %v", err)
}
func TestPackV210BlockMasksComponents(t *testing.T) {
pixel := YCbCr10{Y: 0xC01, Cb: 0xC02, Cr: 0xC03}
frame := make([]byte, 16)
if err := g.GenerateFrame(frame, 0); err != nil {
t.Fatalf("GenerateFrame: %v", err)
}
packV210Block(frame, [6]YCbCr10{pixel, pixel, pixel, pixel, pixel, pixel})
y, cb, cr := sampleV210(frame, 6, 0, 0)
if y != 1 || cb != 2 || cr != 3 {
t.Fatalf("masked components = %d/%d/%d, want 1/2/3", y, cb, cr)
}
}
func TestCPUGeneratorRejectsSmallDestination(t *testing.T) {
g, err := NewCPUGenerator(6, 1, func(_, _, _, _, _ int) YCbCr10 { return YCbCr10{} })
func TestCPUGeneratorErrors(t *testing.T) {
wantErr := errors.New("patch failed")
g, err := NewCPUGenerator(6, 1, fillFrame(0), func([]byte, int, int, int) error {
return wantErr
})
if err != nil {
t.Fatalf("NewCPUGenerator: %v", err)
}
if err := g.GenerateFrame(make([]byte, 15), 0); err == nil || !strings.Contains(err.Error(), "too small") {
t.Fatalf("error = %v, want destination size error", err)
t.Fatalf("small destination error = %v", err)
}
if err := g.GenerateFrame(make([]byte, 16), 4); !errors.Is(err, wantErr) {
t.Fatalf("patch error = %v, want wrapped %v", err, wantErr)
}
}