From acec7a852edbdd77f02842bd5c2175be93d50e34 Mon Sep 17 00:00:00 2001 From: Lila Date: Sat, 27 Dec 2025 18:50:56 +0000 Subject: [PATCH] Fix mesa extension enabling --- mesa_warning.py | 57 +++++++++++++++++++++++++++++++++++++++ properties.py | 32 ++++++++++++++-------- renderer.py | 59 ++++++++++++++++++++++++++++++++++++++--- shader/main3d.frag.glsl | 6 ++++- shader/textures.glsl | 4 --- 5 files changed, 138 insertions(+), 20 deletions(-) create mode 100644 mesa_warning.py diff --git a/mesa_warning.py b/mesa_warning.py new file mode 100644 index 0000000..288bcbf --- /dev/null +++ b/mesa_warning.py @@ -0,0 +1,57 @@ +from bpy.types import Context, Event, Operator + + +class MesaWarningPopup(Operator): + bl_label = "Mesa Driver Limitations" + bl_idname = "dialog.f64_mesa_warning" + bl_description = "Update color management settings to help material preview accuracy" + bl_options = {"UNDO"} + + already_invoked = False # HACK: used to prevent multiple dialogs from popping up + + def invoke(self, context: Context, event: Event): + prefs = context.preferences.addons[__name__.split(".")[0]].preferences + if prefs.dont_warn_about_mesa: + return {"CANCELLED"} + if MesaWarningPopup.already_invoked: + return {"FINISHED"} + MesaWarningPopup.already_invoked = True + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context: Context): + from fast64_internal.utility import multilineLabel + + col = self.layout.column() + col.label(text="You are using Mesa drivers!", icon="MEMORY") + multilineLabel( + col, + ( + ( + "These are much more strict as opposed to commercial drivers\n" + "and do not allow us to enable helpful extensions!" + ) + ), + ) + col.alert = True + col.label(text="This will hinder your accuracy and or performance.", icon="ERROR") + col.alert = False + multilineLabel( + col, + ( + ( + 'You can add "allow_glsl_extension_directive_midshader=true" to your\n' + "blender launch arguments to bypass this restriction, or change render\n" + "device." + ) + ), + icon="INFO", + ) + + prefs = context.preferences.addons[__name__.split(".")[0]].preferences + col.prop(prefs, "dont_warn_about_mesa", text="Don't warn me again") + + def cancel(self, context: Context): + MesaWarningPopup.already_invoked = False + + def execute(self, context): + return {"FINISHED"} diff --git a/properties.py b/properties.py index fa7b35f..9a29236 100644 --- a/properties.py +++ b/properties.py @@ -1,5 +1,6 @@ import bpy -from bpy.types import PropertyGroup, Image +from bpy.types import PropertyGroup, Image, AddonPreferences +from bpy.props import BoolProperty from .globals import F64_GLOBALS @@ -163,13 +164,6 @@ def rebuild_shaders(_scene, _context): class F64RenderSettings(bpy.types.PropertyGroup): - use_atomic_rendering: bpy.props.BoolProperty( - name="Use Atomic Rendering", - default=True, - description="Atomic rendering will draw to a depth and color buffer seperately, which allows for proper blender and decal emulation.\n" - "This may cause artifacts if your GPU does not support the interlock extension", - update=rebuild_shaders, - ) sources_tab: bpy.props.BoolProperty(name="Default Sources") default_prim_color: bpy.props.FloatVectorProperty( description="Primitive Color", @@ -268,8 +262,6 @@ def draw_props(self, layout: bpy.types.UILayout, gameEditorMode: str): prop_split(layout, self, "oot_specific_room", "Specific Room") layout.separator() - if bpy.app.version >= (4, 1, 0): - layout.prop(self, "use_atomic_rendering") layout.prop(self, "always_set") layout.prop(self, "sources_tab", icon="TRIA_DOWN" if self.sources_tab else "TRIA_RIGHT") if self.sources_tab: @@ -300,5 +292,23 @@ def draw_props(self, layout: bpy.types.UILayout, gameEditorMode: str): tex_prop.draw_default_ui(texture_box, i) -class F64RenderProperties(bpy.types.PropertyGroup): +class F64RenderProperties(PropertyGroup): render_settings: bpy.props.PointerProperty(type=F64RenderSettings) + + +class F64AddonPreferences(AddonPreferences): + bl_idname = __name__.split(".")[0] + + use_atomic_rendering: bpy.props.BoolProperty( + name="Use Atomic Rendering", + default=True, + description="Atomic rendering will draw to a depth and color buffer seperately, which allows for proper blender and decal emulation.\n" + "This may cause artifacts if your GPU does not support the interlock extension", + update=rebuild_shaders, + ) + dont_warn_about_mesa: BoolProperty() + + def draw_props(self, layout: bpy.types.UILayout): + atomic_col = layout.column() + atomic_col.prop(self, "use_atomic_rendering") + atomic_col.enabled = bpy.app.version >= (4, 1, 0) diff --git a/renderer.py b/renderer.py index 57ae969..8252c95 100644 --- a/renderer.py +++ b/renderer.py @@ -1,6 +1,8 @@ from io import StringIO import math +import os import pathlib +import sys import time import bpy @@ -39,6 +41,27 @@ def materials_set_light_direction(scene): return not (scene.gameEditorMode == "SM64" and scene.fast64.sm64.matstack_fix) +def flag_enabled(flag_name: str) -> bool: + true_values = {"1", "true", "yes", "on"} + + if os.environ.get(flag_name, "").lower() in true_values: + return True + + return False + + +def check_if_under_mesa(): + vendor = gpu.platform.vendor_get() + renderer = gpu.platform.renderer_get() + version = gpu.platform.version_get() + combined = (vendor + renderer + version).lower() + return "mesa" in combined or "llvmpipe" in combined + + +def show_mesa_warning(): + bpy.ops.dialog.f64_mesa_warning("INVOKE_DEFAULT") + + class Fast64RenderEngine(bpy.types.RenderEngine): bl_idname = "FAST64_RENDER_ENGINE" bl_label = "Fast64 Renderer" @@ -72,14 +95,37 @@ def __init__(self, *args, **kwargs): # Create a 1x1 image bpy.data.images.new("f64render_missing_texture", 1, 1).pixels = MISSING_TEXTURE_COLOR - ext_list = gpu.capabilities.extensions_get() + self.is_mesa_driver = check_if_under_mesa() + if self.is_mesa_driver: + print("Mesa drivers detected!") + self.allow_glsl_extension_directive_midshader = not self.is_mesa_driver or flag_enabled( + "allow_glsl_extension_directive_midshader" + ) + if self.is_mesa_driver: + if self.allow_glsl_extension_directive_midshader: + print( + 'Sucefully bypassed Mesa restriction on GLSL extensions via "allow_glsl_extension_directive_midshader"!' + ) + else: + print("GLSL extension directives mid-shader are disabled!") + + if self.allow_glsl_extension_directive_midshader: + ext_list = gpu.capabilities.extensions_get() + else: + ext_list = [] self.shader_interlock_support = "GL_ARB_fragment_shader_interlock" in ext_list if not self.shader_interlock_support: - print("\n\nWarning: GL_ARB_fragment_shader_interlock not supported!\n\n") + print("Warning: GL_ARB_fragment_shader_interlock not supported!") + self.shader_derivative_control_support = "GL_ARB_derivative_control" in ext_list + if not self.shader_derivative_control_support: + print("Warning: GL_ARB_derivative_control not supported!") if bpy.app.version < (4, 1, 0): - print("\n\nWarning: Blender version too old! Expect limited blending emulation!\n\n") + print("Warning: Blender version too old! Expect limited blending emulation!") self.draw_range_impl = bpy.app.version >= (3, 6, 0) + if not self.allow_glsl_extension_directive_midshader and self.is_mesa_driver: + bpy.app.timers.register(show_mesa_warning) + def __del__(self): def remove_handler(handler, func): while func in handler: @@ -133,6 +179,8 @@ def init_shader(self, scene: bpy.types.Scene): shader_info.define("depth_unchanged", "depth_any") if self.shader_interlock_support: shader_info.define("USE_SHADER_INTERLOCK", "1") + if self.shader_derivative_control_support: + shader_info.define("USE_DERIVATIVE_CONTROL", "1") shader_info.define("BLEND_EMULATION", "1") # Using the already calculated view space normals instead of transforming the light direction makes # for cleaner and faster code @@ -279,7 +327,8 @@ def draw_scene(self, context, depsgraph): f64render_rs: F64RenderSettings = depsgraph.scene.f64render.render_settings always_set = f64render_rs.always_set projection_matrix, view_matrix = context.region_data.perspective_matrix, context.region_data.view_matrix - self.use_atomic_rendering = bpy.app.version >= (4, 1, 0) and f64render_rs.use_atomic_rendering + prefs = context.preferences.addons[__name__.split(".")[0]].preferences + self.use_atomic_rendering = bpy.app.version >= (4, 1, 0) and prefs.use_atomic_rendering if F64_GLOBALS.rebuild_shaders or self.shader is None: F64_GLOBALS.rebuild_shaders = False @@ -364,6 +413,8 @@ class F64RenderSettingsPanel(bpy.types.Panel): def draw(self, context): f64render_rs: F64RenderSettings = context.scene.f64render.render_settings f64render_rs.draw_props(self.layout, context.scene.gameEditorMode) + prefs = context.preferences.addons[__name__.split(".")[0]].preferences + prefs.draw_props(self.layout) def draw_render_settings(self, context: bpy.types.Context): diff --git a/shader/main3d.frag.glsl b/shader/main3d.frag.glsl index f5c0fba..0bad189 100644 --- a/shader/main3d.frag.glsl +++ b/shader/main3d.frag.glsl @@ -4,6 +4,10 @@ layout(pixel_interlock_unordered) in; #endif +#ifdef USE_DERIVATIVE_CONTROL + #extension GL_ARB_derivative_control : enable +#endif + #define DECAL_DEPTH_DELTA 100 vec3 cc_fetchColor(in int val, in vec4 shade, in vec4 comb, in float lodFraction, in vec4 texData0, in vec4 texData1) @@ -144,7 +148,7 @@ void main() vec4 ccShade = geoModeSelect(G_SHADE_SMOOTH, cc_shade_flat, cc_shade); -#ifdef GL_ARB_derivative_control +#ifdef USE_DERIVATIVE_CONTROL const vec2 dx = abs(vec2(dFdxCoarse(inputUV.x), dFdyCoarse(inputUV.x))); const vec2 dy = abs(vec2(dFdxCoarse(inputUV.y), dFdyCoarse(inputUV.y))); #else diff --git a/shader/textures.glsl b/shader/textures.glsl index ad9893e..823cf8b 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -1,7 +1,3 @@ -#ifdef GL_ARB_derivative_control - #extension GL_ARB_derivative_control : enable -#endif - vec4 quantize3Bit(in vec4 color) { return vec4(round(color.rgb * 8.0) / 8.0, step(0.5, color.a)); }