init: 自 zomaintain/backend/rdplib 平移独立成库; module path 从上游 github.com/nakagami/grdp 改为 git.zeroonesoft.cn/golib/rdplib
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
package rdpgfx
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestParallelRowsCoverage verifies parallelRows invokes fn over contiguous,
|
||||
// non-overlapping chunks that together cover [0,h) exactly once, for both the
|
||||
// serial (small) and parallel (large) regimes.
|
||||
func TestParallelRowsCoverage(t *testing.T) {
|
||||
for _, dim := range []struct{ w, h int }{
|
||||
{16, 16}, // tiny → serial
|
||||
{64, 64}, // small → serial
|
||||
{256, 256}, // at threshold → parallel
|
||||
{512, 300}, // parallel, height not divisible by worker count
|
||||
{1920, 1080}, // typical full-screen → parallel
|
||||
{8, 1}, // single row
|
||||
{8, 0}, // zero rows
|
||||
} {
|
||||
counts := make([]int32, dim.h)
|
||||
parallelRows(dim.w, dim.h, func(y0, y1 int) {
|
||||
for r := y0; r < y1; r++ {
|
||||
counts[r]++
|
||||
}
|
||||
})
|
||||
for r := 0; r < dim.h; r++ {
|
||||
if counts[r] != 1 {
|
||||
t.Fatalf("dim %dx%d: row %d visited %d times, want 1", dim.w, dim.h, r, counts[r])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// serialI420ToBGRA is an independent reference implementation used to verify the
|
||||
// parallelised i420ToBGRA output is identical regardless of how rows are split.
|
||||
func serialI420ToBGRA(src *H264FrameI420) []byte {
|
||||
w, h := src.Width, src.Height
|
||||
out := make([]byte, w*h*4)
|
||||
for row := 0; row < h; row++ {
|
||||
yOff := row * src.YStride
|
||||
uOff := (row >> 1) * src.UStride
|
||||
vOff := (row >> 1) * src.VStride
|
||||
for col := 0; col < w; col++ {
|
||||
uv := col >> 1
|
||||
o := (row*w + col) * 4
|
||||
u := int(src.U[uOff+uv]) - 128
|
||||
v := int(src.V[vOff+uv]) - 128
|
||||
if src.FullRange {
|
||||
y := int(src.Y[yOff+col])
|
||||
out[o] = clampByte((256*y + 475*u + 128) >> 8)
|
||||
out[o+1] = clampByte((256*y - 48*u - 120*v + 128) >> 8)
|
||||
out[o+2] = clampByte((256*y + 403*v + 128) >> 8)
|
||||
} else {
|
||||
c := int(src.Y[yOff+col]) - 16
|
||||
out[o] = clampByte((298*c + 541*u + 128) >> 8)
|
||||
out[o+1] = clampByte((298*c - 55*u - 136*v + 128) >> 8)
|
||||
out[o+2] = clampByte((298*c + 459*v + 128) >> 8)
|
||||
}
|
||||
out[o+3] = 255
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func makeI420(w, h int, fullRange bool, rng *rand.Rand) *H264FrameI420 {
|
||||
cw, ch := (w+1)/2, (h+1)/2
|
||||
f := &H264FrameI420{
|
||||
Y: make([]byte, w*h), U: make([]byte, cw*ch), V: make([]byte, cw*ch),
|
||||
YStride: w, UStride: cw, VStride: cw,
|
||||
Width: w, Height: h, FullRange: fullRange,
|
||||
}
|
||||
for i := range f.Y {
|
||||
f.Y[i] = byte(rng.Intn(256))
|
||||
}
|
||||
for i := range f.U {
|
||||
f.U[i] = byte(rng.Intn(256))
|
||||
f.V[i] = byte(rng.Intn(256))
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// TestI420ToBGRAParallelMatchesSerial checks that the parallelised conversion
|
||||
// produces byte-identical output to the reference for both small (serial) and
|
||||
// large (parallel) frames, in full- and limited-range modes.
|
||||
func TestI420ToBGRAParallelMatchesSerial(t *testing.T) {
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
for _, dim := range []struct{ w, h int }{
|
||||
{64, 64}, // serial path
|
||||
{640, 480}, // parallel path
|
||||
{512, 511}, // parallel, odd height
|
||||
} {
|
||||
for _, fr := range []bool{false, true} {
|
||||
f := makeI420(dim.w, dim.h, fr, rng)
|
||||
got, pooled := i420ToBGRA(f)
|
||||
if got == nil {
|
||||
t.Fatalf("i420ToBGRA returned nil for %dx%d", dim.w, dim.h)
|
||||
}
|
||||
want := serialI420ToBGRA(f)
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("%dx%d fr=%v: len %d != %d", dim.w, dim.h, fr, len(got), len(want))
|
||||
}
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Fatalf("%dx%d fr=%v: byte %d = %d, want %d", dim.w, dim.h, fr, i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
if pooled {
|
||||
releaseBitmapBuf(got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkI420ToBGRA1080p(b *testing.B) {
|
||||
rng := rand.New(rand.NewSource(2))
|
||||
f := makeI420(1920, 1080, false, rng)
|
||||
b.SetBytes(int64(1920 * 1080 * 4))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
out, pooled := i420ToBGRA(f)
|
||||
if pooled {
|
||||
releaseBitmapBuf(out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// serialCombineAVC444v2BGRA mirrors combineAVC444v2BGRA's exact indexing in a
|
||||
// single serial loop, used to confirm the parallelised version is byte-identical
|
||||
// regardless of how the rows are split across workers.
|
||||
func serialCombineAVC444v2BGRA(yPlane []byte, yStride int, cachedU, cachedV []byte, uvStride int,
|
||||
i420aux *H264FrameI420, fullRange bool, w, h int) []byte {
|
||||
out := make([]byte, w*h*4)
|
||||
halfW := w / 2
|
||||
quarterW := w / 4
|
||||
for row := 0; row < h; row++ {
|
||||
yRowOff := row * yStride
|
||||
uvRow := row >> 1
|
||||
uvRowOff := uvRow * uvStride
|
||||
auxYRowOff := row * i420aux.YStride
|
||||
auxURowOff := uvRow * i420aux.UStride
|
||||
auxVRowOff := uvRow * i420aux.VStride
|
||||
outIdx := row * w * 4
|
||||
for col := 0; col < w; col++ {
|
||||
Y := yPlane[yRowOff+col]
|
||||
var Cb, Cr byte
|
||||
if col&1 == 1 {
|
||||
k := col >> 1
|
||||
Cb = i420aux.Y[auxYRowOff+k]
|
||||
Cr = i420aux.Y[auxYRowOff+halfW+k]
|
||||
} else if row&1 == 0 {
|
||||
k := col >> 1
|
||||
Cb = cachedU[uvRowOff+k]
|
||||
Cr = cachedV[uvRowOff+k]
|
||||
} else {
|
||||
k := col >> 2
|
||||
if col&2 == 0 {
|
||||
Cb = i420aux.U[auxURowOff+k]
|
||||
Cr = i420aux.U[auxURowOff+quarterW+k]
|
||||
} else {
|
||||
Cb = i420aux.V[auxVRowOff+k]
|
||||
Cr = i420aux.V[auxVRowOff+quarterW+k]
|
||||
}
|
||||
}
|
||||
u := int(Cb) - 128
|
||||
v := int(Cr) - 128
|
||||
if fullRange {
|
||||
y := int(Y)
|
||||
out[outIdx] = clampByte((256*y + 475*u + 128) >> 8)
|
||||
out[outIdx+1] = clampByte((256*y - 48*u - 120*v + 128) >> 8)
|
||||
out[outIdx+2] = clampByte((256*y + 403*v + 128) >> 8)
|
||||
} else {
|
||||
c := int(Y) - 16
|
||||
out[outIdx] = clampByte((298*c + 541*u + 128) >> 8)
|
||||
out[outIdx+1] = clampByte((298*c - 55*u - 136*v + 128) >> 8)
|
||||
out[outIdx+2] = clampByte((298*c + 459*v + 128) >> 8)
|
||||
}
|
||||
out[outIdx+3] = 255
|
||||
outIdx += 4
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestCombineAVC444v2BGRAParallelMatchesSerial(t *testing.T) {
|
||||
rng := rand.New(rand.NewSource(3))
|
||||
for _, dim := range []struct{ w, h int }{
|
||||
{64, 64}, // serial path
|
||||
{640, 480}, // parallel path
|
||||
{512, 510}, // parallel, even dims
|
||||
} {
|
||||
w, h := dim.w, dim.h
|
||||
uvStride := (w + 1) / 2
|
||||
uvH := (h + 1) / 2
|
||||
yPlane := make([]byte, w*h)
|
||||
cachedU := make([]byte, uvStride*uvH)
|
||||
cachedV := make([]byte, uvStride*uvH)
|
||||
for i := range yPlane {
|
||||
yPlane[i] = byte(rng.Intn(256))
|
||||
}
|
||||
for i := range cachedU {
|
||||
cachedU[i] = byte(rng.Intn(256))
|
||||
cachedV[i] = byte(rng.Intn(256))
|
||||
}
|
||||
aux := &H264FrameI420{
|
||||
Y: make([]byte, w*h), U: make([]byte, uvStride*uvH), V: make([]byte, uvStride*uvH),
|
||||
YStride: w, UStride: uvStride, VStride: uvStride, Width: w, Height: h,
|
||||
}
|
||||
for i := range aux.Y {
|
||||
aux.Y[i] = byte(rng.Intn(256))
|
||||
}
|
||||
for i := range aux.U {
|
||||
aux.U[i] = byte(rng.Intn(256))
|
||||
aux.V[i] = byte(rng.Intn(256))
|
||||
}
|
||||
for _, fr := range []bool{false, true} {
|
||||
got, pooled := combineAVC444v2BGRA(yPlane, w, cachedU, cachedV, uvStride, aux, fr, w, h, nil)
|
||||
want := serialCombineAVC444v2BGRA(yPlane, w, cachedU, cachedV, uvStride, aux, fr, w, h)
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Fatalf("%dx%d fr=%v: byte %d = %d, want %d", w, h, fr, i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
if pooled {
|
||||
releaseBitmapBuf(got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user