From cad795c8901602ace80141aeececeed6a00ba271 Mon Sep 17 00:00:00 2001 From: ykcheungab Date: Mon, 14 Sep 2026 14:57:56 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E9=98=B6=E6=AE=B5=201=20=E7=9A=84?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E6=A0=B7=E4=BE=8B=EF=BC=9A=E8=BD=AF=E4=BB=B6?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E8=A2=AB=E6=8B=92=E3=80=81=E7=9D=80=E8=89=B2?= =?UTF-8?q?=E5=99=A8=E7=BC=96=E8=AF=91=E5=A4=B1=E8=B4=A5=E5=8F=AA=E9=99=8D?= =?UTF-8?q?=E7=BA=A7=E4=B8=89=E7=BB=B4=20-=20What:=20test=5Fscene3d=5Fgl?= =?UTF-8?q?=5Fsmoke.py=20=E5=A2=9E=E5=8A=A0=E4=B8=A4=E9=A1=B9=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E6=A0=B7=E4=BE=8B=E3=80=82=E5=85=B6=E4=B8=80=EF=BC=9A?= =?UTF-8?q?=E6=8A=8A=20is=5Fsoftware=5Frenderer=20=E6=89=93=E6=A1=A9?= =?UTF-8?q?=E4=B8=BA=E7=9C=9F=E5=90=8E=E8=B0=83=E7=94=A8=20GLDispatch.bind?= =?UTF-8?q?=5Fcurrent=5Fcontext=EF=BC=8C=E6=96=AD=E8=A8=80=E6=8A=9B=20Thre?= =?UTF-8?q?eDUnsupported=E3=80=81=E8=AF=8A=E6=96=AD=E7=A0=81=E4=B8=BA=20gl?= =?UTF-8?q?=5Funsupported=E3=80=81=E4=B8=94=E5=BC=82=E5=B8=B8=E5=B8=A6?= =?UTF-8?q?=E5=9B=9E=E7=9A=84=20GLCapabilities=20=E6=98=8E=E7=A1=AE?= =?UTF-8?q?=E6=A0=87=E7=9D=80=20software=5Frenderer=3DTrue=EF=BC=88?= =?UTF-8?q?=E6=9D=BF=E5=8D=A1=E4=B8=8D=E5=BE=97=E5=9C=A8=E8=BD=AF=E4=BB=B6?= =?UTF-8?q?=E5=85=89=E6=A0=85=E5=99=A8=E4=B8=8A=E7=AD=BE=E6=94=B6=EF=BC=89?= =?UTF-8?q?=E3=80=82=E5=85=B6=E4=BA=8C=EF=BC=9A=E6=8A=8A=20shader=5Fsource?= =?UTF-8?q?=20=E6=89=93=E6=A1=A9=E6=88=90=E9=9D=9E=E6=B3=95=20GLSL?= =?UTF-8?q?=EF=BC=8C=E6=96=AD=E8=A8=80=20GLRenderer.initialize=20=E6=8A=9B?= =?UTF-8?q?=20diagnostic.code=3Dshader=5Fcompile=5Ffailed=20=E4=B8=94=20is?= =?UTF-8?q?=5Finitialized=20=E4=BF=9D=E6=8C=81=E5=81=87=EF=BC=9B=E5=86=8D?= =?UTF-8?q?=E7=94=A8=E4=B8=80=E4=B8=AA=20initialize=20=E5=BF=85=E7=84=B6?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E7=9A=84=20renderer=20=E6=9E=84=E9=80=A0?= =?UTF-8?q?=E7=9C=9F=E5=AE=9E=20widget=EF=BC=88=E8=B5=B0=E5=85=AC=E5=BC=80?= =?UTF-8?q?=20API=EF=BC=9Ashow/repaint/processEvents=EF=BC=89=EF=BC=8C?= =?UTF-8?q?=E6=96=AD=E8=A8=80=E8=A7=86=E5=9B=BE=E8=BF=9B=E5=85=A5=20unavai?= =?UTF-8?q?lable=E3=80=81=E8=AF=8A=E6=96=AD=E5=B7=B2=E5=8F=91=E5=87=BA?= =?UTF-8?q?=E3=80=81=E7=AC=AC=E4=BA=8C=E6=AC=A1=20repaint=20=E6=98=AF?= =?UTF-8?q?=E7=A9=BA=E6=93=8D=E4=BD=9C=E8=80=8C=E4=B8=8D=E6=98=AF=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E2=80=94=E2=80=94=E5=8D=B3=E4=B8=89=E7=BB=B4=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E5=8F=AA=E9=99=8D=E7=BA=A7=E8=87=AA=E5=B7=B1=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E5=BD=B1=E5=93=8D=E5=BA=94=E7=94=A8=E3=80=82=20-=20Wh?= =?UTF-8?q?y:=20=E8=A7=84=E6=A0=BC=20=C2=A713=20=E8=A6=81=E6=B1=82?= =?UTF-8?q?=E6=AF=8F=E9=98=B6=E6=AE=B5=E4=BA=A4=E4=BB=98=E5=90=AB=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E6=A0=B7=E4=BE=8B=EF=BC=9B=E9=98=B6=E6=AE=B5=201=20?= =?UTF-8?q?=E7=9A=84=E4=B8=A4=E4=B8=AA=E5=A4=B1=E8=B4=A5=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=EF=BC=88GL=20=E4=B8=8D=E8=B6=B3=E3=80=81shader=20=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E5=A4=B1=E8=B4=A5=EF=BC=89=E6=AD=A4=E5=89=8D=E5=8F=AA?= =?UTF-8?q?=E6=9C=89=E5=AE=9E=E7=8E=B0=E6=B2=A1=E6=9C=89=E9=AA=8C=E6=94=B6?= =?UTF-8?q?=EF=BC=8C=E8=80=8C=E8=BF=99=E4=B8=A4=E6=9D=A1=E6=81=B0=E6=81=B0?= =?UTF-8?q?=E6=98=AF=E6=9D=BF=E4=B8=8A=E6=9C=80=E5=8F=AF=E8=83=BD=E9=81=87?= =?UTF-8?q?=E5=88=B0=E7=9A=84=E6=83=85=E5=86=B5=E3=80=82=20-=20Verificatio?= =?UTF-8?q?n:=20=E7=9C=9F=E5=AE=9E=20context=20=E4=B8=8B=20tests/test=5Fsc?= =?UTF-8?q?ene3d=5Fgl=5Fsmoke.py=2010=20=E9=A1=B9=E5=85=A8=E8=BF=87?= =?UTF-8?q?=EF=BC=88=E5=8E=9F=208=20+=20=E6=96=B0=202=EF=BC=89=EF=BC=9Boff?= =?UTF-8?q?screen=20=E4=B8=8B=E8=AF=A5=E6=96=87=E4=BB=B6=E6=8C=89=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=20skip=EF=BC=9Bshared=20=E5=85=A8=E9=87=8F=20187=20pa?= =?UTF-8?q?ssed=E3=80=81studio=20=E5=85=A8=E9=87=8F=20118=20passed?= =?UTF-8?q?=E3=80=81ui=5Fcheck=20554=20=E9=A1=B9=200=20=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E3=80=82=20-=20Not=20in=20this=20change:=20=E6=9C=AA=E6=94=B9?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=EF=BC=88=E8=BF=99=E4=B8=80=E7=89=88=E5=8F=AA?= =?UTF-8?q?=E6=98=AF=E6=8A=8A=E5=B7=B2=E6=9C=89=E5=A4=B1=E8=B4=A5=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E9=92=89=E8=BF=9B=E6=B5=8B=E8=AF=95=EF=BC=89=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/studio/tests/test_scene3d_gl_smoke.py | 63 ++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/apps/studio/tests/test_scene3d_gl_smoke.py b/apps/studio/tests/test_scene3d_gl_smoke.py index bc32e91..fad0bc4 100644 --- a/apps/studio/tests/test_scene3d_gl_smoke.py +++ b/apps/studio/tests/test_scene3d_gl_smoke.py @@ -298,6 +298,69 @@ def test_release_is_idempotent_and_a_new_context_gets_the_scene_back(gl_widget, assert _drawn(_render(widget, qt_app)).size, "the scene is not on screen again" +def test_a_software_renderer_is_refused_with_its_capabilities(gl_widget, monkeypatch): + """A board must not be signed off on llvmpipe, and the report has to say so.""" + from orisys_studio.scene3d import gl_backend + + widget, _, _ = gl_widget + monkeypatch.setattr(gl_backend, "is_software_renderer", lambda vendor, renderer: True) + with pytest.raises(gl_backend.ThreeDUnsupported) as excinfo: + gl_backend.GLDispatch().bind_current_context(widget.context()) + capabilities = excinfo.value.capabilities + assert capabilities.software_renderer is True + assert any(item.code == "gl_unsupported" for item in capabilities.diagnostics) + assert "software" in excinfo.value.diagnostic.message.lower() + + +def test_a_broken_shader_takes_the_view_down_not_the_application(gl_widget, qt_app, monkeypatch): + """Shader compilation failure is a diagnostic and an empty view, nothing more.""" + from orisys_studio.scene3d import gl_backend + from orisys_studio.scene3d.camera import OrbitCamera + from orisys_studio.scene3d.widget import Sensor3DWidget + + widget, _, _ = gl_widget + capabilities = widget.capabilities() + assert capabilities is not None + + # The renderer refuses to come up, and says why. + def broken_source(profile, program): + return "#version 330 core\nvoid main() { this is not glsl }\n", "#version 330 core\n" + + monkeypatch.setattr(gl_backend, "shader_source", broken_source) + renderer = gl_backend.GLRenderer() + widget.makeCurrent() + try: + with pytest.raises(Exception) as excinfo: + renderer.initialize(capabilities) + finally: + widget.doneCurrent() + item = getattr(excinfo.value, "diagnostic", None) + assert item is not None and item.code == "shader_compile_failed", excinfo.value + assert not renderer.is_initialized, "a failed initialize must not claim to be up" + + # The widget turns the same failure into a state: a diagnostic, an empty view, + # and no exception on the way out. + monkeypatch.undo() + + class FailingRenderer(gl_backend.GLRenderer): + def initialize(self, capabilities): + raise gl_backend.error("shader_compile_failed", "forced for this test") + + failing = Sensor3DWidget(FailingRenderer(), OrbitCamera()) + seen: list = [] + failing.diagnosticRaised.connect(seen.append) + failing.resize(120, 90) + failing.show() + failing.repaint() + qt_app.processEvents() + failing.repaint() # a second paint of a failed view must be a no-op + qt_app.processEvents() + assert failing.unavailable + assert seen and seen[0].code == "shader_compile_failed" + failing.close() + failing.deleteLater() + + def test_the_probe_reports_this_machine_truthfully(): """The probe's contract: exit 0 only for a GPU-driven context.""" import importlib.util