Remove excessive
This commit is contained in:
@@ -1,159 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"log"
|
|
||||||
flowdef "mxl-pattern-generator/internal/flow-def"
|
|
||||||
"mxl-pattern-generator/internal/generator"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/qvest-digital/go-mxl/mxl"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var width, height uint = 1920, 1080
|
|
||||||
var fps_num, fps_den uint = 25, 1
|
|
||||||
flowUUID := "5fbec3b1-1b0f-417d-9059-8b94a47197ed"
|
|
||||||
log.Printf("Go Pattern Gen")
|
|
||||||
log.Printf("Video: %dx%dp%d/%d", width, height, fps_num, fps_den)
|
|
||||||
log.Printf("UUID: %s", flowUUID)
|
|
||||||
domain := "/dev/shm/mxl"
|
|
||||||
|
|
||||||
inst, err := mxl.NewInstance(domain, "")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("MXL Init Failed: %v", err)
|
|
||||||
}
|
|
||||||
defer inst.Close()
|
|
||||||
|
|
||||||
flowDef, err := flowdef.NewFlowDefJSON(
|
|
||||||
flowdef.TYPE_VIDEO,
|
|
||||||
flowUUID,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
fps_num,
|
|
||||||
fps_den,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not create Flow Definition: %v", err)
|
|
||||||
}
|
|
||||||
writer, isCreated, err := inst.NewWriter(flowDef)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to create MXL writer: %v", err)
|
|
||||||
}
|
|
||||||
if !isCreated {
|
|
||||||
log.Printf("reusing existing flow: %s, domain: %s", flowUUID, domain)
|
|
||||||
}
|
|
||||||
defer writer.Close()
|
|
||||||
|
|
||||||
var gen *generator.OpenCLGenerator
|
|
||||||
gen, err = generator.NewOpenCLGenerator(width, height, "/home/itten/codeproj/go-mxl-pattern-gen/kernels/v210_pack.cl")
|
|
||||||
gen, err = generator.NewOpenCLGenerator(width, height, "/home/itten/codeproj/go-mxl-pattern-gen/kernels/v210_pack.cl")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("OpenCL init failed: %v", err)
|
|
||||||
}
|
|
||||||
defer gen.Close()
|
|
||||||
gen.SetText(generator.TextConfig{
|
|
||||||
Str: "MXL PATTERN GEN",
|
|
||||||
X: 0, Y: 0, Scale: 8,
|
|
||||||
FG: [3]uint32{940, 512, 512},
|
|
||||||
BG: [3]uint32{64, 512, 512},
|
|
||||||
Boxed: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
flowCfg := writer.Config()
|
|
||||||
rate := flowCfg.Common.GrainRate
|
|
||||||
idx := mxl.CurrentIndex(rate)
|
|
||||||
log.Printf("writing flow grainRate=%d/%d starting at idx=%d", rate.Num, rate.Den, idx)
|
|
||||||
|
|
||||||
stop := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
|
||||||
|
|
||||||
var written int64
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-stop:
|
|
||||||
log.Printf("stopping after %d grains", written)
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
gwa, err := writer.OpenGrain(idx)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("OpenGrain(%d): %v", idx, err)
|
|
||||||
}
|
|
||||||
// fillGrainRamp(gwa.Payload, idx)
|
|
||||||
// smpteBars(gwa.Payload)
|
|
||||||
gen.GenerateFrame(gwa.Payload, int(idx))
|
|
||||||
// if err != nil {
|
|
||||||
// log.Fatalf("Frame Generator: %v", err)
|
|
||||||
// }
|
|
||||||
if err := gwa.Commit(gwa.TotalSlices, 0); err != nil {
|
|
||||||
log.Fatalf("Commit(%d): %v", idx, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
written++
|
|
||||||
idx++
|
|
||||||
if written%100 == 0 {
|
|
||||||
log.Printf("grains written=%d, index=%d", written, idx)
|
|
||||||
}
|
|
||||||
// Pace ourselves to roughly the grain rate
|
|
||||||
mxl.SleepNs(mxl.NsUntilIndex(idx, rate))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fillGrainRamp writes byte((i+start)&0xFF) across buf. A matching reader
|
|
||||||
// uses the same pattern to verify grain continuit
|
|
||||||
func fillGrainRamp(buf []byte, start uint64) {
|
|
||||||
for i := range buf {
|
|
||||||
buf[i] = byte((uint64(i) + start) & 0xFF)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func smpteBars(dest []byte) {
|
|
||||||
const width = 1920
|
|
||||||
|
|
||||||
// SMPTE 75% bars, Rec.709, 10-bit Y'CbCr: {Y, Cb, Cr}
|
|
||||||
bars := [7][3]uint32{
|
|
||||||
{721, 512, 512}, // 75% white
|
|
||||||
{674, 176, 543}, // 75% yellow
|
|
||||||
{581, 589, 176}, // 75% cyan
|
|
||||||
{534, 253, 207}, // 75% green
|
|
||||||
{251, 771, 817}, // 75% magenta
|
|
||||||
{204, 435, 848}, // 75% red
|
|
||||||
{111, 848, 481}, // 75% blue
|
|
||||||
}
|
|
||||||
|
|
||||||
barIdx := func(x int) int {
|
|
||||||
idx := x * len(bars) / width
|
|
||||||
if idx >= len(bars) {
|
|
||||||
return len(bars) - 1
|
|
||||||
}
|
|
||||||
return idx
|
|
||||||
}
|
|
||||||
|
|
||||||
// v210: each 16-byte block carries 6 pixels (Y0..Y5, chroma co-sited at 0/2/4)
|
|
||||||
for block := 0; block*16+16 <= len(dest); block++ {
|
|
||||||
baseX := (block * 6) % width
|
|
||||||
|
|
||||||
var y [6]uint32
|
|
||||||
for i := range y {
|
|
||||||
y[i] = bars[barIdx(baseX+i)][0]
|
|
||||||
}
|
|
||||||
cb0, cr0 := bars[barIdx(baseX)][1], bars[barIdx(baseX)][2]
|
|
||||||
cb2, cr2 := bars[barIdx(baseX+2)][1], bars[barIdx(baseX+2)][2]
|
|
||||||
cb4, cr4 := bars[barIdx(baseX+4)][1], bars[barIdx(baseX+4)][2]
|
|
||||||
|
|
||||||
w0 := (cb0 & 0x3FF) | ((y[0] & 0x3FF) << 10) | ((cr0 & 0x3FF) << 20)
|
|
||||||
w1 := (y[1] & 0x3FF) | ((cb2 & 0x3FF) << 10) | ((y[2] & 0x3FF) << 20)
|
|
||||||
w2 := (cr2 & 0x3FF) | ((y[3] & 0x3FF) << 10) | ((cb4 & 0x3FF) << 20)
|
|
||||||
w3 := (y[4] & 0x3FF) | ((cr4 & 0x3FF) << 10) | ((y[5] & 0x3FF) << 20)
|
|
||||||
|
|
||||||
byteIdx := block * 16
|
|
||||||
binary.LittleEndian.PutUint32(dest[byteIdx+0:], w0)
|
|
||||||
binary.LittleEndian.PutUint32(dest[byteIdx+4:], w1)
|
|
||||||
binary.LittleEndian.PutUint32(dest[byteIdx+8:], w2)
|
|
||||||
binary.LittleEndian.PutUint32(dest[byteIdx+12:], w3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
// Command wgpu-sample renders 75% SMPTE color bars (Rec.709, 10-bit v210)
|
|
||||||
// with a wgpu compute shader, verifies the output against the reference
|
|
||||||
// table, and reports per-frame timings.
|
|
||||||
//
|
|
||||||
// Run from the repo root: go run ./cmd/wgpu-sample
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gogpu/gputypes"
|
|
||||||
"github.com/gogpu/wgpu"
|
|
||||||
|
|
||||||
// Register all available GPU backends (Vulkan, DX12, GLES, Metal, etc.)
|
|
||||||
_ "github.com/gogpu/wgpu/hal/allbackends"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
width, height = 1920, 1080
|
|
||||||
wgSize = 64
|
|
||||||
blocks = width * height / 6
|
|
||||||
frameSize = uint64(blocks * 16)
|
|
||||||
benchFrames = 100
|
|
||||||
)
|
|
||||||
|
|
||||||
// 75% SMPTE bars, Rec.709, 10-bit {Y, Cb, Cr}
|
|
||||||
var bars = [7][3]uint32{
|
|
||||||
{721, 512, 512}, {674, 176, 543}, {581, 589, 176},
|
|
||||||
{534, 253, 207}, {251, 771, 817}, {204, 435, 848}, {111, 848, 481},
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if err := run(); err != nil {
|
|
||||||
log.Fatalf("FATAL: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func run() error {
|
|
||||||
instance, err := wgpu.CreateInstance(nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("CreateInstance: %w", err)
|
|
||||||
}
|
|
||||||
defer instance.Release()
|
|
||||||
|
|
||||||
adapter, err := instance.RequestAdapter(nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("RequestAdapter: %w", err)
|
|
||||||
}
|
|
||||||
defer adapter.Release()
|
|
||||||
|
|
||||||
info := adapter.Info()
|
|
||||||
fmt.Printf("adapter: %s (%s, %s, driver %s)\n", info.Name, info.Vendor, info.DeviceType, info.Driver)
|
|
||||||
|
|
||||||
device, err := adapter.RequestDevice(nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("RequestDevice: %w", err)
|
|
||||||
}
|
|
||||||
defer device.Release()
|
|
||||||
queue := device.Queue()
|
|
||||||
|
|
||||||
wgsl, err := os.ReadFile("kernels/v210_bars.wgsl")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("read kernel: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := device.CreateBuffer(&wgpu.BufferDescriptor{
|
|
||||||
Label: "v210-out", Size: frameSize,
|
|
||||||
Usage: wgpu.BufferUsageStorage | wgpu.BufferUsageCopySrc,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer out.Release()
|
|
||||||
|
|
||||||
staging, err := device.CreateBuffer(&wgpu.BufferDescriptor{
|
|
||||||
Label: "v210-staging", Size: frameSize,
|
|
||||||
Usage: wgpu.BufferUsageCopyDst | wgpu.BufferUsageMapRead,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer staging.Release()
|
|
||||||
|
|
||||||
params := make([]byte, 16)
|
|
||||||
binary.LittleEndian.PutUint32(params[0:], width)
|
|
||||||
binary.LittleEndian.PutUint32(params[4:], height)
|
|
||||||
uniform, err := device.CreateBuffer(&wgpu.BufferDescriptor{
|
|
||||||
Label: "params", Size: uint64(len(params)),
|
|
||||||
Usage: wgpu.BufferUsageUniform | wgpu.BufferUsageCopyDst,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer uniform.Release()
|
|
||||||
if err := queue.WriteBuffer(uniform, 0, params); err != nil {
|
|
||||||
return fmt.Errorf("write params: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
shader, err := device.CreateShaderModule(&wgpu.ShaderModuleDescriptor{
|
|
||||||
Label: "bars-shader", WGSL: string(wgsl),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("shader: %w", err)
|
|
||||||
}
|
|
||||||
defer shader.Release()
|
|
||||||
|
|
||||||
bgl, err := device.CreateBindGroupLayout(&wgpu.BindGroupLayoutDescriptor{
|
|
||||||
Label: "bars-bgl",
|
|
||||||
Entries: []gputypes.BindGroupLayoutEntry{
|
|
||||||
{Binding: 0, Visibility: wgpu.ShaderStageCompute, Buffer: &gputypes.BufferBindingLayout{Type: gputypes.BufferBindingTypeStorage}},
|
|
||||||
{Binding: 1, Visibility: wgpu.ShaderStageCompute, Buffer: &gputypes.BufferBindingLayout{Type: gputypes.BufferBindingTypeUniform}},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer bgl.Release()
|
|
||||||
|
|
||||||
bg, err := device.CreateBindGroup(&wgpu.BindGroupDescriptor{
|
|
||||||
Label: "bars-bg", Layout: bgl,
|
|
||||||
Entries: []wgpu.BindGroupEntry{
|
|
||||||
{Binding: 0, Buffer: out, Size: frameSize},
|
|
||||||
{Binding: 1, Buffer: uniform, Size: uint64(len(params))},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer bg.Release()
|
|
||||||
|
|
||||||
pl, err := device.CreatePipelineLayout(&wgpu.PipelineLayoutDescriptor{
|
|
||||||
Label: "bars-pl", BindGroupLayouts: []*wgpu.BindGroupLayout{bgl},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer pl.Release()
|
|
||||||
|
|
||||||
pipeline, err := device.CreateComputePipeline(&wgpu.ComputePipelineDescriptor{
|
|
||||||
Label: "bars-pipeline", Layout: pl, Module: shader, EntryPoint: "main",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("pipeline: %w", err)
|
|
||||||
}
|
|
||||||
defer pipeline.Release()
|
|
||||||
|
|
||||||
render := func(frame uint32) error {
|
|
||||||
binary.LittleEndian.PutUint32(params[8:], frame)
|
|
||||||
if err := queue.WriteBuffer(uniform, 0, params); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
encoder, err := device.CreateCommandEncoder(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pass, err := encoder.BeginComputePass(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pass.SetPipeline(pipeline)
|
|
||||||
pass.SetBindGroup(0, bg, nil)
|
|
||||||
pass.Dispatch((blocks+wgSize-1)/wgSize, 1, 1)
|
|
||||||
if err := pass.End(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
encoder.CopyBufferToBuffer(out, 0, staging, 0, frameSize)
|
|
||||||
cmd, err := encoder.Finish()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := queue.Submit(cmd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
readback := func() ([]byte, error) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
if err := staging.Map(ctx, wgpu.MapModeRead, 0, frameSize); err != nil {
|
|
||||||
return nil, fmt.Errorf("map: %w", err)
|
|
||||||
}
|
|
||||||
rng, err := staging.MappedRange(0, frameSize)
|
|
||||||
if err != nil {
|
|
||||||
_ = staging.Unmap()
|
|
||||||
return nil, fmt.Errorf("mapped range: %w", err)
|
|
||||||
}
|
|
||||||
data := make([]byte, frameSize)
|
|
||||||
copy(data, rng.Bytes())
|
|
||||||
if err := staging.Unmap(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Correctness: render one frame and verify every pixel.
|
|
||||||
fmt.Printf("frame: %dx%d v210, %d bytes\n", width, height, frameSize)
|
|
||||||
if err := render(0); err != nil {
|
|
||||||
return fmt.Errorf("render: %w", err)
|
|
||||||
}
|
|
||||||
data, err := readback()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("readback: %w", err)
|
|
||||||
}
|
|
||||||
if err := verify(data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println("verify: PASS (all 1920x1080 pixels match the 75% Rec.709 table)")
|
|
||||||
|
|
||||||
// Rough timing: full dispatch + copy + map/unmap per frame.
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
if err := render(uint32(i)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := readback(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
start := time.Now()
|
|
||||||
for i := 0; i < benchFrames; i++ {
|
|
||||||
if err := render(uint32(i)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := readback(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
perFrame := time.Since(start) / benchFrames
|
|
||||||
fmt.Printf("timing: %v/frame (dispatch+copy+readback), %.1f fps equivalent\n",
|
|
||||||
perFrame, float64(time.Second)/float64(perFrame))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify decodes the v210 frame and checks Y at every pixel and the
|
|
||||||
// co-sited chroma (even columns) against the reference table.
|
|
||||||
func verify(data []byte) error {
|
|
||||||
barOf := func(x int) int {
|
|
||||||
if b := x * 7 / width; b < 7 {
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
return 6
|
|
||||||
}
|
|
||||||
for y := 0; y < height; y++ {
|
|
||||||
for x := 0; x < width; x++ {
|
|
||||||
p := y*width + x
|
|
||||||
off := (p / 6) * 16
|
|
||||||
w0 := binary.LittleEndian.Uint32(data[off:])
|
|
||||||
w1 := binary.LittleEndian.Uint32(data[off+4:])
|
|
||||||
w2 := binary.LittleEndian.Uint32(data[off+8:])
|
|
||||||
w3 := binary.LittleEndian.Uint32(data[off+12:])
|
|
||||||
var yv, cb, cr uint32
|
|
||||||
switch p % 6 {
|
|
||||||
case 0:
|
|
||||||
yv, cb, cr = (w0>>10)&0x3FF, w0&0x3FF, (w0>>20)&0x3FF
|
|
||||||
case 1:
|
|
||||||
yv = w1 & 0x3FF
|
|
||||||
case 2:
|
|
||||||
yv, cb, cr = (w1>>20)&0x3FF, (w1>>10)&0x3FF, w2&0x3FF
|
|
||||||
case 3:
|
|
||||||
yv = (w2 >> 10) & 0x3FF
|
|
||||||
case 4:
|
|
||||||
yv, cb, cr = w3&0x3FF, (w2>>20)&0x3FF, (w3>>10)&0x3FF
|
|
||||||
case 5:
|
|
||||||
yv = (w3 >> 20) & 0x3FF
|
|
||||||
}
|
|
||||||
b := bars[barOf(x)]
|
|
||||||
if yv != b[0] {
|
|
||||||
return fmt.Errorf("pixel (%d,%d): Y=%d want %d", x, y, yv, b[0])
|
|
||||||
}
|
|
||||||
if x%2 == 0 && (cb != b[1] || cr != b[2]) {
|
|
||||||
return fmt.Errorf("pixel (%d,%d): Cb=%d Cr=%d want %d/%d", x, y, cb, cr, b[1], b[2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
// 75% SMPTE color bars, Rec.709, 10-bit Y'CbCr, packed as v210 (4:2:2),
|
|
||||||
// with an 8x16 bitmap-font text overlay (burn-in).
|
|
||||||
// Each work-item packs 6 pixels (6 Y + 3 Cb + 3 Cr) into one 16-byte block.
|
|
||||||
|
|
||||||
#define GW 8
|
|
||||||
#define GH 16
|
|
||||||
|
|
||||||
// Text overlay lookup. Atlas: 96 glyphs (ASCII 32..127) x GH rows,
|
|
||||||
// one byte per row, bit n = pixel at column n (LSB = leftmost).
|
|
||||||
// Returns: 1 = glyph pixel (foreground), 0 = inside text box (background),
|
|
||||||
// -1 = outside the text box (pattern shows through).
|
|
||||||
int text_pixel(__global const uchar* atlas, __global const uchar* text, int text_len,
|
|
||||||
int px, int py, int txt_x, int txt_y, int scale) {
|
|
||||||
if (text_len <= 0) return -1;
|
|
||||||
int tx = px - txt_x;
|
|
||||||
int ty = py - txt_y;
|
|
||||||
if (tx < 0 || ty < 0) return -1;
|
|
||||||
if (tx >= text_len * GW * scale || ty >= GH * scale) return -1;
|
|
||||||
int c = text[tx / (GW * scale)];
|
|
||||||
if (c < 32 || c > 127) return 0;
|
|
||||||
uchar row = atlas[(c - 32) * GH + ty / scale];
|
|
||||||
return (row >> ((tx / scale) % GW)) & 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
__kernel void generate_v210_pattern(
|
|
||||||
__global uint* output, int width, int height, int frame_index,
|
|
||||||
__global const uchar* atlas, __global const uchar* text, int text_len,
|
|
||||||
int txt_x, int txt_y, int scale, int has_bg,
|
|
||||||
uint fg_y, uint fg_cb, uint fg_cr,
|
|
||||||
uint bg_y, uint bg_cb, uint bg_cr) {
|
|
||||||
int g_id = get_global_id(0);
|
|
||||||
int total_blocks = (width * height) / 6;
|
|
||||||
if (g_id >= total_blocks) return;
|
|
||||||
|
|
||||||
int x = (g_id * 6) % width;
|
|
||||||
int py = (g_id * 6) / width;
|
|
||||||
|
|
||||||
// Bar order: 75% white, yellow, cyan, green, magenta, red, blue
|
|
||||||
ushort Y_table[7] = {721, 674, 581, 534, 251, 204, 111};
|
|
||||||
ushort U_table[7] = {512, 176, 589, 253, 771, 435, 848};
|
|
||||||
ushort V_table[7] = {512, 543, 176, 207, 817, 848, 481};
|
|
||||||
|
|
||||||
#define BAR(px) min(((px) * 7) / width, 6)
|
|
||||||
|
|
||||||
uint y[6], cb[6], cr[6];
|
|
||||||
for (int i = 0; i < 6; i++) {
|
|
||||||
int px = x + i;
|
|
||||||
int b = BAR(px);
|
|
||||||
uint yv = Y_table[b], cbv = U_table[b], crv = V_table[b];
|
|
||||||
int st = text_pixel(atlas, text, text_len, px, py, txt_x, txt_y, scale);
|
|
||||||
if (st > 0) {
|
|
||||||
yv = fg_y; cbv = fg_cb; crv = fg_cr;
|
|
||||||
} else if (st == 0 && has_bg) {
|
|
||||||
yv = bg_y; cbv = bg_cb; crv = bg_cr;
|
|
||||||
}
|
|
||||||
y[i] = yv; cb[i] = cbv; cr[i] = crv;
|
|
||||||
}
|
|
||||||
|
|
||||||
// v210 word layout, chroma co-sited with luma samples 0/2/4:
|
|
||||||
// w0 = Cb0|Y0<<10|Cr0<<20; w1 = Y1|Cb2<<10|Y2<<20;
|
|
||||||
// w2 = Cr2|Y3<<10|Cb4<<20; w3 = Y4|Cr4<<10|Y5<<20
|
|
||||||
uint word0 = (cb[0] & 0x3FF) | ((y[0] & 0x3FF) << 10) | ((cr[0] & 0x3FF) << 20);
|
|
||||||
uint word1 = (y[1] & 0x3FF) | ((cb[2] & 0x3FF) << 10) | ((y[2] & 0x3FF) << 20);
|
|
||||||
uint word2 = (cr[2] & 0x3FF) | ((y[3] & 0x3FF) << 10) | ((cb[4] & 0x3FF) << 20);
|
|
||||||
uint word3 = (y[4] & 0x3FF) | ((cr[4] & 0x3FF) << 10) | ((y[5] & 0x3FF) << 20);
|
|
||||||
|
|
||||||
int out_idx = g_id * 4;
|
|
||||||
output[out_idx + 0] = word0;
|
|
||||||
output[out_idx + 1] = word1;
|
|
||||||
output[out_idx + 2] = word2;
|
|
||||||
output[out_idx + 3] = word3;
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Bake an 8x16 1-bpp bitmap font (ASCII 32..127) from DejaVu Sans Mono 13px.
|
|
||||||
|
|
||||||
Output: Go source file with the atlas. One byte per glyph row, bit n = pixel
|
|
||||||
at column n (LSB = leftmost), matching the kernel's (row >> col) & 1 lookup.
|
|
||||||
"""
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
|
||||||
|
|
||||||
FONT = "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf"
|
|
||||||
SIZE = 13
|
|
||||||
BASELINE = 12 # baseline row inside the 16px cell
|
|
||||||
THRESH = 110
|
|
||||||
|
|
||||||
font = ImageFont.truetype(FONT, SIZE)
|
|
||||||
atlas = []
|
|
||||||
for c in range(32, 128):
|
|
||||||
img = Image.new("L", (8, 16), 0)
|
|
||||||
d = ImageDraw.Draw(img)
|
|
||||||
d.text((0, BASELINE), chr(c), font=font, fill=255, anchor="ls")
|
|
||||||
px = img.load()
|
|
||||||
for y in range(16):
|
|
||||||
b = 0
|
|
||||||
for x in range(8):
|
|
||||||
if px[x, y] >= THRESH:
|
|
||||||
b |= 1 << x
|
|
||||||
atlas.append(b)
|
|
||||||
|
|
||||||
def art(c):
|
|
||||||
print(f"--- '{chr(c)}' ---")
|
|
||||||
for y in range(16):
|
|
||||||
row = atlas[(c - 32) * 16 + y]
|
|
||||||
print("".join("#" if (row >> x) & 1 else "." for x in range(8)))
|
|
||||||
|
|
||||||
for c in "AgM%m0(":
|
|
||||||
art(ord(c))
|
|
||||||
|
|
||||||
lines = []
|
|
||||||
for i in range(0, len(atlas), 16):
|
|
||||||
chunk = ", ".join(f"0x{b:02X}" for b in atlas[i:i+16])
|
|
||||||
lines.append(f"\t{chunk},")
|
|
||||||
body = "\n".join(lines)
|
|
||||||
|
|
||||||
src = f'''package generator
|
|
||||||
|
|
||||||
// 8x16 1-bpp bitmap font baked from DejaVu Sans Mono {SIZE}px (public-domain
|
|
||||||
// style console glyphs). ASCII 32..127, one byte per row, LSB = leftmost pixel.
|
|
||||||
var fontAtlas8x16 = [96 * 16]byte{{
|
|
||||||
{body}
|
|
||||||
}}
|
|
||||||
'''
|
|
||||||
with open("internal/generator/font.go", "w") as f:
|
|
||||||
f.write(src)
|
|
||||||
print("wrote internal/generator/font.go,", len(atlas), "bytes of glyph data")
|
|
||||||
Reference in New Issue
Block a user