Unverified Commit 6463dfc5 authored by Roman Sedaikin's avatar Roman Sedaikin Committed by GitHub

Handling native exeptions (#198)

Handle system render exceptions to choose appropriate renderer on Windows (DirectX and OpenGL).
parent d4d654df
......@@ -4,6 +4,7 @@ build
local.properties
.DS_Store
.idea
.vscode
deploy.sh
deploy.bat
skiko/src/jvmMain/java
......@@ -53,6 +53,8 @@ val casualRun = tasks.named<JavaExec>("run") {
systemProperty("skiko.fps.enabled", "true")
systemProperty("skiko.linux.autodpi", "true")
systemProperty("skiko.hardwareInfo.enabled", "true")
systemProperty("skiko.win.exception.logger.enabled", "true")
systemProperty("skiko.win.exception.handler.enabled", "true")
jvmArgs?.add("-ea")
// Use systemProperty("skiko.library.path", "/tmp") to test loader.
System.getProperties().entries
......
#if SK_BUILD_FOR_WIN
#include <jni.h>
#include <windows.h>
#include <stdio.h>
void logJavaException(JNIEnv *env, const char * function, DWORD sehCode);
#endif
\ No newline at end of file
#pragma once
#include <jni.h>
template<typename T>
T inline fromJavaPointer(jlong ptr) { return reinterpret_cast<T>(static_cast<uintptr_t>(ptr)); }
......
#ifdef SK_DIRECT3D
#include <Windows.h>
#include <sstream>
#include <iostream>
#include "jni_helpers.h"
extern "C"
{
JNIEXPORT jstring JNICALL Java_org_jetbrains_skiko_RenderExceptionsHandlerKt_getNativeGraphicsAdapterInfo(
JNIEnv *env, jobject object)
{
std::stringstream stream;
std::string actualAdapter;
for(int i = 0;; i++)
{
DISPLAY_DEVICE device = {sizeof(device), 0};
BOOL result = EnumDisplayDevices(NULL, i, &device, EDD_GET_DEVICE_INTERFACE_NAME);
if(!result) break;
std::string currentAdapter = std::string(device.DeviceString);
if (actualAdapter != currentAdapter) {
actualAdapter = currentAdapter;
stream << " - " << currentAdapter << std::endl;
}
}
return env->NewStringUTF(stream.str().c_str());
}
LONG GetStringRegKey(HKEY hKey, const std::wstring &strValueName, std::wstring &strValue)
{
strValue = L"Unknown";
WCHAR szBuffer[512];
DWORD dwBufferSize = sizeof(szBuffer);
ULONG nError;
nError = RegQueryValueExW(hKey, strValueName.c_str(), 0, NULL, (LPBYTE)szBuffer, &dwBufferSize);
if (ERROR_SUCCESS == nError)
{
strValue = szBuffer;
}
return nError;
}
JNIEXPORT jstring JNICALL Java_org_jetbrains_skiko_RenderExceptionsHandlerKt_getNativeCpuInfo(
JNIEnv *env, jobject object)
{
auto path = L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
auto key = L"ProcessorNameString";
DWORD result;
HKEY hKey;
auto status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hKey);
if (ERROR_SUCCESS == status) {
std::wstring strValue;
GetStringRegKey(hKey, key, strValue);
std::string result = std::string(strValue.begin(), strValue.end());
return env->NewStringUTF(result.c_str());
}
return env->NewStringUTF("Can't get CPU info.");
}
}
#endif
\ No newline at end of file
#ifdef SK_DIRECT3D
#include <stdexcept>
#include <locale>
#include <Windows.h>
#include <jawt_md.h>
......@@ -8,20 +7,27 @@
#include "GrBackendSurface.h"
#include "GrDirectContext.h"
#include "SkSurface.h"
#include "exceptions_handler.h"
extern "C"
{
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_context_Direct3DContextHandler_flush(
JNIEnv *env, jobject redrawer, jlong contextPtr, jlong surfacePtr)
{
__try
{
SkSurface *surface = fromJavaPointer<SkSurface *>(surfacePtr);
GrDirectContext *context = fromJavaPointer<GrDirectContext *>(contextPtr);
surface->flushAndSubmit(true);
surface->flush(SkSurface::BackendSurfaceAccess::kPresent, GrFlushInfo());
context->flush({});
context->submit(true);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
}
}
}
#endif
#ifdef SK_DIRECT3D
#include <stdexcept>
#include <locale>
#include <Windows.h>
#include <jawt_md.h>
#include "jni_helpers.h"
#include "exceptions_handler.h"
#include "GrBackendSurface.h"
#include "GrDirectContext.h"
......@@ -16,18 +16,6 @@
#include <dxgi1_4.h>
#include <dxgi1_6.h>
#define GR_D3D_CALL_ERRCHECK(X) \
do \
{ \
HRESULT result = X; \
SkASSERT(SUCCEEDED(result)); \
if (!SUCCEEDED(result)) \
{ \
SkDebugf("Failed Direct3D call. Error: 0x%08x\n", result); \
throw std::exception("ERROR"); \
} \
} while (false)
const int BuffersCount = 2;
// This is a list of not supported graphics cards that have rendering issues (black screen, flickering)
......@@ -71,11 +59,32 @@ public:
queue.reset(nullptr);
device.reset(nullptr);
}
void initSwapChain() {
gr_cp<IDXGIFactory4> swapChainFactory4;
gr_cp<IDXGISwapChain1> swapChain1;
CreateDXGIFactory2(0, IID_PPV_ARGS(&swapChainFactory4));
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
swapChainDesc.BufferCount = BuffersCount;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.Scaling = DXGI_SCALING_NONE;
swapChainFactory4->CreateSwapChainForHwnd(queue.get(), window, &swapChainDesc, nullptr, nullptr, &swapChain1);
swapChainFactory4->MakeWindowAssociation(window, DXGI_MWA_NO_ALT_ENTER);
swapChain1->QueryInterface(IID_PPV_ARGS(&swapChain));
RECT windowRect;
GetWindowRect(window, &windowRect);
unsigned int w = windowRect.right - windowRect.left;
unsigned int h = windowRect.bottom - windowRect.top;
swapChain->ResizeBuffers(BuffersCount, w, h, DXGI_FORMAT_R8G8B8A8_UNORM, 0);
swapChainFactory4.reset(nullptr);
}
};
extern "C"
{
HRESULT D3D12CreateDevice(
IUnknown *pAdapter,
D3D_FEATURE_LEVEL MinimumFeatureLevel,
......@@ -325,50 +334,37 @@ extern "C"
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_initSwapChain(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
__try
{
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr);
// Make the swapchain
gr_cp<IDXGIFactory4> swapChainFactory;
GR_D3D_CALL_ERRCHECK(CreateDXGIFactory2(0, IID_PPV_ARGS(&swapChainFactory)));
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
swapChainDesc.BufferCount = BuffersCount;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.Scaling = DXGI_SCALING_NONE;
gr_cp<IDXGISwapChain1> swapChain;
GR_D3D_CALL_ERRCHECK(swapChainFactory->CreateSwapChainForHwnd(d3dDevice->queue.get(), d3dDevice->window, &swapChainDesc, nullptr, nullptr, &swapChain));
GR_D3D_CALL_ERRCHECK(swapChainFactory->MakeWindowAssociation(d3dDevice->window, DXGI_MWA_NO_ALT_ENTER));
GR_D3D_CALL_ERRCHECK(swapChain->QueryInterface(IID_PPV_ARGS(&d3dDevice->swapChain)));
RECT windowRect;
GetWindowRect(d3dDevice->window, &windowRect);
unsigned int w = windowRect.right - windowRect.left;
unsigned int h = windowRect.bottom - windowRect.top;
GR_D3D_CALL_ERRCHECK(d3dDevice->swapChain->ResizeBuffers(BuffersCount, w, h, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
swapChainFactory.reset(nullptr);
swapChainFactory.reset(nullptr);
d3dDevice->initSwapChain();
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_initFence(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
__try
{
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr);
for (int i = 0; i < BuffersCount; i++)
{
d3dDevice->fenceValues[i] = 10000;
}
GR_D3D_CALL_ERRCHECK(d3dDevice->device->CreateFence(d3dDevice->fenceValues[0],
D3D12_FENCE_FLAG_NONE,
IID_PPV_ARGS(&d3dDevice->fence)));
d3dDevice->device->CreateFence(d3dDevice->fenceValues[0], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&d3dDevice->fence));
d3dDevice->fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
SkASSERT(d3dDevice->fenceEvent);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
}
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_makeDirectXContext(
JNIEnv *env, jobject redrawer, jlong devicePtr)
......@@ -390,43 +386,56 @@ extern "C"
1,
1,
0);
GR_D3D_CALL_ERRCHECK(d3dDevice->swapChain->GetBuffer(index, IID_PPV_ARGS(&d3dDevice->buffers[index])));
d3dDevice->swapChain->GetBuffer(index, IID_PPV_ARGS(&d3dDevice->buffers[index]));
info.fResource = d3dDevice->buffers[index];
SkSurfaceProps surfaceProps(0, kRGB_H_SkPixelGeometry);
GrBackendTexture backendTexture((int)d3dDevice->buffers[index]->GetDesc().Width, (int)d3dDevice->buffers[index]->GetDesc().Height, info);
return toJavaPointer(SkSurface::MakeFromBackendTexture(
auto result = SkSurface::MakeFromBackendTexture(
context, backendTexture, kTopLeft_GrSurfaceOrigin, 0,
kRGBA_8888_SkColorType, SkColorSpace::MakeSRGB(), &surfaceProps)
.release());
.release();
return toJavaPointer(result);
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_resizeBuffers(
JNIEnv *env, jobject redrawer, jlong devicePtr, jint width, jint height)
{
__try {
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr);
for (int i = 0; i < BuffersCount; i++)
{
if (d3dDevice->fence->GetCompletedValue() < d3dDevice->fenceValues[i])
{
GR_D3D_CALL_ERRCHECK(d3dDevice->fence->SetEventOnCompletion(d3dDevice->fenceValues[i], d3dDevice->fenceEvent));
d3dDevice->fence->SetEventOnCompletion(d3dDevice->fenceValues[i], d3dDevice->fenceEvent);
WaitForSingleObjectEx(d3dDevice->fenceEvent, INFINITE, FALSE);
}
d3dDevice->buffers[i].reset(nullptr);
}
GR_D3D_CALL_ERRCHECK(d3dDevice->swapChain->ResizeBuffers(BuffersCount, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
d3dDevice->swapChain->ResizeBuffers(BuffersCount, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_swap(
JNIEnv *env, jobject redrawer, jlong devicePtr, jboolean isVsyncEnabled)
{
__try
{
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr);
// 1 value in [Present(1, 0)] enables vblank wait so this is how vertical sync works in DirectX.
const UINT64 fenceValue = d3dDevice->fenceValues[d3dDevice->bufferIndex];
GR_D3D_CALL_ERRCHECK(d3dDevice->swapChain->Present((int)isVsyncEnabled, 0));
GR_D3D_CALL_ERRCHECK(d3dDevice->queue->Signal(d3dDevice->fence.get(), fenceValue));
d3dDevice->swapChain->Present((int)isVsyncEnabled, 0);
d3dDevice->queue->Signal(d3dDevice->fence.get(), fenceValue);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_disposeDevice(
......@@ -439,17 +448,23 @@ extern "C"
JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_getBufferIndex(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
__try {
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr);
const UINT64 fenceValue = d3dDevice->fenceValues[d3dDevice->bufferIndex];
d3dDevice->bufferIndex = d3dDevice->swapChain->GetCurrentBackBufferIndex();
if (d3dDevice->fence->GetCompletedValue() < fenceValue)
{
GR_D3D_CALL_ERRCHECK(d3dDevice->fence->SetEventOnCompletion(fenceValue, d3dDevice->fenceEvent));
d3dDevice->fence->SetEventOnCompletion(fenceValue, d3dDevice->fenceEvent);
WaitForSingleObjectEx(d3dDevice->fenceEvent, INFINITE, FALSE);
}
d3dDevice->fenceValues[d3dDevice->bufferIndex] = fenceValue + 1;
return d3dDevice->bufferIndex;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
}
}
JNIEXPORT jstring JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_getAdapterName(JNIEnv *env, jobject redrawer, jlong devicePtr)
{
......
#if SK_BUILD_FOR_WIN
#include "exceptions_handler.h"
static JavaVM *jvm = NULL;
bool isHandleException(JNIEnv *env)
{
static jclass systemClass = NULL;
if (!systemClass)
{
systemClass = env->FindClass("java/lang/System");
}
static jmethodID getPropertyMethod = NULL;
if (!getPropertyMethod)
{
getPropertyMethod = env->GetStaticMethodID(systemClass, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
}
jstring propertyName = env->NewStringUTF("skiko.win.exception.handler.enabled");
jstring propertyString = (jstring)env->CallStaticObjectMethod(systemClass, getPropertyMethod, propertyName);
if (propertyString == 0)
{
return false;
}
const char *property = env->GetStringUTFChars(propertyString, 0);
bool result = !strncmp(property, "true", 4);
env->ReleaseStringUTFChars(propertyString, property);
return result;
}
const char *getDescription(DWORD code)
{
switch (code)
{
case EXCEPTION_ACCESS_VIOLATION:
return "EXCEPTION_ACCESS_VIOLATION";
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
case EXCEPTION_BREAKPOINT:
return "EXCEPTION_BREAKPOINT";
case EXCEPTION_DATATYPE_MISALIGNMENT:
return "EXCEPTION_DATATYPE_MISALIGNMENT";
case EXCEPTION_FLT_DENORMAL_OPERAND:
return "EXCEPTION_FLT_DENORMAL_OPERAND";
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
case EXCEPTION_FLT_INEXACT_RESULT:
return "EXCEPTION_FLT_INEXACT_RESULT";
case EXCEPTION_FLT_INVALID_OPERATION:
return "EXCEPTION_FLT_INVALID_OPERATION";
case EXCEPTION_FLT_OVERFLOW:
return "EXCEPTION_FLT_OVERFLOW";
case EXCEPTION_FLT_STACK_CHECK:
return "EXCEPTION_FLT_STACK_CHECK";
case EXCEPTION_FLT_UNDERFLOW:
return "EXCEPTION_FLT_UNDERFLOW";
case EXCEPTION_ILLEGAL_INSTRUCTION:
return "EXCEPTION_ILLEGAL_INSTRUCTION";
case EXCEPTION_IN_PAGE_ERROR:
return "EXCEPTION_IN_PAGE_ERROR";
case EXCEPTION_INT_DIVIDE_BY_ZERO:
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
case EXCEPTION_INT_OVERFLOW:
return "EXCEPTION_INT_OVERFLOW";
case EXCEPTION_INVALID_DISPOSITION:
return "EXCEPTION_INVALID_DISPOSITION";
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
case EXCEPTION_PRIV_INSTRUCTION:
return "EXCEPTION_PRIV_INSTRUCTION";
case EXCEPTION_SINGLE_STEP:
return "EXCEPTION_SINGLE_STEP";
case EXCEPTION_STACK_OVERFLOW:
return "EXCEPTION_STACK_OVERFLOW";
default:
return "UNKNOWN EXCEPTION";
}
}
void logJavaException(JNIEnv *env, const char *function, DWORD sehCode)
{
char buffer[200];
int result = snprintf(
buffer, 200, "Native exception in [%s]:\nSEH description: %s\n", function, getDescription(sehCode));
if (jvm == NULL)
{
env->GetJavaVM(&jvm);
}
if (isHandleException(env))
{
static jclass logClass = NULL;
if (!logClass)
{
logClass = env->FindClass("org/jetbrains/skiko/RenderExceptionsHandler");
}
static jmethodID logMethod = NULL;
if (!logMethod)
{
logMethod = env->GetStaticMethodID(logClass, "logAndThrow", "(Ljava/lang/String;)V");
}
env->CallStaticVoidMethod(logClass, logMethod, env->NewStringUTF(buffer));
}
}
#endif
\ No newline at end of file
#define WIN32_LEAN_AND_MEAN
#include <vector>
#include <string>
#include <windows.h>
#include <wingdi.h>
#include <gl/GL.h>
#include <jawt_md.h>
#include <dwmapi.h>
#include "jni_helpers.h"
#include "exceptions_handler.h"
extern "C"
{
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_getDevice(JNIEnv *env, jobject redrawer, jlong platformInfoPtr)
{
__try
{
JAWT_Win32DrawingSurfaceInfo* dsi_win = fromJavaPointer<JAWT_Win32DrawingSurfaceInfo *>(platformInfoPtr);
HWND hwnd = dsi_win->hwnd;
HDC device = GetDC(hwnd);
......@@ -31,8 +30,16 @@ extern "C"
return toJavaPointer(device);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
}
return (jlong) 0;
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_setSwapInterval(JNIEnv *env, jobject redrawer, jint interval)
{
__try
{
typedef BOOL (WINAPI *PFNWGLSWAPINTERVALEXTPROC)(int interval);
// according to [https://opengl.gpuinfo.org/listreports.php?extension=WGL_EXT_swap_control&option=not] (filter by OS=windows)
......@@ -43,12 +50,24 @@ extern "C"
wglSwapIntervalEXT(interval);
}
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_swapBuffers(JNIEnv *env, jobject redrawer, jlong devicePtr)
{
__try
{
HDC device = fromJavaPointer<HDC>(devicePtr);
SwapBuffers(device);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_makeCurrent(JNIEnv *env, jobject redrawer, jlong devicePtr, jlong contextPtr)
{
......@@ -58,10 +77,18 @@ extern "C"
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_createContext(JNIEnv *env, jobject redrawer, jlong devicePtr)
{
__try
{
HDC device = fromJavaPointer<HDC>(devicePtr);
return toJavaPointer(wglCreateContext(device));
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
}
return (jlong) 0;
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_deleteContext(JNIEnv *env, jobject redrawer, jlong contextPtr)
{
......
......@@ -10,8 +10,8 @@ import java.util.concurrent.atomic.AtomicBoolean
object Library {
internal const val SKIKO_LIBRARY_PATH_PROPERTY = "skiko.library.path"
internal val cacheRoot = "${System.getProperty("user.home")}/.skiko/"
private val skikoLibraryPath = System.getProperty(SKIKO_LIBRARY_PATH_PROPERTY)
private val cacheRoot = "${System.getProperty("user.home")}/.skiko/"
private var copyDir: File? = null
// Same native library cannot be loaded in several classloaders, so we have to clone
......
package org.jetbrains.skiko
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import kotlin.jvm.JvmStatic
import org.jetbrains.skiko.hostFullName
internal class RenderExceptionsHandler {
companion object {
private var output: File? = null
@JvmStatic
fun logAndThrow(message: String) {
if (output == null) {
output = File(
"${Library.cacheRoot}/skiko-render-exception-${ProcessHandle.current().pid()}.log"
)
}
val exception = Exception(message)
if (System.getProperty("skiko.win.exception.logger.enabled") == "true") {
writeLog(exception)
}
throw exception
}
private fun writeLog(exception: Exception) {
val outputBuilder = StringBuilder().apply {
append("When: ${SimpleDateFormat("dd/M/yyyy hh:mm:ss").format(Date())}\n")
append("Skiko version: ${Version.skiko}\n")
append("OS: $hostFullName\n")
append("CPU: ${getNativeCpuInfo()}\n")
append("Graphics adapters:\n${getNativeGraphicsAdapterInfo()}\n")
append("Exception message: ${exception.message}\n")
append("Exception stack trace:\n")
val stackTrace = exception.stackTrace.filterIndexed { line, _ -> line > 1 }
for(line in stackTrace) {
append("$line\n")
}
append("\n\n")
}
output?.appendText(outputBuilder.toString())
}
}
}
fun <T> withExceptionHandler(onException: () -> T, action: () -> T): T {
try {
return action()
} catch (e: Exception) {
println(e.message)
return onException()
}
}
private external fun getNativeGraphicsAdapterInfo(): String
private external fun getNativeCpuInfo(): String
......@@ -4,6 +4,7 @@ import org.jetbrains.skia.*
import org.jetbrains.skiko.context.ContextHandler
import org.jetbrains.skiko.context.createContextHandler
import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.withExceptionHandler
import java.awt.Graphics
import java.awt.event.*
import java.awt.im.InputMethodRequests
......@@ -130,6 +131,10 @@ open class SkiaLayer(
private var picture: PictureHolder? = null
private val pictureRecorder = PictureRecorder()
private val pictureLock = Any()
private val onExceptionAction: () -> Boolean = {
findNextWorkingRenderApi(false)
false
}
private fun findNextWorkingRenderApi(redraw: Boolean) {
var thrown: Boolean
......@@ -142,7 +147,7 @@ open class SkiaLayer(
contextHandler = createContextHandler(this, renderApi)
redrawer = platformOperations.createRedrawer(this, renderApi, properties)
if (redraw) redrawer!!.redrawImmediately()
} catch (e: IllegalArgumentException) {
} catch (e: Exception) {
println(e.message)
thrown = true
}
......@@ -206,12 +211,14 @@ open class SkiaLayer(
// such as `jframe.isEnabled = false` on Linux
//
// To avoid recursive call of `draw` (we don't support recursive calls) we just schedule redrawing.
withExceptionHandler(onExceptionAction) {
if (isRendering) {
redrawer?.needRedraw()
} else {
redrawer?.redrawImmediately()
}
}
}
// We need to delegate all event listeners to the Canvas (so and focus/input)
// Canvas is heavyweight AWT component, JPanel is lightweight Swing component
......@@ -308,7 +315,7 @@ open class SkiaLayer(
val pictureHeight = (height * contentScale).toInt().coerceAtLeast(0)
val bounds = Rect.makeWH(pictureWidth.toFloat(), pictureHeight.toFloat())
val canvas = pictureRecorder.beginRecording(bounds)!!
val canvas = pictureRecorder.beginRecording(bounds)
// clipping
for (component in clipComponents) {
......@@ -339,7 +346,10 @@ open class SkiaLayer(
findNextWorkingRenderApi(true)
return false
}
withExceptionHandler(onExceptionAction) {
initCanvas()
true
}
}
return true
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment