Skip to content

Commit 80484b5

Browse files
committed
Use custom shaders to workaround FLOAT limitation of GPUTexture
1 parent a15e04d commit 80484b5

File tree

2 files changed

+122
-16
lines changed

2 files changed

+122
-16
lines changed

BlenderMalt/MaltPipeline.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import os, platform, time
22
import bpy
3-
import gpu
43
from BlenderMalt.MaltUtils import malt_path_getter, malt_path_setter
54
from . import MaltMaterial, MaltMeshes, MaltTextures
65

@@ -76,13 +75,8 @@ def update_pipeline(self, context):
7675
docs_path = docs_path if os.path.exists(docs_path) else None
7776

7877
path = bpy.path.abspath(pipeline, library=self.id_data.library)
79-
80-
viewport_bit_depth = int(self.viewport_bit_depth)
81-
if gpu.platform.backend_type_get() != 'OPENGL':
82-
viewport_bit_depth = 32 # force 32 bit depth since gpu module only supports FLOAT data_format on GPUTexture
83-
8478
import Bridge
85-
bridge = Bridge.Client_API.Bridge(path, viewport_bit_depth, debug_mode, renderdoc_path, plugin_dirs, docs_path)
79+
bridge = Bridge.Client_API.Bridge(path, int(self.viewport_bit_depth), debug_mode, renderdoc_path, plugin_dirs, docs_path)
8680
from Malt.Utils import LOG
8781
LOG.info('Blender {} {} {}'.format(bpy.app.version_string, bpy.app.build_branch, bpy.app.build_hash))
8882
params = bridge.get_parameters()

BlenderMalt/MaltRenderEngine.py

Lines changed: 121 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ def view_draw(self, context, depsgraph):
359359
if region.type == 'UI':
360360
region.tag_redraw()
361361

362+
global DISPLAY_DRAW
362363
if self.render_backend == 'OPENGL':
363364
fbo = GL.gl_buffer(GL.GL_INT, 1)
364365
GL.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING, fbo)
@@ -382,24 +383,33 @@ def view_draw(self, context, depsgraph):
382383
render_texture = Texture(resolution, GL.GL_RGBA8, GL.GL_UNSIGNED_BYTE, pixels.buffer(),
383384
mag_filter=mag_filter)
384385

385-
global DISPLAY_DRAW
386386
if DISPLAY_DRAW is None:
387-
DISPLAY_DRAW = DisplayDraw()
387+
DISPLAY_DRAW = DisplayDrawGL()
388388
DISPLAY_DRAW.draw(fbo, render_texture)
389389
else:
390390
import gpu
391-
from gpu_extras.presets import draw_texture_2d
392-
data_format = 'FLOAT' # GPUTexture only supports 'FLOAT' buffer types
391+
data_size = len(pixels)
392+
w,h = resolution
393+
if self.bridge.viewport_bit_depth == 8:
394+
data_size = data_size // 4
395+
h = h // 4
396+
elif self.bridge.viewport_bit_depth == 16:
397+
data_size = data_size // 2
398+
h = h // 2
399+
data_format = 'FLOAT' #Pretend we are uploading float data, since it's the only supported format.
393400
texture_format = 'RGBA32F'
394-
#TODO do we need the sRGBConversion shader?
395-
buffer = gpu.types.Buffer(data_format, len(pixels), pixels.buffer())
396-
render_texture = gpu.types.GPUTexture(viewport_resolution, format=texture_format, data=buffer)
397-
draw_texture_2d(render_texture, (0, 0), render_texture.width, render_texture.height)
401+
data_as_float = (ctypes.c_float * data_size).from_address(pixels._buffer.data)
402+
buffer = gpu.types.Buffer(data_format, data_size, data_as_float)
403+
render_texture = gpu.types.GPUTexture((w, h), format=texture_format, data=buffer)
404+
405+
if DISPLAY_DRAW is None:
406+
DISPLAY_DRAW = DisplayDrawGPU()
407+
DISPLAY_DRAW.draw(self.bridge.viewport_bit_depth, resolution, render_texture)
398408

399409

400410
DISPLAY_DRAW = None
401411

402-
class DisplayDraw():
412+
class DisplayDrawGL():
403413
def __init__(self):
404414
positions=[
405415
1.0, 1.0, 1.0,
@@ -425,6 +435,108 @@ def draw(self, fbo, texture):
425435
self.shader.bind()
426436
self.quad.draw()
427437

438+
class DisplayDrawGPU():
439+
def __init__(self):
440+
import gpu
441+
from gpu_extras.batch import batch_for_shader
442+
443+
vertex_src = """
444+
void main()
445+
{
446+
IO_POSITION = IN_POSITION * vec3(1000, 1000, 0.5);
447+
gl_Position = vec4(IO_POSITION, 1);
448+
}
449+
"""
450+
451+
pixel_src = """
452+
vec3 srgb_to_linear(vec3 srgb)
453+
{
454+
vec3 low = srgb / 12.92;
455+
vec3 high = pow((srgb + 0.055)/1.055, vec3(2.4));
456+
return mix(low, high, greaterThan(srgb, vec3(0.04045)));
457+
}
458+
459+
void main()
460+
{
461+
vec2 uv = IO_POSITION.xy * 0.5 + 0.5;
462+
463+
int divisor = 32 / bit_depth;
464+
465+
ivec2 output_texel = ivec2(vec2(output_res) * uv);
466+
int output_texel_linear = output_texel.y * output_res.x + output_texel.x;
467+
468+
int texel_linear_read = output_texel_linear / divisor;
469+
ivec2 texel_read = ivec2(texel_linear_read % output_res.x, texel_linear_read / output_res.x);
470+
int sub_texel_index = output_texel_linear % divisor;
471+
472+
vec4 texel_value = texelFetch(input_texture, texel_read, 0);
473+
474+
if(bit_depth == 32)
475+
{
476+
OUT_COLOR = texel_value;
477+
}
478+
else if(bit_depth == 16)
479+
{
480+
vec2 sub_texel_value = sub_texel_index == 0 ? texel_value.xy : texel_value.zw;
481+
482+
uint packed_xy = floatBitsToUint(sub_texel_value.x);
483+
uint packed_yz = floatBitsToUint(sub_texel_value.y);
484+
485+
OUT_COLOR.rg = unpackHalf2x16(packed_xy);
486+
OUT_COLOR.ba = unpackHalf2x16(packed_yz);
487+
}
488+
else if(bit_depth == 8)
489+
{
490+
float sub_texel_value = texel_value[sub_texel_index];
491+
uint packed_value = floatBitsToUint(sub_texel_value);
492+
OUT_COLOR = unpackUnorm4x8(packed_value);
493+
OUT_COLOR.rgb = srgb_to_linear(OUT_COLOR.rgb);
494+
}
495+
else{
496+
OUT_COLOR = vec4(1,1,0,1);
497+
}
498+
}
499+
"""
500+
501+
self.iface = gpu.types.GPUStageInterfaceInfo("IFace")
502+
self.iface.smooth('VEC3', "IO_POSITION")
503+
504+
self.sh_info = gpu.types.GPUShaderCreateInfo()
505+
self.sh_info.push_constant('INT', "bit_depth")
506+
self.sh_info.push_constant('IVEC2', "output_res")
507+
self.sh_info.sampler(0, 'FLOAT_2D', "input_texture")
508+
self.sh_info.vertex_source(vertex_src)
509+
self.sh_info.vertex_in(0, 'VEC3', "IN_POSITION")
510+
self.sh_info.vertex_out(self.iface)
511+
self.sh_info.fragment_source(pixel_src)
512+
self.sh_info.fragment_out(0, 'VEC4', "OUT_COLOR")
513+
514+
self.shader = gpu.shader.create_from_info(self.sh_info)
515+
516+
positions=[
517+
( 1.0, 1.0, 1.0),
518+
( 1.0, -1.0, 1.0),
519+
(-1.0, -1.0, 1.0),
520+
(-1.0, 1.0, 1.0),
521+
]
522+
indices=[
523+
(0, 1, 3),
524+
(1, 2, 3),
525+
]
526+
527+
self.quad = batch_for_shader(
528+
self.shader, 'TRIS',
529+
{"IN_POSITION": positions},
530+
indices=indices
531+
)
532+
533+
def draw(self, bit_depth, resolution, texture):
534+
self.shader.bind()
535+
self.shader.uniform_int("bit_depth", bit_depth)
536+
self.shader.uniform_int("output_res", resolution)
537+
self.shader.uniform_sampler("input_texture", texture)
538+
self.quad.draw(self.shader)
539+
428540

429541
class OT_MaltRenderDocCapture(bpy.types.Operator):
430542
bl_idname = "wm.malt_renderdoc_capture"

0 commit comments

Comments
 (0)