11import ctypes , time , platform
22import xxhash
33import bpy
4+ import gpu
45from mathutils import Vector , Matrix , Quaternion
56from Malt import Scene
67from Malt .Pipeline import SHADER_DIR
@@ -42,15 +43,20 @@ def __init__(self, *args, **kwargs):
4243 self .bridge = MaltPipeline .get_bridge ()
4344 self .bridge_id = self .bridge .get_viewport_id () if self .bridge else None
4445 self .last_frame_time = 0
46+ self .render_backend = gpu .platform .backend_type_get ()
4547
4648 def __del__ (self ):
4749 try :
4850 self .bridge .free_viewport_id (self .bridge_id )
4951 self .bridge = None
5052 except :
51- # Sometimes Blender seems to call the destructor on unitialiazed instances (???)
53+ # Sometimes Blender seems to call the destructor on un-initialized instances (???)
54+ pass
55+ try :
56+ super ().__del__ ()
57+ except AttributeError :
58+ # Quiet __del__ not being defined on bpy.types.RenderEngine
5259 pass
53- super ().__del__ ()
5460
5561 def get_scene (self , context , depsgraph , request_scene_update , overrides ):
5662 if request_scene_update == True :
@@ -357,36 +363,57 @@ def view_draw(self, context, depsgraph):
357363 if region .type == 'UI' :
358364 region .tag_redraw ()
359365
360- fbo = GL .gl_buffer (GL .GL_INT , 1 )
361- GL .glGetIntegerv (GL .GL_FRAMEBUFFER_BINDING , fbo )
362-
363- data_format = GL .GL_FLOAT
364- texture_format = GL .GL_RGBA32F
365- if self .bridge .viewport_bit_depth == 8 :
366- data_format = GL .GL_UNSIGNED_BYTE
367- texture_format = GL .GL_RGBA8
368- if GL .glGetInternalformativ (GL .GL_TEXTURE_2D , texture_format , GL .GL_READ_PIXELS , 1 ) != GL .GL_ZERO :
369- data_format = GL .glGetInternalformativ (GL .GL_TEXTURE_2D , texture_format , GL .GL_TEXTURE_IMAGE_TYPE , 1 )
370- elif self .bridge .viewport_bit_depth == 16 :
371- data_format = GL .GL_HALF_FLOAT
372- texture_format = GL .GL_RGBA16F
373-
374- try :
375- render_texture = Texture (resolution , texture_format , data_format , pixels .buffer (),
376- mag_filter = mag_filter , pixel_format = GL .GL_RGBA )
377- except :
378- # Fallback to unsigned byte, just in case (matches Server behavior)
379- render_texture = Texture (resolution , GL .GL_RGBA8 , GL .GL_UNSIGNED_BYTE , pixels .buffer (),
380- mag_filter = mag_filter )
381-
382366 global DISPLAY_DRAW
383- if DISPLAY_DRAW is None :
384- DISPLAY_DRAW = DisplayDraw ()
385- DISPLAY_DRAW .draw (fbo , render_texture )
367+ if self .render_backend == 'OPENGL' :
368+ fbo = GL .gl_buffer (GL .GL_INT , 1 )
369+ GL .glGetIntegerv (GL .GL_FRAMEBUFFER_BINDING , fbo )
370+
371+ data_format = GL .GL_FLOAT
372+ texture_format = GL .GL_RGBA32F
373+ if self .bridge .viewport_bit_depth == 8 :
374+ data_format = GL .GL_UNSIGNED_BYTE
375+ texture_format = GL .GL_RGBA8
376+ if GL .glGetInternalformativ (GL .GL_TEXTURE_2D , texture_format , GL .GL_READ_PIXELS , 1 ) != GL .GL_ZERO :
377+ data_format = GL .glGetInternalformativ (GL .GL_TEXTURE_2D , texture_format , GL .GL_TEXTURE_IMAGE_TYPE , 1 )
378+ elif self .bridge .viewport_bit_depth == 16 :
379+ data_format = GL .GL_HALF_FLOAT
380+ texture_format = GL .GL_RGBA16F
381+
382+ try :
383+ render_texture = Texture (resolution , texture_format , data_format , pixels .buffer (),
384+ mag_filter = mag_filter , pixel_format = GL .GL_RGBA )
385+ except :
386+ # Fallback to unsigned byte, just in case (matches Server behavior)
387+ render_texture = Texture (resolution , GL .GL_RGBA8 , GL .GL_UNSIGNED_BYTE , pixels .buffer (),
388+ mag_filter = mag_filter )
389+
390+ if DISPLAY_DRAW is None :
391+ DISPLAY_DRAW = DisplayDrawGL ()
392+ DISPLAY_DRAW .draw (fbo , render_texture )
393+ else :
394+ import gpu
395+ data_size = len (pixels )
396+ w ,h = resolution
397+ if self .bridge .viewport_bit_depth == 8 :
398+ data_size = data_size // 4
399+ h = h // 4
400+ elif self .bridge .viewport_bit_depth == 16 :
401+ data_size = data_size // 2
402+ h = h // 2
403+ data_format = 'FLOAT' #Pretend we are uploading float data, since it's the only supported format.
404+ texture_format = 'RGBA32F'
405+ data_as_float = (ctypes .c_float * data_size ).from_address (pixels ._buffer .data )
406+ buffer = gpu .types .Buffer (data_format , data_size , data_as_float )
407+ render_texture = gpu .types .GPUTexture ((w , h ), format = texture_format , data = buffer )
408+
409+ if DISPLAY_DRAW is None :
410+ DISPLAY_DRAW = DisplayDrawGPU ()
411+ DISPLAY_DRAW .draw (self .bridge .viewport_bit_depth , resolution , render_texture )
412+
386413
387414DISPLAY_DRAW = None
388415
389- class DisplayDraw ():
416+ class DisplayDrawGL ():
390417 def __init__ (self ):
391418 positions = [
392419 1.0 , 1.0 , 1.0 ,
@@ -412,6 +439,108 @@ def draw(self, fbo, texture):
412439 self .shader .bind ()
413440 self .quad .draw ()
414441
442+ class DisplayDrawGPU ():
443+ def __init__ (self ):
444+ import gpu
445+ from gpu_extras .batch import batch_for_shader
446+
447+ vertex_src = """
448+ void main()
449+ {
450+ IO_POSITION = IN_POSITION * vec3(1000, 1000, 0.5);
451+ gl_Position = vec4(IO_POSITION, 1);
452+ }
453+ """
454+
455+ pixel_src = """
456+ vec3 srgb_to_linear(vec3 srgb)
457+ {
458+ vec3 low = srgb / 12.92;
459+ vec3 high = pow((srgb + 0.055)/1.055, vec3(2.4));
460+ return mix(low, high, greaterThan(srgb, vec3(0.04045)));
461+ }
462+
463+ void main()
464+ {
465+ vec2 uv = IO_POSITION.xy * 0.5 + 0.5;
466+
467+ int divisor = 32 / bit_depth;
468+
469+ ivec2 output_texel = ivec2(vec2(output_res) * uv);
470+ int output_texel_linear = output_texel.y * output_res.x + output_texel.x;
471+
472+ int texel_linear_read = output_texel_linear / divisor;
473+ ivec2 texel_read = ivec2(texel_linear_read % output_res.x, texel_linear_read / output_res.x);
474+ int sub_texel_index = output_texel_linear % divisor;
475+
476+ vec4 texel_value = texelFetch(input_texture, texel_read, 0);
477+
478+ if(bit_depth == 32)
479+ {
480+ OUT_COLOR = texel_value;
481+ }
482+ else if(bit_depth == 16)
483+ {
484+ vec2 sub_texel_value = sub_texel_index == 0 ? texel_value.xy : texel_value.zw;
485+
486+ uint packed_xy = floatBitsToUint(sub_texel_value.x);
487+ uint packed_yz = floatBitsToUint(sub_texel_value.y);
488+
489+ OUT_COLOR.rg = unpackHalf2x16(packed_xy);
490+ OUT_COLOR.ba = unpackHalf2x16(packed_yz);
491+ }
492+ else if(bit_depth == 8)
493+ {
494+ float sub_texel_value = texel_value[sub_texel_index];
495+ uint packed_value = floatBitsToUint(sub_texel_value);
496+ OUT_COLOR = unpackUnorm4x8(packed_value);
497+ OUT_COLOR.rgb = srgb_to_linear(OUT_COLOR.rgb);
498+ }
499+ else{
500+ OUT_COLOR = vec4(1,1,0,1);
501+ }
502+ }
503+ """
504+
505+ self .iface = gpu .types .GPUStageInterfaceInfo ("IFace" )
506+ self .iface .smooth ('VEC3' , "IO_POSITION" )
507+
508+ self .sh_info = gpu .types .GPUShaderCreateInfo ()
509+ self .sh_info .push_constant ('INT' , "bit_depth" )
510+ self .sh_info .push_constant ('IVEC2' , "output_res" )
511+ self .sh_info .sampler (0 , 'FLOAT_2D' , "input_texture" )
512+ self .sh_info .vertex_source (vertex_src )
513+ self .sh_info .vertex_in (0 , 'VEC3' , "IN_POSITION" )
514+ self .sh_info .vertex_out (self .iface )
515+ self .sh_info .fragment_source (pixel_src )
516+ self .sh_info .fragment_out (0 , 'VEC4' , "OUT_COLOR" )
517+
518+ self .shader = gpu .shader .create_from_info (self .sh_info )
519+
520+ positions = [
521+ ( 1.0 , 1.0 , 1.0 ),
522+ ( 1.0 , - 1.0 , 1.0 ),
523+ (- 1.0 , - 1.0 , 1.0 ),
524+ (- 1.0 , 1.0 , 1.0 ),
525+ ]
526+ indices = [
527+ (0 , 1 , 3 ),
528+ (1 , 2 , 3 ),
529+ ]
530+
531+ self .quad = batch_for_shader (
532+ self .shader , 'TRIS' ,
533+ {"IN_POSITION" : positions },
534+ indices = indices
535+ )
536+
537+ def draw (self , bit_depth , resolution , texture ):
538+ self .shader .bind ()
539+ self .shader .uniform_int ("bit_depth" , bit_depth )
540+ self .shader .uniform_int ("output_res" , resolution )
541+ self .shader .uniform_sampler ("input_texture" , texture )
542+ self .quad .draw (self .shader )
543+
415544
416545class OT_MaltRenderDocCapture (bpy .types .Operator ):
417546 bl_idname = "wm.malt_renderdoc_capture"
0 commit comments