diff --git a/docusaurus/docs/getting-started/faq.md b/docusaurus/docs/getting-started/faq.md index b9c1cf2e..63b05b1d 100644 --- a/docusaurus/docs/getting-started/faq.md +++ b/docusaurus/docs/getting-started/faq.md @@ -99,7 +99,7 @@ import Head from '@docusaurus/Head'; "name": "The chat UI doesn't load on Windows - how do I fix it?", "acceptedAnswer": { "@type": "Answer", - "text": "On some Windows GPU/driver configurations, the chat UI may fail to load with a Direct3D/Skiko error. DevoxxGenie v0.8.0+ automatically handles this by retrying with software rendering. To permanently fix it, go to Settings > DevoxxGenie > Appearance and enable 'Force software rendering (fixes GPU issues on Windows)', then restart IntelliJ IDEA. Alternatively, add -Dskiko.renderApi=SOFTWARE to your IDE VM options (Help > Edit Custom VM Options)." + "text": "On some Windows GPU/driver configurations, the chat UI may fail to load with a Direct3D/Skiko error. DevoxxGenie now forces Skiko software rendering on Windows during startup to avoid those GPU issues. On older plugin versions, add -Dskiko.renderApi=SOFTWARE to your IDE VM options (Help > Edit Custom VM Options)." } } ] @@ -203,15 +203,11 @@ Switch to a smaller quantized model. For chat, try `llama3.2:3b` or `llama4:scou On some Windows systems with certain GPU/driver configurations, the DevoxxGenie chat UI may fail to initialize with an error about Skiko/Direct3D. -**Good news**: DevoxxGenie v0.8.0+ **automatically handles this** — if hardware rendering fails, it retries with software rendering and the UI should work normally. +**Good news**: current DevoxxGenie versions force software rendering on Windows during startup, so the chat UI should avoid these GPU-specific failures entirely. -To permanently fix this issue: +If you still hit the issue, restart IntelliJ IDEA to ensure the Windows startup override is applied. -1. Go to **Settings** > **DevoxxGenie** > **Appearance** -2. Enable **"Force software rendering (fixes GPU issues on Windows)"** -3. **Restart IntelliJ IDEA** - -Or add `-Dskiko.renderApi=SOFTWARE` to your IDE VM options (**Help** > **Edit Custom VM Options**). +If you are on an older plugin version, add `-Dskiko.renderApi=SOFTWARE` to your IDE VM options (**Help** > **Edit Custom VM Options**). See the [Troubleshooting guide](troubleshooting.md#chat-ui-not-loading--gpu-rendering-error) for more details. diff --git a/docusaurus/docs/getting-started/troubleshooting.md b/docusaurus/docs/getting-started/troubleshooting.md index 0bbd2c55..93dbf708 100644 --- a/docusaurus/docs/getting-started/troubleshooting.md +++ b/docusaurus/docs/getting-started/troubleshooting.md @@ -21,35 +21,22 @@ UnsatisfiedLinkError: Failed to load Direct3D native library This occurs because the graphics rendering engine (Skiko) cannot initialize hardware-accelerated rendering on some GPU/driver combinations. -### Automatic Fix (v0.8.0+) +### Automatic Fix -Starting with version 0.8.0, DevoxxGenie **automatically handles this issue**: - -1. First, it attempts hardware rendering (Direct3D/OpenGL) -2. If that fails, it automatically retries with software rendering -3. The chat UI should load normally without any action required - -You'll see a log message indicating the fallback was used: -``` -Successfully initialized Compose with software rendering fallback -``` +Current DevoxxGenie versions force Skiko software rendering on Windows during startup, so the chat UI should load without touching GPU-accelerated rendering. ### Manual Workarounds -If you still experience issues, or want to avoid the automatic retry delay, use one of these methods: +If you still experience issues, use one of these methods: -#### Option 1: Force Software Rendering in Settings (Recommended) +#### Option 1: Restart the IDE -1. Open **Settings** (or **Preferences** on macOS) -2. Navigate to **Tools** > **DevoxxGenie** > **Appearance** -3. Scroll to **Rendering Settings** -4. Check **"Force software rendering (fixes GPU issues on Windows)"** -5. Click **Apply** and **OK** -6. **Restart IntelliJ IDEA** +1. Close IntelliJ IDEA fully +2. Start IntelliJ IDEA again -This permanently enables software rendering and prevents the error from occurring. +This ensures the Windows startup override is applied before any chat UI is created. -#### Option 2: VM Options (Alternative) +#### Option 2: VM Options (Alternative for older versions) Add the following system property to your IDE's VM options: diff --git a/src/main/java/com/devoxx/genie/service/PostStartupActivity.java b/src/main/java/com/devoxx/genie/service/PostStartupActivity.java index 1225b380..21a578a0 100644 --- a/src/main/java/com/devoxx/genie/service/PostStartupActivity.java +++ b/src/main/java/com/devoxx/genie/service/PostStartupActivity.java @@ -7,7 +7,6 @@ import com.devoxx.genie.service.prompt.memory.ChatMemoryManager; import com.devoxx.genie.service.prompt.threading.ThreadPoolManager; import com.devoxx.genie.service.prompt.threading.ThreadPoolShutdownManager; -import com.devoxx.genie.ui.settings.DevoxxGenieStateService; import com.devoxx.genie.ui.util.ThemeChangeListener; import com.intellij.execution.ExecutionManager; import com.intellij.openapi.compiler.CompilationStatusListener; @@ -29,14 +28,6 @@ public class PostStartupActivity implements ProjectActivity { @Nullable @Override public Object execute(@NotNull Project project, @NotNull Continuation continuation) { - // Early initialization: Set Skiko software rendering before any Compose UI is created - // This fixes GPU driver incompatibility issues on Windows (issue #981) - DevoxxGenieStateService state = DevoxxGenieStateService.getInstance(); - if (state != null && Boolean.TRUE.equals(state.getForceSkikoSoftwareRendering())) { - System.setProperty("skiko.renderApi", "SOFTWARE"); - log.debug("Skiko software rendering enabled via user settings"); - } - // Initialize chat memory for this project ChatMemoryManager chatMemoryManager = ChatMemoryManager.getInstance(); if (chatMemoryManager != null) { diff --git a/src/main/java/com/devoxx/genie/service/WindowsSkikoRenderApiInitializer.java b/src/main/java/com/devoxx/genie/service/WindowsSkikoRenderApiInitializer.java new file mode 100644 index 00000000..d13fa909 --- /dev/null +++ b/src/main/java/com/devoxx/genie/service/WindowsSkikoRenderApiInitializer.java @@ -0,0 +1,43 @@ +package com.devoxx.genie.service; + +import com.intellij.ide.AppLifecycleListener; +import com.intellij.openapi.util.SystemInfo; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.BooleanSupplier; + +@Slf4j +public final class WindowsSkikoRenderApiInitializer implements AppLifecycleListener { + + static final String SKIKO_RENDER_API_PROPERTY = "skiko.renderApi"; + static final String SOFTWARE_RENDER_API = "SOFTWARE"; + + private final BooleanSupplier isWindows; + private final BiConsumer propertySetter; + + public WindowsSkikoRenderApiInitializer() { + this(() -> SystemInfo.isWindows, System::setProperty); + } + + WindowsSkikoRenderApiInitializer(BooleanSupplier isWindows, BiConsumer propertySetter) { + this.isWindows = Objects.requireNonNull(isWindows, "isWindows"); + this.propertySetter = Objects.requireNonNull(propertySetter, "propertySetter"); + } + + @Override + public void appFrameCreated(List commandLineArgs) { + initialize(); + } + + void initialize() { + if (!isWindows.getAsBoolean()) { + return; + } + + propertySetter.accept(SKIKO_RENDER_API_PROPERTY, SOFTWARE_RENDER_API); + log.info("Forced Skiko software rendering on Windows during application startup"); + } +} diff --git a/src/main/java/com/devoxx/genie/ui/settings/DevoxxGenieStateService.java b/src/main/java/com/devoxx/genie/ui/settings/DevoxxGenieStateService.java index dde862aa..4d5a7b64 100644 --- a/src/main/java/com/devoxx/genie/ui/settings/DevoxxGenieStateService.java +++ b/src/main/java/com/devoxx/genie/ui/settings/DevoxxGenieStateService.java @@ -187,9 +187,6 @@ public static DevoxxGenieStateService getInstance() { // CLAUDE.md / AGENTS.md inclusion option private Boolean useClaudeOrAgentsMdInPrompt = true; - // Skiko rendering settings - force software rendering to fix GPU driver issues on Windows - private Boolean forceSkikoSoftwareRendering = false; - private Boolean showAzureOpenAIFields = false; private Boolean showAwsFields = false; private Boolean shouldPowerFromAWSProfile = false; diff --git a/src/main/java/com/devoxx/genie/ui/settings/appearance/AppearanceSettingsComponent.java b/src/main/java/com/devoxx/genie/ui/settings/appearance/AppearanceSettingsComponent.java index a94f8c5e..c5d51cd1 100644 --- a/src/main/java/com/devoxx/genie/ui/settings/appearance/AppearanceSettingsComponent.java +++ b/src/main/java/com/devoxx/genie/ui/settings/appearance/AppearanceSettingsComponent.java @@ -3,6 +3,7 @@ import com.devoxx.genie.ui.settings.AbstractSettingsComponent; import com.devoxx.genie.ui.settings.DevoxxGenieStateService; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.SystemInfo; import com.intellij.ui.JBIntSpinner; import com.intellij.ui.components.JBTabbedPane; import com.intellij.ui.components.JBTextField; @@ -43,7 +44,6 @@ public class AppearanceSettingsComponent extends AbstractSettingsComponent { private final JCheckBox useRoundedCorners; private final JBIntSpinner cornerRadiusSpinner; private final JCheckBox useCustomColors; - private final JCheckBox forceSkikoSoftwareRendering; private final JButton resetButton; // Removed theme preview components @@ -85,10 +85,7 @@ public AppearanceSettingsComponent() { // Initialize other controls useRoundedCorners = new JCheckBox("Use rounded corners", state.getUseRoundedCorners()); useCustomColors = new JCheckBox("Use custom colors", state.getUseCustomColors()); - forceSkikoSoftwareRendering = new JCheckBox( - "Force software rendering (fixes GPU issues on Windows)", - Boolean.TRUE.equals(state.getForceSkikoSoftwareRendering())); - + // Preview panels removed // Enable/disable controls based on checkbox states @@ -204,11 +201,10 @@ public AppearanceSettingsComponent() { addSettingRow(panel, gbc, "Use rounded corners:", useRoundedCorners); addSettingRow(panel, gbc, "Corner radius (px):", cornerRadiusSpinner); - // Add rendering settings - addSection(panel, gbc, "Rendering Settings"); - JPanel renderingPanel = new JPanel(new BorderLayout()); - renderingPanel.add(forceSkikoSoftwareRendering, BorderLayout.CENTER); - addSettingRow(panel, gbc, "GPU rendering:", renderingPanel); + if (SystemInfo.isWindows) { + addSection(panel, gbc, "Rendering Settings"); + addSettingRow(panel, gbc, "Windows rendering:", new JLabel("Software rendering is forced automatically.")); + } // Add empty panel to push everything to the top gbc.weighty = 1.0; @@ -370,8 +366,7 @@ public boolean isModified() { state.getUseCustomCodeFontSize() != useCustomCodeFontSize.isSelected() || state.getCustomCodeFontSize() != customCodeFontSizeSpinner.getNumber() || state.getUseRoundedCorners() != useRoundedCorners.isSelected() || - state.getUseCustomColors() != useCustomColors.isSelected() || - !Boolean.TRUE.equals(state.getForceSkikoSoftwareRendering()) != !forceSkikoSoftwareRendering.isSelected(); + state.getUseCustomColors() != useCustomColors.isSelected(); } public void apply() { @@ -401,8 +396,7 @@ public void apply() { // Apply other settings state.setUseRoundedCorners(useRoundedCorners.isSelected()); state.setUseCustomColors(useCustomColors.isSelected()); - state.setForceSkikoSoftwareRendering(forceSkikoSoftwareRendering.isSelected()); - + // Notify open windows to refresh their styling ApplicationManager.getApplication().getMessageBus().syncPublisher(APPEARANCE_SETTINGS_TOPIC) .appearanceSettingsChanged(); @@ -435,8 +429,7 @@ public void reset() { // Reset other controls useRoundedCorners.setSelected(state.getUseRoundedCorners()); useCustomColors.setSelected(state.getUseCustomColors()); - forceSkikoSoftwareRendering.setSelected(Boolean.TRUE.equals(state.getForceSkikoSoftwareRendering())); - + // Update control states customFontSizeSpinner.setEnabled(useCustomFontSize.isSelected()); customCodeFontSizeSpinner.setEnabled(useCustomCodeFontSize.isSelected()); @@ -496,8 +489,7 @@ private void resetToDefaultValues() { // Reset other settings useRoundedCorners.setSelected(true); useCustomColors.setSelected(false); // Important: default to using theme-based colors - forceSkikoSoftwareRendering.setSelected(false); // Default to hardware rendering - + // Update control states customFontSizeSpinner.setEnabled(useCustomFontSize.isSelected()); customCodeFontSizeSpinner.setEnabled(useCustomCodeFontSize.isSelected()); diff --git a/src/main/kotlin/com/devoxx/genie/ui/compose/ComposeConversationViewController.kt b/src/main/kotlin/com/devoxx/genie/ui/compose/ComposeConversationViewController.kt index 45e4093b..1cff631d 100644 --- a/src/main/kotlin/com/devoxx/genie/ui/compose/ComposeConversationViewController.kt +++ b/src/main/kotlin/com/devoxx/genie/ui/compose/ComposeConversationViewController.kt @@ -52,8 +52,8 @@ class ComposeConversationViewController( onInitFailure = { error -> LOG.error("Compose/Skiko initialization failed. " + "Hardware rendering and software rendering fallback both failed. " + - "Try updating your GPU drivers or enabling 'Force software rendering' " + - "in DevoxxGenie Settings > Appearance.", error) + "Try updating your GPU drivers or adding -Dskiko.renderApi=SOFTWARE " + + "to the IDE VM options.", error) composeInitFailed = true }, onSoftwareFallback = { diff --git a/src/main/kotlin/com/devoxx/genie/ui/compose/SafeComposeContainer.kt b/src/main/kotlin/com/devoxx/genie/ui/compose/SafeComposeContainer.kt index 985df419..4f522a51 100644 --- a/src/main/kotlin/com/devoxx/genie/ui/compose/SafeComposeContainer.kt +++ b/src/main/kotlin/com/devoxx/genie/ui/compose/SafeComposeContainer.kt @@ -69,8 +69,7 @@ class SafeComposeContainer( "1. Update your GPU drivers
" + "2. Add -Dskiko.renderApi=SOFTWARE to your IDE VM options
" + "   (Help > Edit Custom VM Options)
" + - "3. Enable 'Force software rendering' in DevoxxGenie Settings > Appearance
" + - "4. Restart IntelliJ IDEA after making changes" + + "3. Restart IntelliJ IDEA after making changes" + "", SwingConstants.CENTER ).apply { diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 24c54ca2..0a3a2e7f 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -585,6 +585,11 @@ + + + + diff --git a/src/test/java/com/devoxx/genie/service/WindowsSkikoRenderApiInitializerTest.java b/src/test/java/com/devoxx/genie/service/WindowsSkikoRenderApiInitializerTest.java new file mode 100644 index 00000000..ff9f9cd7 --- /dev/null +++ b/src/test/java/com/devoxx/genie/service/WindowsSkikoRenderApiInitializerTest.java @@ -0,0 +1,37 @@ +package com.devoxx.genie.service; + +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class WindowsSkikoRenderApiInitializerTest { + + @Test + void componentsInitialized_setsSoftwareRenderingOnWindows() { + Map properties = new HashMap<>(); + WindowsSkikoRenderApiInitializer initializer = + new WindowsSkikoRenderApiInitializer(() -> true, properties::put); + + initializer.initialize(); + + assertThat(properties) + .containsEntry( + WindowsSkikoRenderApiInitializer.SKIKO_RENDER_API_PROPERTY, + WindowsSkikoRenderApiInitializer.SOFTWARE_RENDER_API + ); + } + + @Test + void componentsInitialized_doesNothingOutsideWindows() { + Map properties = new HashMap<>(); + WindowsSkikoRenderApiInitializer initializer = + new WindowsSkikoRenderApiInitializer(() -> false, properties::put); + + initializer.initialize(); + + assertThat(properties).isEmpty(); + } +}