Godot Version
4.7
Question
I want to create a “photobooth” node that can capture images of a model. Basically I want to capture “screen shot” images of a scene independent of my main scene so I can then incorporate those textures back into my main scene. In my original design, I just has a Control that incorporated a subviewport to render them, but I was making so many copies of that control that some users are reporting errors that the graphics system is running out of SAMPLER ids.
ERROR: Cannot create uniform set because there's not enough room in the SAMPLERS descriptors heap.
Please increase the value of the rendering/rendering_device/d3d12/max_sampler_descriptors project setting.
at: (drivers/d3d12/rendering_device_driver_d3d12.cpp:3431)
I was thinking of redesigning this so that instead of creating one subviewport per control, I instead had a global node that had a subviewport attached and I could just send requests to it and it could render the frame I need and then just send the result back. However, I’m not sure how I would be able to tell when it’s finished rendering the frame. I could have my node set up the scene the way I want it in my subviewport and set the subviewport’s render_target_update_mode to UPDATE_ONCE, but then how do I tell when rendering is finished and I can return the subviewport’s texture?
This is the code I have in mind so far:
extends Node
class_name MaterialThumbnailGenerator
@onready var subviewport:SubViewport = %SubViewport
@onready var material_preview_scene:MaterialPreviewScene = %material_preview_scene
class Request extends Resource:
var material:Material
var mesh_type:MaterialPreviewScene.MeshType
var size:Vector2i
var callback:Callable
var queue:Array[Request]
var mutex:Mutex = Mutex.new()
var cur_request:Request
func generate_thumbnail(material:Material, mesh_type:MaterialPreviewScene.MeshType, size:Vector2i, callback:Callable):
var request:Request = Request.new()
request.material = material
request.mesh_type = mesh_type
request.size = size
request.callback = callback
mutex.lock()
queue.push_front(request)
mutex.unlock()
func _process(delta: float) -> void:
if !cur_request:
if queue.is_empty():
return
mutex.lock()
cur_request = queue.pop_back()
subviewport.size = cur_request.size
subviewport.render_target_update_mode = SubViewport.UPDATE_ONCE
material_preview_scene.mesh_type = cur_request.mesh_type
material_preview_scene.display_material = cur_request.material
mutex.unlock()
#Wait for image to be ready
#await ???
mutex.lock()
var img:Image = subviewport.get_texture().get_image()
var result:ImageTexture = ImageTexture.create_from_image(img)
cur_request.callback.call(result)
cur_request = null
mutex.unlock()