@ -96,6 +96,119 @@ function draw_html(state) {
@@ -96,6 +96,119 @@ function draw_html(state) {
}
function draw _strokes ( state , width , height , programs , gl , lod _levels , segment _count ,
total _lod _floats , total _lod _indices ,
batches _tv ,
points _tv ,
ids _tv ,
pressures _tv ,
vbo ,
ebo ,
stroke _texture ,
stroke _data ,
stroke _count ,
) {
const pr = programs [ 'main' ] ;
// Last pair (lod unused) to have a proper from;to
tv _add2 ( batches _tv , segment _count ) ;
tv _add2 ( batches _tv , - 1 ) ;
gl . clear ( gl . DEPTH _BUFFER _BIT ) ; // draw strokes above the images
gl . useProgram ( pr . program ) ;
const total _size = points _tv . size * 4 +
ids _tv . size * 4 +
round _to _pow2 ( pressures _tv . size , 4 ) +
total _lod _floats * 4 ;
gl . bindBuffer ( gl . ARRAY _BUFFER , vbo ) ;
gl . bufferData ( gl . ARRAY _BUFFER , total _size , gl . STREAM _DRAW ) ;
// Segment points, segment stroke ids, segment pressures
gl . bufferSubData ( gl . ARRAY _BUFFER , 0 , tv _data ( points _tv ) ) ;
gl . bufferSubData ( gl . ARRAY _BUFFER , points _tv . size * 4 , tv _data ( ids _tv ) ) ;
gl . bufferSubData ( gl . ARRAY _BUFFER , points _tv . size * 4 + ids _tv . size * 4 , tv _data ( pressures _tv ) ) ;
gl . bindBuffer ( gl . ELEMENT _ARRAY _BUFFER , ebo ) ;
gl . bufferData ( gl . ELEMENT _ARRAY _BUFFER , total _lod _indices * 4 , gl . STREAM _DRAW ) ;
// Upload all variants of LOD vertices/indices
const base _lod _points _offset = points _tv . size * 4 + ids _tv . size * 4 + round _to _pow2 ( pressures _tv . size , 4 ) ;
for ( const level of lod _levels ) {
gl . bufferSubData ( gl . ARRAY _BUFFER , base _lod _points _offset + level . vertices _offset , tv _data ( level . data . points ) ) ;
gl . bufferSubData ( gl . ELEMENT _ARRAY _BUFFER , level . indices _offset , tv _data ( level . data . indices ) ) ;
}
// Per-stroke data (base width, color)
gl . bindTexture ( gl . TEXTURE _2D , stroke _texture ) ;
upload _square _rgba16ui _texture ( gl , stroke _data , config . stroke _texture _size ) ;
gl . uniform2f ( pr . locations [ 'u_res' ] , width , height ) ;
gl . uniform2f ( pr . locations [ 'u_scale' ] , state . canvas . zoom , state . canvas . zoom ) ;
gl . uniform2f ( pr . locations [ 'u_translation' ] , state . canvas . offset . x , state . canvas . offset . y ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_count' ] , stroke _count ) ;
gl . uniform1i ( pr . locations [ 'u_debug_mode' ] , state . debug . red ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_data' ] , 0 ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_texture_size' ] , config . stroke _texture _size ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_pos' ] ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_a' ] ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_b' ] ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_stroke_id' ] ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_pressure' ] ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_pos' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_a' ] , 1 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_b' ] , 1 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_stroke_id' ] , 1 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_pressure' ] , 1 ) ;
for ( let b = 0 ; b < batches _tv . size - 2 ; b += 2 ) {
const batch _from = batches _tv . data [ b + 0 ] ;
const batch _size = batches _tv . data [ b + 2 ] - batch _from ;
let lod _level = batches _tv . data [ b + 1 ] ;
if ( config . debug _force _lod !== null ) {
lod _level = config . debug _force _lod ;
}
const level = lod _levels [ lod _level ] ;
if ( batch _size > 0 ) {
//stat_total_vertices += batch_size * level.data.indices.size;
gl . uniform1i ( pr . locations [ 'u_circle_points' ] , level . data . points . size / 2 - 4 ) ;
gl . uniform3f ( pr . locations [ 'u_debug_color' ] ,
( lod _level * 785892 + 125127 ) % 8 / 7 ,
( lod _level * 901824 + 985835 ) % 8 / 7 ,
( lod _level * 232181 + 838533 ) % 8 / 7 ,
) ;
// Points (a, b) and stroke ids are stored in separate cpu buffers so that points can be reused (look at stride and offset values)
gl . vertexAttribPointer ( pr . locations [ 'a_a' ] , 2 , gl . FLOAT , false , 2 * 4 , batch _from * 2 * 4 ) ;
gl . vertexAttribPointer ( pr . locations [ 'a_b' ] , 2 , gl . FLOAT , false , 2 * 4 , batch _from * 2 * 4 + 2 * 4 ) ;
gl . vertexAttribIPointer ( pr . locations [ 'a_stroke_id' ] , 1 , gl . INT , 4 , points _tv . size * 4 + batch _from * 4 ) ;
gl . vertexAttribPointer ( pr . locations [ 'a_pressure' ] , 2 , gl . UNSIGNED _BYTE , true , 1 , points _tv . size * 4 + ids _tv . size * 4 + batch _from ) ;
gl . vertexAttribPointer ( pr . locations [ 'a_pos' ] , 2 , gl . FLOAT , false , 2 * 4 , base _lod _points _offset + level . vertices _offset , 4 ) ;
gl . drawElementsInstanced ( gl . TRIANGLES , level . data . indices . size , gl . UNSIGNED _INT , level . indices _offset , batch _size ) ;
}
}
// I don't really know why I need to do this, but it
// makes background patter drawcall work properly
gl . vertexAttribDivisor ( pr . locations [ 'a_pos' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_a' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_b' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_stroke_id' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_pressure' ] , 0 ) ;
tv _pop ( batches _tv ) ;
tv _pop ( batches _tv ) ;
}
async function draw ( state , context , animate , ts ) {
const dt = ts - context . last _frame _ts ;
const cpu _before = performance . now ( ) ;
@ -291,173 +404,40 @@ async function draw(state, context, animate, ts) {
@@ -291,173 +404,40 @@ async function draw(state, context, animate, ts) {
// "Static" data upload
if ( segment _count > 0 ) {
const pr = programs [ 'main' ] ;
// Last pair (lod unused) to have a proper from;to
tv _add2 ( context . instance _data _batches , segment _count ) ;
tv _add2 ( context . instance _data _batches , - 1 ) ;
gl . clear ( gl . DEPTH _BUFFER _BIT ) ; // draw strokes above the images
gl . useProgram ( pr . program ) ;
const total _static _size = context . instance _data _points . size * 4 +
context . instance _data _ids . size * 4 +
round _to _pow2 ( context . instance _data _pressures . size , 4 ) +
total _lod _floats * 4 ;
gl . bindBuffer ( gl . ARRAY _BUFFER , buffers [ 'b_strokes_static' ] ) ;
gl . bufferData ( gl . ARRAY _BUFFER , total _static _size , gl . STREAM _DRAW ) ;
// Segment points, segment stroke ids, segment pressures
gl . bufferSubData ( gl . ARRAY _BUFFER , 0 , tv _data ( context . instance _data _points ) ) ;
gl . bufferSubData ( gl . ARRAY _BUFFER , context . instance _data _points . size * 4 , tv _data ( context . instance _data _ids ) ) ;
gl . bufferSubData ( gl . ARRAY _BUFFER , context . instance _data _points . size * 4 + context . instance _data _ids . size * 4 ,
tv _data ( context . instance _data _pressures ) ) ;
gl . bindBuffer ( gl . ELEMENT _ARRAY _BUFFER , buffers [ 'i_strokes_static' ] ) ;
gl . bufferData ( gl . ELEMENT _ARRAY _BUFFER , total _lod _indices * 4 , gl . STREAM _DRAW ) ;
// Upload all variants of LOD vertices/indices
const base _lod _points _offset = context . instance _data _points . size * 4 + context . instance _data _ids . size * 4 + round _to _pow2 ( context . instance _data _pressures . size , 4 ) ;
for ( const level of lod _levels ) {
gl . bufferSubData ( gl . ARRAY _BUFFER , base _lod _points _offset + level . vertices _offset , tv _data ( level . data . points ) ) ;
gl . bufferSubData ( gl . ELEMENT _ARRAY _BUFFER , level . indices _offset , tv _data ( level . data . indices ) ) ;
}
// Per-stroke data (base width, color)
gl . bindTexture ( gl . TEXTURE _2D , textures [ 'stroke_data' ] ) ;
upload _square _rgba16ui _texture ( gl , context . stroke _data , config . stroke _texture _size ) ;
gl . uniform2f ( pr . locations [ 'u_res' ] , context . canvas . width , context . canvas . height ) ;
gl . uniform2f ( pr . locations [ 'u_scale' ] , state . canvas . zoom , state . canvas . zoom ) ;
gl . uniform2f ( pr . locations [ 'u_translation' ] , state . canvas . offset . x , state . canvas . offset . y ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_count' ] , state . events . length ) ;
gl . uniform1i ( pr . locations [ 'u_debug_mode' ] , state . debug . red ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_data' ] , 0 ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_texture_size' ] , config . stroke _texture _size ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_pos' ] ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_a' ] ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_b' ] ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_stroke_id' ] ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_pressure' ] ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_pos' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_a' ] , 1 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_b' ] , 1 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_stroke_id' ] , 1 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_pressure' ] , 1 ) ;
for ( let b = 0 ; b < context . instance _data _batches . size - 2 ; b += 2 ) {
const batch _from = context . instance _data _batches . data [ b + 0 ] ;
const batch _size = context . instance _data _batches . data [ b + 2 ] - batch _from ;
let lod _level = context . instance _data _batches . data [ b + 1 ] ;
if ( config . debug _force _lod !== null ) {
lod _level = config . debug _force _lod ;
}
const level = lod _levels [ lod _level ] ;
if ( batch _size > 0 ) {
stat _total _vertices += batch _size * level . data . indices . size ;
gl . uniform1i ( pr . locations [ 'u_circle_points' ] , level . data . points . size / 2 - 4 ) ;
gl . uniform3f ( pr . locations [ 'u_debug_color' ] ,
( lod _level * 785892 + 125127 ) % 8 / 7 ,
( lod _level * 901824 + 985835 ) % 8 / 7 ,
( lod _level * 232181 + 838533 ) % 8 / 7 ,
) ;
// Points (a, b) and stroke ids are stored in separate cpu buffers so that points can be reused (look at stride and offset values)
gl . vertexAttribPointer ( pr . locations [ 'a_a' ] , 2 , gl . FLOAT , false , 2 * 4 , batch _from * 2 * 4 ) ;
gl . vertexAttribPointer ( pr . locations [ 'a_b' ] , 2 , gl . FLOAT , false , 2 * 4 , batch _from * 2 * 4 + 2 * 4 ) ;
gl . vertexAttribIPointer ( pr . locations [ 'a_stroke_id' ] , 1 , gl . INT , 4 , context . instance _data _points . size * 4 + batch _from * 4 ) ;
gl . vertexAttribPointer ( pr . locations [ 'a_pressure' ] , 2 , gl . UNSIGNED _BYTE , true , 1 , context . instance _data _points . size * 4 + context . instance _data _ids . size * 4 + batch _from ) ;
gl . vertexAttribPointer ( pr . locations [ 'a_pos' ] , 2 , gl . FLOAT , false , 2 * 4 , base _lod _points _offset + level . vertices _offset , 4 ) ;
gl . drawElementsInstanced ( gl . TRIANGLES , level . data . indices . size , gl . UNSIGNED _INT , level . indices _offset , batch _size ) ;
}
}
// I don't really know why I need to do this, but it
// makes background patter drawcall work properly
gl . vertexAttribDivisor ( pr . locations [ 'a_pos' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_a' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_b' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_stroke_id' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_pressure' ] , 0 ) ;
draw _strokes ( state , context . canvas . width , context . canvas . height , programs , gl , lod _levels , segment _count ,
total _lod _floats ,
total _lod _indices ,
context . instance _data _batches ,
context . instance _data _points ,
context . instance _data _ids ,
context . instance _data _pressures ,
buffers [ 'b_strokes_static' ] ,
buffers [ 'i_strokes_static' ] ,
textures [ 'stroke_data' ] ,
context . stroke _data ,
state . events . length , // not really
) ;
}
// Dynamic draw (strokes currently being drawn)
if ( false && dynamic _segment _count > 0 ) {
const pr = programs [ 'main' ] ; // same as static
if ( dynamic _segment _count > 0 ) {
// Dynamic strokes should be drawn above static strokes
gl . clear ( gl . DEPTH _BUFFER _BIT ) ;
gl . useProgram ( pr . program ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_count' ] , dynamic _stroke _count ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_data' ] , 0 ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_texture_size' ] , config . dynamic _stroke _texture _size ) ;
gl . bindBuffer ( gl . ARRAY _BUFFER , buffers [ 'b_strokes_dynamic' ] ) ;
// Dynamic data upload
const total _dynamic _size =
context . dynamic _instance _points . size * 4 + context . dynamic _instance _ids . size * 4 +
context . dynamic _instance _pressure . size ;
gl . bufferData ( gl . ARRAY _BUFFER , total _dynamic _size , gl . STREAM _DRAW ) ;
gl . bufferSubData ( gl . ARRAY _BUFFER , 0 , tv _data ( context . dynamic _instance _points ) ) ;
gl . bufferSubData ( gl . ARRAY _BUFFER , context . dynamic _instance _points . size * 4 , tv _data ( context . dynamic _instance _ids ) ) ;
gl . bufferSubData ( gl . ARRAY _BUFFER , context . dynamic _instance _points . size * 4 + context . dynamic _instance _ids . size * 4 ,
tv _data ( context . dynamic _instance _pressure ) ) ;
gl . bindTexture ( gl . TEXTURE _2D , textures [ 'dynamic_stroke_data' ] ) ;
upload _square _rgba16ui _texture ( gl , context . dynamic _stroke _data , config . dynamic _stroke _texture _size ) ;
gl . uniform2f ( pr . locations [ 'u_res' ] , context . canvas . width , context . canvas . height ) ;
gl . uniform2f ( pr . locations [ 'u_scale' ] , state . canvas . zoom , state . canvas . zoom ) ;
gl . uniform2f ( pr . locations [ 'u_translation' ] , state . canvas . offset . x , state . canvas . offset . y ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_count' ] , context . dynamic _stroke _count ) ;
gl . uniform1i ( pr . locations [ 'u_debug_mode' ] , state . debug . red ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_data' ] , 0 ) ;
gl . uniform1i ( pr . locations [ 'u_stroke_texture_size' ] , config . dynamic _stroke _texture _size ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_a' ] ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_b' ] ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_stroke_id' ] ) ;
gl . enableVertexAttribArray ( pr . locations [ 'a_pressure' ] ) ;
// Points (a, b) and stroke ids are stored in separate cpu buffers so that points can be reused (look at stride and offset values)
if ( context . dynamic _instance _ids . size > 1 ) {
gl . vertexAttribPointer ( pr . locations [ 'a_a' ] , 2 , gl . FLOAT , false , 2 * 4 , 0 ) ;
gl . vertexAttribPointer ( pr . locations [ 'a_b' ] , 2 , gl . FLOAT , false , 2 * 4 , 2 * 4 ) ;
} else {
// A special case where there is no second point. Reuse the first point and handle the zero length segment in the shader
gl . vertexAttribPointer ( pr . locations [ 'a_a' ] , 2 , gl . FLOAT , false , 2 * 4 , 0 ) ;
gl . vertexAttribPointer ( pr . locations [ 'a_b' ] , 2 , gl . FLOAT , false , 2 * 4 , 0 ) ;
}
gl . vertexAttribIPointer ( pr . locations [ 'a_stroke_id' ] , 1 , gl . INT , 4 , context . dynamic _instance _points . size * 4 ) ;
gl . vertexAttribPointer ( pr . locations [ 'a_pressure' ] , 2 , gl . UNSIGNED _BYTE , true , 1 , context . dynamic _instance _points . size * 4 + context . dynamic _instance _ids . size * 4 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_a' ] , 1 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_b' ] , 1 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_stroke_id' ] , 1 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_pressure' ] , 1 ) ;
gl . drawArraysInstanced ( gl . TRIANGLES , 0 , 32 * 3 + 6 + 32 * 3 , dynamic _segment _count ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_a' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_b' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_stroke_id' ] , 0 ) ;
gl . vertexAttribDivisor ( pr . locations [ 'a_pressure' ] , 0 ) ;
draw _strokes ( state , context . canvas . width , context . canvas . height , programs , gl , lod _levels , dynamic _segment _count ,
total _lod _floats ,
total _lod _indices ,
context . dynamic _instance _batches ,
context . dynamic _instance _points ,
context . dynamic _instance _ids ,
context . dynamic _instance _pressure ,
buffers [ 'b_strokes_dynamic' ] ,
buffers [ 'i_strokes_dynamic' ] ,
textures [ 'dynamic_stroke_data' ] ,
context . dynamic _stroke _data ,
context . dynamic _stroke _count ,
) ;
}
// HUD: resize handles, etc