datamosh_compositor.gd 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. @tool
  2. extends CompositorEffect
  3. class_name CompositorEffectDatamosh
  4. var context : StringName = "PreviousFrame"
  5. var texture : StringName = "texture"
  6. var refresh_frame : bool = false
  7. # Change these variables
  8. var datamosh_path : String = "res://Misc/datamosh/"
  9. var datamosh_amount : float = 0.4
  10. func _init():
  11. needs_motion_vectors = true
  12. effect_callback_type = CompositorEffect.EFFECT_CALLBACK_TYPE_POST_TRANSPARENT
  13. RenderingServer.call_on_render_thread(_initialize_compute)
  14. func _notification(what):
  15. if what == NOTIFICATION_PREDELETE:
  16. # When this is called it should be safe to clean up our shader.
  17. # If not we'll crash anyway because we can no longer call our _render_callback.
  18. if datamosh_shader.is_valid():
  19. rd.free_rid(datamosh_shader)
  20. if loop_frame_shader.is_valid():
  21. rd.free_rid(loop_frame_shader)
  22. ###############################################################################
  23. # Everything after this point is designed to run on our rendering thread
  24. var rd : RenderingDevice
  25. var datamosh_shader : RID
  26. var datamosh_pipeline : RID
  27. var loop_frame_shader : RID
  28. var loop_frame_pipeline : RID
  29. func _initialize_compute():
  30. rd = RenderingServer.get_rendering_device()
  31. if !rd:
  32. return
  33. # Create our shader
  34. var shader_file = load(datamosh_path + "datamosh.glsl")
  35. var shader_spirv: RDShaderSPIRV = shader_file.get_spirv()
  36. datamosh_shader = rd.shader_create_from_spirv(shader_spirv)
  37. datamosh_pipeline = rd.compute_pipeline_create(datamosh_shader)
  38. shader_file = load(datamosh_path + "loop_frame.glsl")
  39. shader_spirv = shader_file.get_spirv()
  40. loop_frame_shader = rd.shader_create_from_spirv(shader_spirv)
  41. loop_frame_pipeline = rd.compute_pipeline_create(loop_frame_shader)
  42. func get_image_uniform(image : RID, binding : int = 0) -> RDUniform:
  43. var uniform : RDUniform = RDUniform.new()
  44. uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE
  45. uniform.binding = binding
  46. uniform.add_id(image)
  47. return uniform
  48. func _render_callback(p_effect_callback_type, p_render_data):
  49. if Engine.is_editor_hint():
  50. return
  51. if rd and p_effect_callback_type == CompositorEffect.EFFECT_CALLBACK_TYPE_POST_TRANSPARENT:
  52. # Get our render scene buffers object, this gives us access to our render buffers.
  53. # Note that implementation differs per renderer hence the need for the cast.
  54. var render_scene_buffers : RenderSceneBuffersRD = p_render_data.get_render_scene_buffers()
  55. if render_scene_buffers:
  56. # Get our render size, this is the 3D render resolution!
  57. var size = render_scene_buffers.get_internal_size()
  58. if size.x == 0 and size.y == 0:
  59. return
  60. # We can use a compute shader here
  61. var x_groups = (size.x - 1) / 8 + 1
  62. var y_groups = (size.y - 1) / 8 + 1
  63. if render_scene_buffers.has_texture(context, texture):
  64. var tf : RDTextureFormat = render_scene_buffers.get_texture_format(context, texture)
  65. if tf.width != size.x or tf.height != size.y:
  66. # This will clear all textures for this viewport under this context
  67. render_scene_buffers.clear_context(context)
  68. else:
  69. var usage_bits : int = RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT
  70. render_scene_buffers.create_texture(context, texture, RenderingDevice.DATA_FORMAT_R8G8B8A8_SNORM, usage_bits, RenderingDevice.TEXTURE_SAMPLES_1, size, 1, 1, true, false)
  71. refresh_frame = true
  72. print("recreate")
  73. # Loop through views just in case we're doing stereo rendering. No extra cost if this is mono.
  74. var view_count = render_scene_buffers.get_view_count()
  75. for view in range(view_count):
  76. # Get the RID for our color image, we will be reading from and writing to it.
  77. var input_image = render_scene_buffers.get_color_layer(view)
  78. #var velocity_buffer = render_scene_buffers.get_velocity_layer(view)
  79. var velocity_buffer = render_scene_buffers.get_velocity_texture()
  80. var previous_texture_image = render_scene_buffers.get_texture(context, texture)
  81. var push_constant : PackedInt32Array = PackedInt32Array()
  82. push_constant.push_back(size.x)
  83. push_constant.push_back(size.y)
  84. push_constant.push_back(Time.get_ticks_msec())
  85. push_constant.push_back(int(datamosh_amount*100.0))
  86. if not refresh_frame:
  87. # Create a uniform set, this will be cached, the cache will be cleared if our viewports configuration is changed
  88. var uniform : RDUniform = get_image_uniform(input_image)
  89. var input_set = UniformSetCacheRD.get_cache(datamosh_shader, 0, [ uniform ])
  90. uniform = get_image_uniform(velocity_buffer)
  91. var velocity_set = UniformSetCacheRD.get_cache(datamosh_shader, 1, [ uniform ])
  92. uniform = get_image_uniform(previous_texture_image)
  93. var previous_set = UniformSetCacheRD.get_cache(datamosh_shader, 2, [ uniform ])
  94. # Run Datamosh compute shader
  95. var compute_list := rd.compute_list_begin()
  96. rd.compute_list_bind_compute_pipeline(compute_list, datamosh_pipeline)
  97. rd.compute_list_bind_uniform_set(compute_list, input_set, 0)
  98. rd.compute_list_bind_uniform_set(compute_list, velocity_set, 1)
  99. rd.compute_list_bind_uniform_set(compute_list, previous_set, 2)
  100. rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4)
  101. rd.compute_list_dispatch(compute_list, x_groups, y_groups, 1)
  102. rd.compute_list_end()
  103. var uniform := get_image_uniform(input_image)
  104. var input_set := UniformSetCacheRD.get_cache(datamosh_shader, 0, [ uniform ])
  105. uniform = get_image_uniform(previous_texture_image)
  106. var previous_set := UniformSetCacheRD.get_cache(datamosh_shader, 1, [ uniform ])
  107. # Run Loop Frame compute shader
  108. var compute_list := rd.compute_list_begin()
  109. rd.compute_list_bind_compute_pipeline(compute_list, loop_frame_pipeline)
  110. rd.compute_list_bind_uniform_set(compute_list, input_set, 0)
  111. rd.compute_list_bind_uniform_set(compute_list, previous_set, 1)
  112. rd.compute_list_dispatch(compute_list, x_groups, y_groups, 1)
  113. rd.compute_list_end()
  114. refresh_frame = false