Skip to content

Commit 0fd7010

Browse files
author
hartsantler
committed
prototype GLSL backend using WebCLGL: https://github.com/3DRoberto/webclgl
1 parent 8e125d3 commit 0fd7010

7 files changed

Lines changed: 142 additions & 16 deletions

File tree

pythonjs/code_writer.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
class Writer(object):
1010

1111
def __init__(self):
12+
self.inline_glsl = False
13+
self.inline_skip = ('@', 'def ', 'while ', 'if ', 'for ', 'var(')
1214
self.level = 0
1315
self.buffer = list()
1416
self.output = StringIO()
@@ -34,6 +36,8 @@ def write(self, code):
3436

3537
def _write(self, code):
3638
indentation = self.level * 4 * ' '
39+
if self.inline_glsl and not code.startswith( self.inline_skip ):
40+
code = "inline('''%s''')" %code
3741
s = '%s%s\n' % (indentation, code)
3842
self.output.write(s)
3943

pythonjs/python_to_pythonjs.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ def __init__(self, source=None, module=None, module_path=None, dart=False, coffe
153153
self._with_rpc = None
154154
self._with_rpc_name = None
155155
self._with_direct_keys = False
156+
self._with_glsl = False
156157

157158
self._source = source.splitlines()
158159
self._classes = dict() ## class name : [method names]
@@ -1134,7 +1135,9 @@ def visit_Return(self, node):
11341135
# writer.write('self["__dict__"]["%s"] = %s' %(self._cached_property, self.visit(node.value)) )
11351136
# writer.write('return self["__dict__"]["%s"]' %self._cached_property)
11361137
#else:
1137-
if self._inline:
1138+
if self._with_glsl:
1139+
writer.write('out_float = %s' %self.visit(node.value))
1140+
elif self._inline:
11381141
writer.write('__returns__%s = %s' %(self._inline[-1], self.visit(node.value)) )
11391142
if self._inline_breakout:
11401143
writer.write('break')
@@ -1160,7 +1163,10 @@ def visit_BinOp(self, node):
11601163
op = self.visit(node.op)
11611164
right = self.visit(node.right)
11621165

1163-
if op == '|':
1166+
if self._with_glsl:
1167+
return '(%s %s %s)' % (left, op, right)
1168+
1169+
elif op == '|':
11641170
if isinstance(node.right, Str):
11651171
self._custom_op_hack = (node.right.s, left)
11661172
return ''
@@ -1451,7 +1457,7 @@ def visit_Subscript(self, node):
14511457
#return '%s["$wrapped"]' %name
14521458
return '%s[...]' %name
14531459

1454-
elif self._with_ll:
1460+
elif self._with_ll or self._with_glsl:
14551461
return '%s[ %s ]' %(name, self.visit(node.slice))
14561462

14571463
elif self._with_js or self._with_dart:
@@ -2392,7 +2398,7 @@ def visit_FunctionDef(self, node):
23922398
self._cached_property = None
23932399
self._func_typedefs = {}
23942400

2395-
if writer.is_at_global_level() and not self._with_webworker:
2401+
if writer.is_at_global_level() and not self._with_webworker and not self._with_glsl:
23962402
self._global_functions[ node.name ] = node ## save ast-node
23972403

23982404
for decorator in reversed(node.decorator_list):
@@ -2564,7 +2570,11 @@ def visit_FunctionDef(self, node):
25642570
writer.write( 'def %s( %s ):' % (node.name, ','.join(args)) )
25652571

25662572

2567-
elif self._with_js or javascript or self._with_ll:# or self._with_coffee:
2573+
elif self._with_js or javascript or self._with_ll or self._with_glsl:
2574+
2575+
if self._with_glsl:
2576+
writer.write('@__glsl__')
2577+
25682578
if node.args.vararg:
25692579
#raise SyntaxError( 'pure javascript functions can not take variable arguments (*args)' )
25702580
writer.write('#WARNING - NOT IMPLEMENTED: javascript-mode functions with (*args)')
@@ -2994,6 +3004,13 @@ def visit_For(self, node):
29943004
c.constant = True
29953005
self._call_ids += 1
29963006

3007+
if self._with_glsl:
3008+
writer.write( 'for %s in %s:' %(self.visit(node.target), self.visit(node.iter)) )
3009+
writer.push()
3010+
map(self.visit, node.body)
3011+
writer.pull()
3012+
return None
3013+
29973014
if self._with_rpc_name and isinstance(node.iter, ast.Attribute) and isinstance(node.iter.value, ast.Name) and node.iter.value.id == self._with_rpc_name:
29983015
target = self.visit(node.target)
29993016
writer.write('def __rpc_loop__():')
@@ -3240,7 +3257,16 @@ def visit_While(self, node):
32403257
def visit_With(self, node):
32413258
global writer
32423259

3243-
if isinstance( node.context_expr, ast.Call ) and isinstance(node.context_expr.func, ast.Name) and node.context_expr.func.id == 'rpc':
3260+
if isinstance( node.context_expr, Name ) and node.context_expr.id == 'glsl':
3261+
writer.inline_glsl = True
3262+
self._with_glsl = True
3263+
for b in node.body:
3264+
a = self.visit(b)
3265+
if a: writer.write(a)
3266+
self._with_glsl = False
3267+
writer.inline_glsl = False
3268+
3269+
elif isinstance( node.context_expr, ast.Call ) and isinstance(node.context_expr.func, ast.Name) and node.context_expr.func.id == 'rpc':
32443270
self._with_rpc = self.visit( node.context_expr.args[0] )
32453271
if isinstance(node.optional_vars, ast.Name):
32463272
self._with_rpc_name = node.optional_vars.id

pythonjs/pythonjs.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ def __init__(self, requirejs=True, insert_runtime=True, webworker=False, functio
3131
self._webworker = webworker
3232
self._exports = set()
3333

34+
self.special_decorators = set(['__typedef__', '__glsl__'])
35+
self._glsl = False
36+
3437
def indent(self): return ' ' * self._indent
3538
def push(self): self._indent += 1
3639
def pull(self):
@@ -172,8 +175,57 @@ def visit_FunctionDef(self, node):
172175
return buffer
173176

174177
def _visit_function(self, node):
178+
glsl = False
179+
args_typedefs = {}
180+
for decor in node.decorator_list:
181+
if isinstance(decor, ast.Name) and decor.id == '__glsl__':
182+
glsl = True
183+
elif isinstance(decor, ast.Call) and isinstance(decor.func, ast.Name) and decor.func.id == '__typedef__':
184+
for key in decor.keywords:
185+
args_typedefs[ key.arg ] = key.value.id
186+
175187
args = self.visit(node.args)
176-
if len(node.decorator_list)==1 and not ( isinstance(node.decorator_list[0], ast.Call) and node.decorator_list[0].func.id == '__typedef__' ):
188+
189+
if glsl:
190+
lines = ['var __program__ = [];']
191+
x = []
192+
for i,arg in enumerate(args):
193+
assert arg in args_typedefs
194+
x.append( '%s %s' %(args_typedefs[arg].replace('POINTER', '*'), arg) )
195+
lines.append( '__program__.push("void main( %s ) {");' %', '.join(x) )
196+
197+
self.push()
198+
self._glsl = True
199+
for child in node.body:
200+
if isinstance(child, Str):
201+
continue
202+
else:
203+
for sub in self.visit(child).splitlines():
204+
lines.append( '__program__.push("%s");' %(self.indent()+sub) )
205+
self._glsl = False
206+
#buffer += '\n'.join(body)
207+
self.pull()
208+
lines.append('\n__program__.push("%s}");' %self.indent())
209+
lines.append('function call_webclgl_program( %s ) {' %','.join(args))
210+
lines.append(' var offset = 0') ## TODO data range, 0 allows 0-1.0
211+
lines.append(' var __webclgl = new WebCLGL()')
212+
lines.append(' var return_buffer = __webclgl.createBuffer(1, "FLOAT", offset)') ## TODO length of return buffer
213+
for arg in args:
214+
lines.append(' var %s_buffer = __webclgl.createBuffer(%s.length, "FLOAT", offset)' %(arg,arg))
215+
lines.append(' __webclgl.enqueueWriteBuffer(%s_buffer, %s)' %(arg, arg))
216+
217+
lines.append(' var __kernel = __webclgl.createKernel( __program__ );')
218+
for i,arg in enumerate(args):
219+
lines.append(' __kernel.setKernelArg(%s, %s_buffer)' %(i, arg))
220+
221+
lines.append(' __kernel.compile()')
222+
lines.append(' __webclgl.enqueueNDRangeKernel(__kernel, return_buffer)')
223+
lines.append(' return __webclgl.enqueueReadBuffer_Float( return_buffer )')
224+
lines.append('} // end of wrapper')
225+
226+
return '\n'.join(lines)
227+
228+
elif len(node.decorator_list)==1 and not ( isinstance(node.decorator_list[0], ast.Call) and node.decorator_list[0].func.id not in self.special_decorators ):
177229
dec = self.visit(node.decorator_list[0])
178230
buffer = self.indent() + '%s.%s = function(%s) {\n' % (dec,node.name, ', '.join(args))
179231

@@ -605,6 +657,16 @@ def visit_For(self, node):
605657
above works because [...] returns the internal Array of mylist
606658
607659
'''
660+
if self._glsl:
661+
target = self.visit(node.target)
662+
iter = self.visit(node.iter.args[0])
663+
lines = ['for (int %s; %s < %s; %s++) {' %(target, target, iter, target)]
664+
for b in node.body:
665+
lines.append( self.visit(b) )
666+
lines.append( '}' )
667+
return '\n'.join(lines)
668+
669+
608670
self._iter_id += 1
609671
iname = '__iter%s' %self._iter_id
610672
index = '__idx%s' %self._iter_id

pythonjs/runtime/dart_builtins.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ def __init__(self, items):
1515
self[...] = new( List() )
1616
self.length = items.length
1717

18-
#vec = new( Float32x4.zero() )
19-
#self[...].add( vec )
20-
2118
i = 0; s = 0
2219
while i < items.length:
2320
x = items[i]
@@ -42,7 +39,6 @@ def __getitem__(self, index):
4239
def __setitem__(self, index, value):
4340
if index < 0: index = self.length + index
4441

45-
#float32x4 vec = self[...][ index // 4 ]
4642
vec = self[...][ index // 4 ]
4743
lane = index % 4
4844
if lane == 0: vec = vec.withX(value)

pythonjs/typedpython.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,45 @@
1-
whitespace = [' ', '\t']
1+
types = ['str', 'list', 'dict']
2+
3+
glsl_types = ['float*', 'vec2']
4+
glsl_aliases = ['floatPOINTER']
5+
types.extend( glsl_types )
6+
types.extend( glsl_aliases )
7+
28
native_number_types = ['int', 'float', 'double'] ## float and double are the same
39
simd_types = ['float32x4', 'int32x4']
410
vector_types = ['float32vec']
511
vector_types.extend( simd_types )
612
number_types = ['long'] ## requires https://github.com/dcodeIO/Long.js
713
number_types.extend( native_number_types )
814

9-
types = ['str', 'list', 'dict']
1015
types.extend( number_types)
1116
types.extend( vector_types )
1217

1318

19+
__whitespace = [' ', '\t']
20+
1421
def transform_source( source, strip=False ):
1522
output = []
1623
for line in source.splitlines():
1724
a = []
18-
for char in line:
19-
if a and char in whitespace:
25+
for i,char in enumerate(line):
26+
nextchar = None
27+
j = i+1
28+
while j < len(line):
29+
nextchar = line[j]
30+
if nextchar.strip(): break
31+
j += 1
32+
33+
if a and char in __whitespace:
2034
b = ''.join(a)
2135
b = b.strip()
22-
if b in types:
36+
if b in types and nextchar != '=':
2337
if strip:
2438
a = a[ : -len(b) ]
2539
else:
40+
if a[-1]=='*':
41+
a.pop()
42+
a.append('POINTER')
2643
a.append('=')
2744
a.append( char )
2845
else:

regtests/run.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,13 @@ def __call__(self, name):
348348
return lambda f: f
349349
webworker = webworker()
350350
351+
class glsl(object):
352+
def __enter__(self, *args): pass
353+
def __exit__(self, *args): pass
354+
def __call__(self, name):
355+
return lambda f: f
356+
glsl = glsl()
357+
351358
try:
352359
import numpy
353360
except:

regtests/webclgl/hello_gpu.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""gpu test"""
2+
3+
def main():
4+
with glsl:
5+
def main(buffA, buffB, num):
6+
float* buffA
7+
float* buffB
8+
float num
9+
vec2 n = get_global_id()
10+
float result = 0.0
11+
for i in range(1000):
12+
result = sqrt(result + A[n] + B[n] + float(i))
13+
#out_float = result ## translator should take care of this?
14+
return result

0 commit comments

Comments
 (0)