From b2391424d9fdb0ef6aad2d123bb83ef4d3aa8391 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Thu, 24 Jun 2021 17:45:14 +0300 Subject: [PATCH] examples: add examples/gg/random.v demonstrating how to stream images/pixels --- examples/gg/random.v | 63 ++++++++++++++++++++++++++++++++++++ vlib/builtin/int.v | 4 +++ vlib/gg/gg.v | 4 ++- vlib/gg/image.v | 62 +++++++++++++++++++++++++++++++++-- vlib/sokol/gfx/gfx_structs.v | 7 ++-- 5 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 examples/gg/random.v diff --git a/examples/gg/random.v b/examples/gg/random.v new file mode 100644 index 0000000000..b706038d7f --- /dev/null +++ b/examples/gg/random.v @@ -0,0 +1,63 @@ +import gg +import time + +const pwidth = 800 + +const pheight = 600 + +const pbytes = 4 + +struct AppState { +mut: + gg &gg.Context = 0 + istream_idx int + pixels [pwidth][pheight]u32 +} + +[direct_array_access] +fn (mut state AppState) update() { + mut rcolor := u64(state.gg.frame) + for { + for x in 0 .. pwidth { + for y in 0 .. pheight { + rcolor = rcolor * 1664525 + 1013904223 + state.pixels[x][y] = u32(rcolor & 0x0000_0000_FFFF_FFFF) | 0x1010AFFF + } + } + time.sleep(33 * time.millisecond) + } +} + +fn (mut state AppState) draw() { + mut istream_image := state.gg.get_cached_image_by_idx(state.istream_idx) + istream_image.update_pixel_data(&state.pixels) + size := gg.window_size() + state.gg.draw_image(0, 0, size.width, size.height, istream_image) +} + +// gg callbacks: + +fn graphics_init(mut state AppState) { + state.istream_idx = state.gg.new_streaming_image(pwidth, pheight, pbytes) +} + +fn graphics_frame(mut state AppState) { + state.gg.begin() + state.draw() + state.gg.end() +} + +fn main() { + mut state := &AppState{} + state.gg = gg.new_context( + width: 800 + height: 600 + create_window: true + window_title: 'Random Static' + init_fn: graphics_init + frame_fn: graphics_frame + user_data: state + ) + go state.update() + state.gg.run() +} diff --git a/vlib/builtin/int.v b/vlib/builtin/int.v index 941862a744..84c86cde00 100644 --- a/vlib/builtin/int.v +++ b/vlib/builtin/int.v @@ -16,6 +16,10 @@ pub fn ptr_str(ptr voidptr) string { return buf1 } +pub fn (x size_t) str() string { + return u64(x).str() +} + pub fn (cptr &char) str() string { return u64(cptr).hex() } diff --git a/vlib/gg/gg.v b/vlib/gg/gg.v index ef8a684ccd..8f7013b57a 100644 --- a/vlib/gg/gg.v +++ b/vlib/gg/gg.v @@ -241,7 +241,9 @@ fn gg_init_sokol_window(user_data voidptr) { } for i in 0 .. g.image_cache.len { - g.image_cache[i].init_sokol_image() + if g.image_cache[i].simg.id == 0 { + g.image_cache[i].init_sokol_image() + } } } diff --git a/vlib/gg/image.v b/vlib/gg/image.v index 6868b517c1..3f2083c8c1 100644 --- a/vlib/gg/image.v +++ b/vlib/gg/image.v @@ -4,7 +4,7 @@ module gg // import gx // import sokol.sapp -// import sokol.gfx +import sokol.gfx import os import sokol import sokol.sgl @@ -153,6 +153,17 @@ pub fn (mut ctx Context) create_image_from_byte_array(b []byte) Image { return ctx.create_image_from_memory(b.data, b.len) } +pub fn (mut ctx Context) cache_image(img Image) int { + ctx.image_cache << img + image_idx := ctx.image_cache.len - 1 + ctx.image_cache[image_idx].id = image_idx + return image_idx +} + +pub fn (mut ctx Context) get_cached_image_by_idx(image_idx int) &Image { + return &ctx.image_cache[image_idx] +} + pub fn (mut img Image) init_sokol_image() &Image { // println('\n init sokol image $img.path ok=$img.simg_ok') mut img_desc := C.sg_image_desc{ @@ -161,7 +172,7 @@ pub fn (mut img Image) init_sokol_image() &Image { num_mipmaps: 0 wrap_u: .clamp_to_edge wrap_v: .clamp_to_edge - label: &char(0) + label: img.path.str d3d11_texture: 0 } img_desc.data.subimage[0][0] = C.sg_range{ @@ -174,6 +185,53 @@ pub fn (mut img Image) init_sokol_image() &Image { return img } +// new_streaming_image returns a cached `image_idx` of a special image, that +// can be updated *each frame* by calling: gg.update_pixel_data(image_idx, buf) +// ... where buf is a pointer to the actual pixel data for the image. +// NB: you still need to call app.gg.draw_image after that, to actually draw it. +pub fn (mut ctx Context) new_streaming_image(w int, h int, channels int) int { + mut img := Image{} + img.width = w + img.height = h + img.nr_channels = channels // 4 bytes per pixel for .rgba8, see pixel_format + mut img_desc := C.sg_image_desc{ + width: img.width + height: img.height + pixel_format: .rgba8 + num_slices: 1 + num_mipmaps: 1 + usage: .stream + wrap_u: .clamp_to_edge + wrap_v: .clamp_to_edge + min_filter: .linear + mag_filter: .linear + label: img.path.str + } + // Sokol requires that streamed images have NO .ptr/.size initially: + img_desc.data.subimage[0][0] = C.sg_range{ + ptr: 0 + size: size_t(0) + } + img.simg = C.sg_make_image(&img_desc) + img.simg_ok = true + img.ok = true + img_idx := ctx.cache_image(img) + return img_idx +} + +// update_pixel_data is a helper for working with image streams (i.e. images, +// that are updated dynamically by the CPU on each frame) +pub fn (mut ctx Context) update_pixel_data(cached_image_idx int, buf &byte) { + ctx.get_cached_image_by_idx(cached_image_idx).update_pixel_data(buf) +} + +pub fn (mut img Image) update_pixel_data(buf &byte) { + mut data := C.sg_image_data{} + data.subimage[0][0].ptr = buf + data.subimage[0][0].size = size_t(img.width * img.height * img.nr_channels) + gfx.update_image(img.simg, &data) +} + // draw_image_with_config takes in a config that details how the // provided image should be drawn onto the screen pub fn (ctx &Context) draw_image_with_config(config DrawImageConfig) { diff --git a/vlib/sokol/gfx/gfx_structs.v b/vlib/sokol/gfx/gfx_structs.v index fe2c24b2ca..1bf6c3a75a 100644 --- a/vlib/sokol/gfx/gfx_structs.v +++ b/vlib/sokol/gfx/gfx_structs.v @@ -396,10 +396,13 @@ pub fn (i C.sg_image) free() { C.sg_destroy_image(i) } +pub const sg_cubeface_num = 6 + +pub const sg_max_mipmaps = 16 + pub struct C.sg_image_data { pub mut: - // subimage [C.SG_CUBEFACE_NUM][C.SG_MAX_MIPMAPS]C.sg_range - subimage [6][16]C.sg_range + subimage [sg_cubeface_num][sg_max_mipmaps]C.sg_range } pub struct C.sg_features {