Skip to content

Commit 76e1a98

Browse files
author
hartsantler
committed
glsl/webclgl backend: function can now accept array or number arguments,
`with glsl as myfunc:` is now required to set the name of the wrapper function. the wrapper function will take an additonal argument that sets the `offset` range.
1 parent 1558601 commit 76e1a98

3 files changed

Lines changed: 59 additions & 29 deletions

File tree

pythonjs/python_to_pythonjs.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3258,13 +3258,21 @@ def visit_With(self, node):
32583258
global writer
32593259

32603260
if isinstance( node.context_expr, Name ) and node.context_expr.id == 'glsl':
3261+
if not isinstance(node.optional_vars, ast.Name):
3262+
raise SyntaxError( self.format_error('wrapper function name must be given: `with glsl as myfunc:`') )
3263+
main_func = None
32613264
writer.inline_glsl = True
32623265
self._with_glsl = True
32633266
for b in node.body:
3267+
if isinstance(b, ast.FunctionDef) and b.name == 'main':
3268+
main_func = True
3269+
writer.write('@__glsl__.%s' %node.optional_vars.id)
32643270
a = self.visit(b)
32653271
if a: writer.write(a)
32663272
self._with_glsl = False
32673273
writer.inline_glsl = False
3274+
if not main_func:
3275+
raise SyntaxError( self.format_error('a function named `main` must be defined as the entry point for the shader program') )
32683276

32693277
elif isinstance( node.context_expr, ast.Call ) and isinstance(node.context_expr.func, ast.Name) and node.context_expr.func.id == 'rpc':
32703278
self._with_rpc = self.visit( node.context_expr.args[0] )

pythonjs/pythonjs.py

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def __init__(self, requirejs=True, insert_runtime=True, webworker=False, functio
3333

3434
self.special_decorators = set(['__typedef__', '__glsl__'])
3535
self._glsl = False
36+
self._has_glsl = False
3637

3738
def indent(self): return ' ' * self._indent
3839
def push(self): self._indent += 1
@@ -67,13 +68,15 @@ def visit_AugAssign(self, node):
6768
return a
6869

6970
def visit_Module(self, node):
71+
header = []
72+
lines = []
73+
7074
if self._requirejs and not self._webworker:
71-
lines = [
75+
header.extend([
7276
'define( function(){',
7377
'__module__ = {}'
74-
]
75-
else:
76-
lines = []
78+
])
79+
7780

7881
if self._insert_runtime:
7982
dirname = os.path.dirname(os.path.abspath(__file__))
@@ -95,7 +98,10 @@ def visit_Module(self, node):
9598
lines.append( 'return __module__')
9699
lines.append('}) //end requirejs define')
97100

98-
#return '\n'.join(lines)
101+
if self._has_glsl:
102+
header.append( 'var __shader__ = []' )
103+
104+
lines = header + lines
99105
## fixed by Foxboron
100106
return '\n'.join(l if isinstance(l,str) else l.encode("utf-8") for l in lines)
101107

@@ -184,23 +190,32 @@ def _visit_call_helper_var_glsl(self, node):
184190

185191
def _visit_function(self, node):
186192
glsl = False
193+
glsl_wrapper_name = False
187194
args_typedefs = {}
188195
for decor in node.decorator_list:
189196
if isinstance(decor, ast.Name) and decor.id == '__glsl__':
190197
glsl = True
198+
elif isinstance(decor, ast.Attribute) and isinstance(decor.value, ast.Name) and decor.value.id == '__glsl__':
199+
glsl_wrapper_name = decor.attr
191200
elif isinstance(decor, ast.Call) and isinstance(decor.func, ast.Name) and decor.func.id == '__typedef__':
192201
for key in decor.keywords:
193202
args_typedefs[ key.arg ] = key.value.id
194203

195204
args = self.visit(node.args)
196205

197206
if glsl:
198-
lines = ['var __program__ = [];']
207+
is_main = node.name == 'main'
208+
self._has_glsl = True ## writes `__shader__ = []` in header
209+
lines = []
199210
x = []
200211
for i,arg in enumerate(args):
201212
assert arg in args_typedefs
202213
x.append( '%s %s' %(args_typedefs[arg].replace('POINTER', '*'), arg) )
203-
lines.append( '__program__.push("void main( %s ) {");' %', '.join(x) )
214+
215+
if is_main:
216+
lines.append( '__shader__.push("void main( %s ) {");' %', '.join(x) )
217+
else: ## TODO return type
218+
lines.append( '__shader__.push("void %s( %s ) {");' %(node.name, ', '.join(x)) )
204219

205220
self.push()
206221
self._glsl = True
@@ -209,27 +224,30 @@ def _visit_function(self, node):
209224
continue
210225
else:
211226
for sub in self.visit(child).splitlines():
212-
lines.append( '__program__.push("%s");' %(self.indent()+sub) )
227+
lines.append( '__shader__.push("%s");' %(self.indent()+sub) )
213228
self._glsl = False
214229
#buffer += '\n'.join(body)
215230
self.pull()
216-
lines.append('\n__program__.push("%s}");' %self.indent())
217-
lines.append('function call_webclgl_program( %s ) {' %','.join(args))
218-
lines.append(' var offset = 0') ## TODO data range, 0 allows 0-1.0
219-
lines.append(' var __webclgl = new WebCLGL()')
220-
lines.append(' var return_buffer = __webclgl.createBuffer(1, "FLOAT", offset)') ## TODO length of return buffer
221-
for arg in args:
222-
lines.append(' var %s_buffer = __webclgl.createBuffer(%s.length, "FLOAT", offset)' %(arg,arg))
223-
lines.append(' __webclgl.enqueueWriteBuffer(%s_buffer, %s)' %(arg, arg))
224-
225-
lines.append(' var __kernel = __webclgl.createKernel( __program__ );')
226-
for i,arg in enumerate(args):
227-
lines.append(' __kernel.setKernelArg(%s, %s_buffer)' %(i, arg))
228-
229-
lines.append(' __kernel.compile()')
230-
lines.append(' __webclgl.enqueueNDRangeKernel(__kernel, return_buffer)')
231-
lines.append(' return __webclgl.enqueueReadBuffer_Float( return_buffer )')
232-
lines.append('} // end of wrapper')
231+
lines.append('__shader__.push("%s}");' %self.indent())
232+
233+
if is_main:
234+
lines.append('function %s( %s, __offset ) {' %(glsl_wrapper_name, ','.join(args)) )
235+
lines.append(' __offset = __offset || 0') ## note by default: 0 allows 0-1.0
236+
lines.append(' var __webclgl = new WebCLGL()')
237+
lines.append(' var __kernel = __webclgl.createKernel( __shader__ );')
238+
239+
lines.append(' var return_buffer = __webclgl.createBuffer(1, "FLOAT", __offset)') ## TODO length of return buffer
240+
for i,arg in enumerate(args):
241+
lines.append(' if (%s instanceof Array) {' %arg)
242+
lines.append(' var %s_buffer = __webclgl.createBuffer(%s.length, "FLOAT", __offset)' %(arg,arg))
243+
lines.append(' __webclgl.enqueueWriteBuffer(%s_buffer, %s)' %(arg, arg))
244+
lines.append(' __kernel.setKernelArg(%s, %s_buffer)' %(i, arg))
245+
lines.append(' } else { __kernel.setKernelArg(%s, %s) }' %(i, arg))
246+
247+
lines.append(' __kernel.compile()')
248+
lines.append(' __webclgl.enqueueNDRangeKernel(__kernel, return_buffer)')
249+
lines.append(' return __webclgl.enqueueReadBuffer_Float( return_buffer )')
250+
lines.append('} // end of wrapper')
233251

234252
return '\n'.join(lines)
235253

regtests/webclgl/hello_gpu.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
"""gpu test"""
22

33
def main():
4-
with glsl:
4+
with glsl as myfunc:
55
def main(buffA, buffB, num):
66
float* buffA
77
float* buffB
88
float num
9-
vec2 n = get_global_id()
9+
vec2 n = get_global_id() ## WebCL API
1010
float result = 0.0
1111
for i in range(1000):
1212
result = sqrt(result + A[n] + B[n] + float(i))
13-
#out_float = result ## translator should take care of this?
14-
return result
13+
return result * num
14+
15+
A = [1,2,3]
16+
B = [4,5,6]
17+
res = myfunc( A, B, 2.0 )
18+
print(res)

0 commit comments

Comments
 (0)