Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions docusaurus/docs/getting-started/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)."
}
}
]
Expand Down Expand Up @@ -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.

Expand Down
29 changes: 8 additions & 21 deletions docusaurus/docs/getting-started/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,14 +28,6 @@ public class PostStartupActivity implements ProjectActivity {
@Nullable
@Override
public Object execute(@NotNull Project project, @NotNull Continuation<? super Unit> 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, String> propertySetter;

public WindowsSkikoRenderApiInitializer() {
this(() -> SystemInfo.isWindows, System::setProperty);
}

WindowsSkikoRenderApiInitializer(BooleanSupplier isWindows, BiConsumer<String, String> propertySetter) {
this.isWindows = Objects.requireNonNull(isWindows, "isWindows");
this.propertySetter = Objects.requireNonNull(propertySetter, "propertySetter");
}

@Override
public void appFrameCreated(List<String> 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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ class SafeComposeContainer(
"1. Update your GPU drivers<br>" +
"2. Add <code>-Dskiko.renderApi=SOFTWARE</code> to your IDE VM options<br>" +
"&nbsp;&nbsp;&nbsp;(Help &gt; Edit Custom VM Options)<br>" +
"3. Enable 'Force software rendering' in DevoxxGenie Settings > Appearance<br>" +
"4. Restart IntelliJ IDEA after making changes" +
"3. Restart IntelliJ IDEA after making changes" +
"</div></html>",
SwingConstants.CENTER
).apply {
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,11 @@
<postStartupActivity implementation="com.devoxx.genie.service.PostStartupActivity"/>
</extensions>

<applicationListeners>
<listener class="com.devoxx.genie.service.WindowsSkikoRenderApiInitializer"
topic="com.intellij.ide.AppLifecycleListener"/>
</applicationListeners>

<!-- Declare K2 Kotlin plugin mode compatibility (required since IntelliJ IDEA 2024.2.1) -->
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<supportsKotlinPluginMode supportsK2="true"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, String> 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<String, String> properties = new HashMap<>();
WindowsSkikoRenderApiInitializer initializer =
new WindowsSkikoRenderApiInitializer(() -> false, properties::put);

initializer.initialize();

assertThat(properties).isEmpty();
}
}
Loading