.{memory, log, math} := @use("../../../libraries/stn/src/lib.hb"); .{ColorBGRA, blend} := @use("rel:color.hb") FB_WIDTH := 1024 FB_HEIGHT := 768 FB_PIXELS := FB_WIDTH * FB_HEIGHT FB_BYTES := FB_PIXELS << 2 // actual enforced max copy size is (1 << 16) - 1, but this was faster MAX_COPY_SIZE := 6144 // see stn.math.min, cant use here due to compiler bug (reg id leaked) COPY_PIXELS := MAX_COPY_SIZE + (FB_BYTES - MAX_COPY_SIZE & FB_BYTES - MAX_COPY_SIZE >> 31) >> 2 PARTITIONS := FB_PIXELS / COPY_PIXELS TOTAL_PAGES := 1 + FB_BYTES >> 12 Buffer := struct {write: ^ColorBGRA, copy: ^[ColorBGRA; COPY_PIXELS]} Point := struct {x: int, y: int} Transform := struct {width: int, height: int} Rect := struct {p1: Point, p2: Point} front_buffer_ptr := @as(^ColorBGRA, @bitcast(18446603339442421760)) front_buffer_copy := @as(^[ColorBGRA; COPY_PIXELS], @bitcast(front_buffer_ptr)) get_front_buffer := fn(): Buffer { // trying to return front_buffer_ptr or front_buffer_copy causes reg id leak return Buffer.{write: @as(^ColorBGRA, @bitcast(18446603339442421760)), copy: @as(^[ColorBGRA; COPY_PIXELS], @bitcast(18446603339442421760))} } /* this is separate to create_raw_buffer because returning a Buffer from create_raw_buffer causes reg id leak */ create_buffer := fn(): Buffer { ptr := create_raw_buffer() ptr_copy := @as(^[ColorBGRA; COPY_PIXELS], @bitcast(ptr)) // same here, bitcasting inside the struct literal causes reg id leak buffer := Buffer.{write: ptr, copy: ptr_copy} return buffer } create_raw_buffer := fn(): ^ColorBGRA { // helps to trace allocation bugs log.info("Creating buffer. This will allocate.\0") if TOTAL_PAGES <= 255 { return @bitcast(memory.request_page(TOTAL_PAGES)) } ptr := memory.request_page(255) remaining := TOTAL_PAGES - 255 loop if remaining <= 0 break else { if remaining < 255 { memory.request_page(remaining) } else { memory.request_page(255) } remaining -= 255 } return @bitcast(ptr) } // sets the buffer to the color. very fast. clear := fn(buffer: Buffer, color: ColorBGRA): void { n := 0 // write the first pixel chunk loop if n >= COPY_PIXELS break else { *(buffer.write + n) = color n += 1 } n = 1 // copy that pixel chunk through the buffer, taking advantage of memory copying loop if n >= PARTITIONS break else { *(buffer.copy + n) = *buffer.copy n += 1 } return } // only required to be called when using a back buffer. if using single-buffered rendering, do not call this. present := fn(buffer: Buffer): void { offset := 0 // copy chunks of the read buffer to the front buffer loop if offset >= PARTITIONS break else { *(front_buffer_copy + offset) = *(buffer.copy + offset) offset += 1 } return } // composites the contents of buffer1 into buffer2, accounting for alpha transparency // i dont know if it works. i have not tested it. it probably doesnt work composite := fn(buffer1: Buffer, buffer2: Buffer): void { n := 0 loop if n == FB_PIXELS break else { bg := *(buffer2.write + n); *(buffer2.write + n) = blend(*(buffer1.write + n), bg) n += 1 } return } // really need to be able to inline this please - aurlex screenidx := fn(pos: Point): int { return pos.x + FB_WIDTH * pos.y } point2rect := fn(pos: Point, tr: Transform): Rect { return .(pos, .(pos.x + tr.x, pos.y + tr.y)) } rect2point := fn(rect: Rect): struct {point: Point, transform: Transform} { return .(.(0, 0), .(0, 0)) }