Skip to content

Commit 7526dc7

Browse files
authored
Merge pull request #613 from gamingrobot/blender-gpu
Use blender gpu module to support vulkan render backend
2 parents 5a00fa9 + eb06aaf commit 7526dc7

File tree

1 file changed

+157
-28
lines changed

1 file changed

+157
-28
lines changed

BlenderMalt/MaltRenderEngine.py

Lines changed: 157 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ctypes, time, platform
22
import xxhash
33
import bpy
4+
import gpu
45
from mathutils import Vector, Matrix, Quaternion
56
from Malt import Scene
67
from 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

387414
DISPLAY_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

416545
class OT_MaltRenderDocCapture(bpy.types.Operator):
417546
bl_idname = "wm.malt_renderdoc_capture"

0 commit comments

Comments
 (0)