Skip to content

Commit

Permalink
examples: add examples/gg/random.v demonstrating how to stream images…
Browse files Browse the repository at this point in the history
…/pixels
  • Loading branch information
spytheman committed Jun 24, 2021
1 parent 4bfe761 commit b239142
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 5 deletions.
63 changes: 63 additions & 0 deletions examples/gg/random.v
Original file line number Diff line number Diff line change
@@ -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()
}
4 changes: 4 additions & 0 deletions vlib/builtin/int.v
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
4 changes: 3 additions & 1 deletion vlib/gg/gg.v
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
}

Expand Down
62 changes: 60 additions & 2 deletions vlib/gg/image.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module gg

// import gx
// import sokol.sapp
// import sokol.gfx
import sokol.gfx
import os
import sokol
import sokol.sgl
Expand Down Expand Up @@ -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{
Expand All @@ -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{
Expand All @@ -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) {
Expand Down
7 changes: 5 additions & 2 deletions vlib/sokol/gfx/gfx_structs.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

1 comment on commit b239142

@larpon
Copy link
Contributor

@larpon larpon commented on b239142 Jun 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool example! Why are there no locks? Is access to the image array atomic?

Please sign in to comment.