Vipitis commited on
Commit
d4e43d6
1 Parent(s): 9d03b07

remove wgpu, add documentation

Browse files
Files changed (2) hide show
  1. app.py +56 -173
  2. requirements.txt +0 -3
app.py CHANGED
@@ -1,137 +1,9 @@
1
  import gradio as gr
2
  from transformers import pipeline, AutoModelForCausalLM, AutoTokenizer
3
- from wgpu.utils.shadertoy import *
4
- from wgpu.gui.offscreen import WgpuCanvas as OffscreenCanvas, run as run_offscreen
5
- import wgpu
6
- import time
7
- import ctypes
8
  import datasets
9
- from PIL import Image
10
  import asyncio
11
  import numpy as np
12
 
13
- # reimplement the Shadertoy class with offscreen canvas!
14
- class ShadertoyCustom(Shadertoy):
15
- def __init__(self, shader_code, resolution=(800, 450), canvas_class=WgpuCanvas, run_fn=run):
16
- self._canvas_class = canvas_class
17
- self._fun_fn = run_fn
18
- super().__init__(shader_code, resolution)
19
- self._uniform_data = UniformArray(
20
- ("mouse", "f", 4),
21
- ("resolution", "f", 3),
22
- ("time", "f", 1),
23
- ("time_delta", "f", 1),
24
- ("frame", "I", 1),
25
- )
26
-
27
- self._shader_code = shader_code
28
- self._uniform_data["resolution"] = resolution + (1,)
29
-
30
- self._prepare_render()
31
- self._bind_events()
32
-
33
- def _prepare_render(self):
34
- import wgpu.backends.rs # noqa
35
-
36
- self._canvas = self._canvas_class(title="Shadertoy", size=self.resolution, max_fps=60)
37
-
38
- adapter = wgpu.request_adapter(
39
- canvas=self._canvas, power_preference="high-performance"
40
- )
41
- self._device = adapter.request_device()
42
-
43
- self._present_context = self._canvas.get_context()
44
-
45
- # We use "bgra8unorm" not "bgra8unorm-srgb" here because we want to let the shader fully control the color-space.
46
- self._present_context.configure(
47
- device=self._device, format=wgpu.TextureFormat.bgra8unorm
48
- )
49
-
50
- shader_type = self.shader_type
51
- if shader_type == "glsl":
52
- vertex_shader_code = vertex_code_glsl
53
- frag_shader_code = (
54
- builtin_variables_glsl + self.shader_code + fragment_code_glsl
55
- )
56
- elif shader_type == "wgsl":
57
- vertex_shader_code = vertex_code_wgsl
58
- frag_shader_code = (
59
- builtin_variables_wgsl + self.shader_code + fragment_code_wgsl
60
- )
61
-
62
- vertex_shader_program = self._device.create_shader_module(
63
- label="triangle_vert", code=vertex_shader_code
64
- )
65
- frag_shader_program = self._device.create_shader_module(
66
- label="triangle_frag", code=frag_shader_code
67
- )
68
-
69
- self._uniform_buffer = self._device.create_buffer(
70
- size=self._uniform_data.nbytes,
71
- usage=wgpu.BufferUsage.UNIFORM | wgpu.BufferUsage.COPY_DST,
72
- )
73
-
74
- bind_group_layout = self._device.create_bind_group_layout(
75
- entries=binding_layout
76
- )
77
-
78
- self._bind_group = self._device.create_bind_group(
79
- layout=bind_group_layout,
80
- entries=[
81
- {
82
- "binding": 0,
83
- "resource": {
84
- "buffer": self._uniform_buffer,
85
- "offset": 0,
86
- "size": self._uniform_data.nbytes,
87
- },
88
- },
89
- ],
90
- )
91
-
92
- self._render_pipeline = self._device.create_render_pipeline(
93
- layout=self._device.create_pipeline_layout(
94
- bind_group_layouts=[bind_group_layout]
95
- ),
96
- vertex={
97
- "module": vertex_shader_program,
98
- "entry_point": "main",
99
- "buffers": [],
100
- },
101
- primitive={
102
- "topology": wgpu.PrimitiveTopology.triangle_list,
103
- "front_face": wgpu.FrontFace.ccw,
104
- "cull_mode": wgpu.CullMode.none,
105
- },
106
- depth_stencil=None,
107
- multisample=None,
108
- fragment={
109
- "module": frag_shader_program,
110
- "entry_point": "main",
111
- "targets": [
112
- {
113
- "format": wgpu.TextureFormat.bgra8unorm,
114
- "blend": {
115
- "color": (
116
- wgpu.BlendFactor.one,
117
- wgpu.BlendFactor.zero,
118
- wgpu.BlendOperation.add,
119
- ),
120
- "alpha": (
121
- wgpu.BlendFactor.one,
122
- wgpu.BlendFactor.zero,
123
- wgpu.BlendOperation.add,
124
- ),
125
- },
126
- },
127
- ],
128
- },
129
- )
130
-
131
- def show(self, time: float = 0.0):
132
- self._canvas.request_draw(self._draw_frame)
133
- self._fun_fn()
134
-
135
  def make_script(shader_code):
136
  # code copied and fixed(escaping single quotes to double quotes!!!) from https://webglfundamentals.org/webgl/webgl-shadertoy.html
137
  script = ("""
@@ -358,23 +230,54 @@ def make_iframe(shader_code): #keep a single function?
358
  return f"""<iframe width="640" height="512" srcdoc=\'{script}\' allowfullscreen></iframe>"""
359
 
360
 
361
- text = """
362
  # Welcome to the interactive shadercoding demo.
363
- ## (WIP), you can try and explore the dataset a bit right now. (frames are rendered on the fly, not part of the dataset(yet))
364
-
365
- This gives you access to a filtered version of the [Shadertoys](https://huggingface.co/datasets/Vipitis/Shadertoys) dataset, only shaders that const of a single pass (and have at least one fuction with a return statement) are available.
366
- In the near future there will be some buttons and sliders to generate variations of the shadercode itself, and hence get some different images.
367
- If I find an efficient way, the shaders might run in real time and be interactive.
 
 
 
 
 
 
 
 
368
 
369
- ## TODO:
 
 
 
 
 
 
 
 
 
 
 
 
 
370
  - [x] use embedded Shadertoy for reference/attribution (done, but some errors)
371
- - [] working render implementation on CPU only space (use the browser for WebGPU?, maybe via an iFrame too?) freespace uses lavapipe which works on really simple stuff.
372
  - [~] generate variations of return statements [ShaderEval task1](https://huggingface.co/spaces/Vipitis/ShaderEval) (needs to be reworked using the other parts)
373
- - [] generation history stating which function and orig/generated returns. (use State ??). do it as comments in the code?
374
  - [x] generate whole functions (seems to work quite well)
375
- - [] display errros/issues to the user
376
- - [] generate whole shaders (via prompts?)
377
- - [] accordion with generation parameters (as pipeline_kwargs?)
 
 
 
 
 
 
 
 
 
 
 
378
  """
379
 
380
  passes_dataset = datasets.load_dataset("Vipitis/Shadertoys")
@@ -389,23 +292,6 @@ GLSL_LANGUAGE = Language('./build/my-languages.so', 'glsl')
389
  parser = Parser()
390
  parser.set_language(GLSL_LANGUAGE)
391
 
392
-
393
- async def get_image(code, time= 0.0, resolution=(512, 420)):
394
- tree = parser.parse(bytes(code, "utf8"))
395
- if tree.root_node.has_error:
396
- print("ERROR in the tree, aborting.")
397
- raise gr.Error("the code seems to have issues")
398
- return None
399
- shader = ShadertoyCustom(code, resolution, OffscreenCanvas, run_offscreen) #pass offscreen canvas here.
400
- shader._uniform_data["time"] = time #set any time you want
401
- shader._canvas.request_draw(shader._draw_frame)
402
- # frame = shader._canvas.snapshot().data
403
- frame = np.asarray(shader._canvas.draw())
404
- img = Image.fromarray(frame)
405
- # remove transparent pixels
406
- img = img.convert('RGB')
407
- return img
408
-
409
  def grab_sample(sample_idx):
410
  sample_pass = all_single_passes[sample_idx]
411
  sample_code = sample_pass["code"]
@@ -454,7 +340,7 @@ def get_full_replacement(orig_code, retn_start_idx, retn_end_idx, prediction) ->
454
  variation = orig_code[:retn_start_idx] + generated + orig_code[retn_end_idx:]
455
  return variation
456
 
457
- def alter_return(orig_code, func_idx=0, pipeline=PIPE): #default pipeline can't be passed as gloabl?
458
  """
459
  Replaces the return statement of a function with a generated one.
460
  Args:
@@ -467,6 +353,9 @@ def alter_return(orig_code, func_idx=0, pipeline=PIPE): #default pipeline can't
467
  if pipeline is None:
468
  print("no pipeline found, loading default one")
469
  pipeline = _make_pipeline()
 
 
 
470
 
471
  retrns = []
472
  retrn_start_idx = orig_code.find("return")
@@ -499,7 +388,8 @@ def _line_chr2char(text, line_idx, chr_idx):
499
  char_idx += chr_idx
500
  return char_idx
501
 
502
- def alter_body(old_code, func_id, funcs_list, pipeline=PIPE):
 
503
  """
504
  Replaces the body of a function with a generated one.
505
  Args:
@@ -561,7 +451,7 @@ def construct_embed(source_url):
561
  return f'<iframe width="640" height="360" frameborder="0" src="https://www.shadertoy.com/embed/{shader_id}?gui=true&t=0&paused=true&muted=true" allowfullscreen></iframe>'
562
 
563
  with gr.Blocks() as site:
564
- text_md = gr.Markdown(text)
565
  model_cp = gr.Textbox(value="Vipitis/santacoder-finetuned-Shadertoys-fine", label="Model Checkpoint (Enter to load!)", interactive=True)
566
  sample_idx = gr.Slider(minimum=0, maximum=num_samples, value=3211, label="pick sample from dataset", step=1.0)
567
  func_dropdown = gr.Dropdown(label="chose a function to modify") #breaks if I add a string in before that?
@@ -569,31 +459,24 @@ with gr.Blocks() as site:
569
  gen_return_button = gr.Button("generate a alternate return statement", label="generate return")
570
  gen_func_button = gr.Button("generate an alternate function body", label="generate function")
571
  # update_funcs_button = gr.Button("update functions", label="update functions")
572
- render_button = gr.Button("render frame0 (can carsh the sapce on invalid shadercode)",label="render frame")
573
- time_slider = gr.Slider(minimum=0, maximum=10, value=0, label="time (update on release, also used to pick other functions as a bodge)", step=0.02)
574
  with gr.Row():
575
  with gr.Column():
576
  source_embed = gr.HTML('<iframe width="640" height="360" frameborder="0" src="https://www.shadertoy.com/embed/WsBcWV?gui=true&t=0&paused=true&muted=true" allowfullscreen></iframe>', label="How this shader originally renders")
577
- rendered_frame = gr.Image(shape=(512, 420), label=f"rendered frame preview", type="pil") #colors are messed up?
578
  our_embed = gr.HTML(label="glsl render of the current code")
579
  sample_code = gr.Code(label="Current Code (will update changes you generate)", language=None)
580
-
 
 
581
  sample_pass = gr.State(value={})
582
  pipe = gr.State(value=PIPE)
583
  funcs = gr.State(value=[])
584
  # hist_state = gr.State(Value={})
585
  # history_table = gr.JSON()
586
 
587
- model_cp.submit(fn=_make_pipeline, inputs=[model_cp], outputs=[pipe])
588
- sample_idx.release(fn=grab_sample, inputs=[sample_idx], outputs=[sample_pass, sample_code, source_embed])
589
- # sample_idx.release(fn=list_dropdown, inputs=[sample_code], outputs=[funcs, func_dropdown]) #use multiple event handles to call other functions! seems to not work really well. always messes up
590
- gen_return_button.click(fn=alter_return, inputs=[sample_code, time_slider, pipe], outputs=[sample_code])
591
  gen_func_button.click(fn=alter_body, inputs=[sample_code, func_dropdown, funcs, pipe], outputs=[sample_code, pipe])
592
- # run_button.click(fn=add_history, inputs=[time_slider, sample_pass, sample_code, hist_state], outputs=[history_table, hist_state])
593
- # sample_idx.release(fn=construct_embed, inputs=[sample_idx], outputs=[source_embed]) #twice to make have different outputs?
594
  sample_code.change(fn=list_dropdown, inputs=[sample_code], outputs=[funcs, func_dropdown]) # to update this after generation, so spans aren't messed up
595
  sample_code.change(fn=make_iframe, inputs=[sample_code], outputs=[our_embed]) #twice could cause issues, find better ways.
596
- time_slider.release(fn=lambda code, time: asyncio.run(get_image(code, time)), inputs=[sample_code, time_slider], outputs=rendered_frame)
597
- render_button.click(fn=lambda code: asyncio.run(get_image(code)), inputs=[sample_code], outputs=rendered_frame)
598
- # run_button.click(fn=print, inputs=[model_cp, sample_idx], outputs=output)
599
  site.launch()
 
1
  import gradio as gr
2
  from transformers import pipeline, AutoModelForCausalLM, AutoTokenizer
 
 
 
 
 
3
  import datasets
 
4
  import asyncio
5
  import numpy as np
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  def make_script(shader_code):
8
  # code copied and fixed(escaping single quotes to double quotes!!!) from https://webglfundamentals.org/webgl/webgl-shadertoy.html
9
  script = ("""
 
230
  return f"""<iframe width="640" height="512" srcdoc=\'{script}\' allowfullscreen></iframe>"""
231
 
232
 
233
+ intro_text = """
234
  # Welcome to the interactive shadercoding demo.
235
+ This gives you access to a filtered version of the [Shadertoys](https://huggingface.co/datasets/Vipitis/Shadertoys) dataset, only shaders that consist of a single pass are available.
236
+ And then lets you use code generation models to make alterations to part of the shadercode.
237
+
238
+ ## How To Use:
239
+ 1. Load any Model for [`text-generation`](https://huggingface.co/models?pipeline_tag=text-generation) and hit ENTER.
240
+ 2. Use the slider to sample a shader from the dataset.
241
+ - The original shader will be embedding on the left, click on title to get to the source.
242
+ - The shadercode will be displayed on the right, this is interactive.
243
+ - A preview of the currently displayed shadercode will be displayed on the lower left. (hover to advance time)
244
+ 3. use the dropdown to select a function to modify.
245
+ 4. press either button to make modifications to that function
246
+ 5. you can also edit the code manually.
247
+ """
248
 
249
+ outro_text ="""
250
+ ## Models to try (look at [ShaderEval](https://huggingface.co/spaces/Vipitis/ShaderEval) for an indication of how helpful they will be):
251
+ - [gpt2](https://huggingface.co/gpt2) baseline for language models, really struggles with shadercode.
252
+ - [bigscience/bloom-1b1](https://huggingface.co/bigscience/bloom-1b1) a newer and larger freely available model. Does understand a big of code.
253
+ - [codeparrot/codeparrot-small](https://huggingface.co/codeparrot/codeparrot-small) a model trained on code, but not on shadercode. Manages to graps the patterns.
254
+ - [salesforce/codegen-2B-multi](https://huggingface.co/salesforce/codegen-2B-multi) a larger model that indicates some potential.
255
+ - [bigcode/santacoder](https://huggingface.co/bigcode/santacoder) a model trained on subset of [TheStack](https://huggingface.co/datasets/bigcode/the-stack), struggles with shadercode.
256
+ - [Vipitis/santacoder-finetuned-the-stack-glsl](https://huggingface.co/Vipitis/santacoder-finetuned-the-stack-glsl) fine-tuned by me on the glsl subset of [TheStack](https://huggingface.co/datasets/bigcode/the-stack), is an improvement.
257
+ - [Vipitis/santacoder-finetuned-Shadertoys](https://huggingface.co/Vipitis/santacoder-finetuned-Shadertoys) fine-tuned by me on whole shaders from [Shadertoys](https://huggingface.co/datasets/Vipitis/Shadertoys). Does overfit quite a bit with greedy decoding.
258
+ - [Vipitis/santacoder-finetuned-Shadertoys-fine](https://huggingface.co/Vipitis/santacoder-finetuned-Shadertoys-fine) fine-tuned by me just functions from [Shadertoys-fine](https://huggingface.co/datasets/Vipitis/Shadertoys-fine). Memorizes the exact function about half the time.
259
+ - [bigcode/starcoder](https://huggingface.co/bigcode/starcoder) a very large model which I haven't tried yet.
260
+ - **any other model you want to**
261
+
262
+ ## TODO (feel free to contribute with a [Pull-Request](https://huggingface.co/Vipitis/santacoder-finetuned-the-stack-glsl/discussions?status=open&type=pull_request)):
263
  - [x] use embedded Shadertoy for reference/attribution (done, but some errors)
264
+ - [~] working render implementation on CPU only space (as webgl via webglfundamentals, ccs needs fixing for iframe (or hijack Shadertoy iframe))
265
  - [~] generate variations of return statements [ShaderEval task1](https://huggingface.co/spaces/Vipitis/ShaderEval) (needs to be reworked using the other parts)
 
266
  - [x] generate whole functions (seems to work quite well)
267
+ - [] dropdown for model selection (from curated list or all supported models?)
268
+ - [] generation history stating which function and orig/generated returns. (use State ??). do it as comments in the code?
269
+ - [] display errros/issues to the user (raise gr.Error could be one idea, but highlighting in the code would be awesome)
270
+ - [] generate whole shaders (via prompts guidance, recursive from errors)
271
+ - [] accordion with generation parameters (as pipeline_kwargs?) look up starcoder playround and take "inspiration" from there
272
+ - [] support FIM task for better model context
273
+ - [] gradio examples
274
+
275
+ ### Notes:
276
+ - this is meant as a resource to show code generation for a "creative" task.
277
+ - the goal is not to not replace shader artists, but aims to be an assistant instead.
278
+ - the space still lacks quite a lot of features, but will continue to evolve.
279
+ - this demo can be useful to sannity check evaluation results, where the academic numbers are made.
280
+ - If you create a remix with these tools, please attribute the original creator of your starting point when sharing the results. (And perhaps share in the [discussion tab](https://huggingface.co/Vipitis/santacoder-finetuned-the-stack-glsl/discussions?status=open&type=discussion) too)
281
  """
282
 
283
  passes_dataset = datasets.load_dataset("Vipitis/Shadertoys")
 
292
  parser = Parser()
293
  parser.set_language(GLSL_LANGUAGE)
294
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  def grab_sample(sample_idx):
296
  sample_pass = all_single_passes[sample_idx]
297
  sample_code = sample_pass["code"]
 
340
  variation = orig_code[:retn_start_idx] + generated + orig_code[retn_end_idx:]
341
  return variation
342
 
343
+ def alter_return(orig_code, func_idx="0:", pipeline=PIPE): #default pipeline can't be passed as gloabl?
344
  """
345
  Replaces the return statement of a function with a generated one.
346
  Args:
 
353
  if pipeline is None:
354
  print("no pipeline found, loading default one")
355
  pipeline = _make_pipeline()
356
+
357
+ print(f"{func_idx=}")
358
+ func_idx = int(func_idx.split(":")[0].strip())
359
 
360
  retrns = []
361
  retrn_start_idx = orig_code.find("return")
 
388
  char_idx += chr_idx
389
  return char_idx
390
 
391
+
392
+ def alter_body(old_code, func_id: str, funcs_list: list, pipeline=PIPE):
393
  """
394
  Replaces the body of a function with a generated one.
395
  Args:
 
451
  return f'<iframe width="640" height="360" frameborder="0" src="https://www.shadertoy.com/embed/{shader_id}?gui=true&t=0&paused=true&muted=true" allowfullscreen></iframe>'
452
 
453
  with gr.Blocks() as site:
454
+ top_md = gr.Markdown(intro_text)
455
  model_cp = gr.Textbox(value="Vipitis/santacoder-finetuned-Shadertoys-fine", label="Model Checkpoint (Enter to load!)", interactive=True)
456
  sample_idx = gr.Slider(minimum=0, maximum=num_samples, value=3211, label="pick sample from dataset", step=1.0)
457
  func_dropdown = gr.Dropdown(label="chose a function to modify") #breaks if I add a string in before that?
 
459
  gen_return_button = gr.Button("generate a alternate return statement", label="generate return")
460
  gen_func_button = gr.Button("generate an alternate function body", label="generate function")
461
  # update_funcs_button = gr.Button("update functions", label="update functions")
 
 
462
  with gr.Row():
463
  with gr.Column():
464
  source_embed = gr.HTML('<iframe width="640" height="360" frameborder="0" src="https://www.shadertoy.com/embed/WsBcWV?gui=true&t=0&paused=true&muted=true" allowfullscreen></iframe>', label="How this shader originally renders")
 
465
  our_embed = gr.HTML(label="glsl render of the current code")
466
  sample_code = gr.Code(label="Current Code (will update changes you generate)", language=None)
467
+ bot_md = gr.Markdown(outro_text)
468
+
469
+
470
  sample_pass = gr.State(value={})
471
  pipe = gr.State(value=PIPE)
472
  funcs = gr.State(value=[])
473
  # hist_state = gr.State(Value={})
474
  # history_table = gr.JSON()
475
 
476
+ model_cp.submit(fn=_make_pipeline, inputs=[model_cp], outputs=[pipe]) # how can we trigger this on load?
477
+ sample_idx.release(fn=grab_sample, inputs=[sample_idx], outputs=[sample_pass, sample_code, source_embed])
478
+ gen_return_button.click(fn=alter_return, inputs=[sample_code, func_dropdown, pipe], outputs=[sample_code])
 
479
  gen_func_button.click(fn=alter_body, inputs=[sample_code, func_dropdown, funcs, pipe], outputs=[sample_code, pipe])
 
 
480
  sample_code.change(fn=list_dropdown, inputs=[sample_code], outputs=[funcs, func_dropdown]) # to update this after generation, so spans aren't messed up
481
  sample_code.change(fn=make_iframe, inputs=[sample_code], outputs=[our_embed]) #twice could cause issues, find better ways.
 
 
 
482
  site.launch()
requirements.txt CHANGED
@@ -1,11 +1,8 @@
1
  transformers
2
  datasets
3
  jsonlines
4
- wgpu
5
  torch
6
  pillow
7
  gradio
8
- numpy
9
- glfw
10
  jupylet
11
  tree-sitter
 
1
  transformers
2
  datasets
3
  jsonlines
 
4
  torch
5
  pillow
6
  gradio
 
 
7
  jupylet
8
  tree-sitter