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 ...@@ -4,6 +4,7 @@ build
local.properties local.properties
.DS_Store .DS_Store
.idea .idea
.vscode
deploy.sh deploy.sh
deploy.bat deploy.bat
skiko/src/jvmMain/java skiko/src/jvmMain/java
...@@ -53,6 +53,8 @@ val casualRun = tasks.named<JavaExec>("run") { ...@@ -53,6 +53,8 @@ val casualRun = tasks.named<JavaExec>("run") {
systemProperty("skiko.fps.enabled", "true") systemProperty("skiko.fps.enabled", "true")
systemProperty("skiko.linux.autodpi", "true") systemProperty("skiko.linux.autodpi", "true")
systemProperty("skiko.hardwareInfo.enabled", "true") systemProperty("skiko.hardwareInfo.enabled", "true")
systemProperty("skiko.win.exception.logger.enabled", "true")
systemProperty("skiko.win.exception.handler.enabled", "true")
jvmArgs?.add("-ea") jvmArgs?.add("-ea")
// Use systemProperty("skiko.library.path", "/tmp") to test loader. // Use systemProperty("skiko.library.path", "/tmp") to test loader.
System.getProperties().entries 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 #pragma once
#include <jni.h>
template<typename T> template<typename T>
T inline fromJavaPointer(jlong ptr) { return reinterpret_cast<T>(static_cast<uintptr_t>(ptr)); } T inline fromJavaPointer(jlong ptr) { return reinterpret_cast<T>(static_cast<uintptr_t>(ptr)); }
template<typename T> template<typename T>
jlong inline toJavaPointer(T ptr) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr)); } jlong inline toJavaPointer(T ptr) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr)); }
\ No newline at end of file
...@@ -85,6 +85,6 @@ extern "C" ...@@ -85,6 +85,6 @@ extern "C"
if (display && context) { if (display && context) {
glXDestroyContext(display, *context); glXDestroyContext(display, *context);
delete context; delete context;
} }
} }
} }
#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 #ifdef SK_DIRECT3D
#include <stdexcept>
#include <locale> #include <locale>
#include <Windows.h> #include <Windows.h>
#include <jawt_md.h> #include <jawt_md.h>
...@@ -8,19 +7,26 @@ ...@@ -8,19 +7,26 @@
#include "GrBackendSurface.h" #include "GrBackendSurface.h"
#include "GrDirectContext.h" #include "GrDirectContext.h"
#include "SkSurface.h" #include "SkSurface.h"
#include "exceptions_handler.h"
extern "C" extern "C"
{ {
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_context_Direct3DContextHandler_flush( JNIEXPORT void JNICALL Java_org_jetbrains_skiko_context_Direct3DContextHandler_flush(
JNIEnv *env, jobject redrawer, jlong contextPtr, jlong surfacePtr) JNIEnv *env, jobject redrawer, jlong contextPtr, jlong surfacePtr)
{ {
SkSurface *surface = fromJavaPointer<SkSurface *>(surfacePtr); __try
GrDirectContext *context = fromJavaPointer<GrDirectContext *>(contextPtr); {
SkSurface *surface = fromJavaPointer<SkSurface *>(surfacePtr);
surface->flushAndSubmit(true); GrDirectContext *context = fromJavaPointer<GrDirectContext *>(contextPtr);
surface->flush(SkSurface::BackendSurfaceAccess::kPresent, GrFlushInfo()); surface->flushAndSubmit(true);
context->flush({}); surface->flush(SkSurface::BackendSurfaceAccess::kPresent, GrFlushInfo());
context->submit(true); context->flush({});
context->submit(true);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
}
} }
} }
......
#ifdef SK_DIRECT3D #ifdef SK_DIRECT3D
#include <stdexcept>
#include <locale> #include <locale>
#include <Windows.h> #include <Windows.h>
#include <jawt_md.h> #include <jawt_md.h>
#include "jni_helpers.h" #include "jni_helpers.h"
#include "exceptions_handler.h"
#include "GrBackendSurface.h" #include "GrBackendSurface.h"
#include "GrDirectContext.h" #include "GrDirectContext.h"
...@@ -16,18 +16,6 @@ ...@@ -16,18 +16,6 @@
#include <dxgi1_4.h> #include <dxgi1_4.h>
#include <dxgi1_6.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; const int BuffersCount = 2;
// This is a list of not supported graphics cards that have rendering issues (black screen, flickering) // This is a list of not supported graphics cards that have rendering issues (black screen, flickering)
...@@ -71,11 +59,32 @@ public: ...@@ -71,11 +59,32 @@ public:
queue.reset(nullptr); queue.reset(nullptr);
device.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" extern "C"
{ {
HRESULT D3D12CreateDevice( HRESULT D3D12CreateDevice(
IUnknown *pAdapter, IUnknown *pAdapter,
D3D_FEATURE_LEVEL MinimumFeatureLevel, D3D_FEATURE_LEVEL MinimumFeatureLevel,
...@@ -326,48 +335,35 @@ extern "C" ...@@ -326,48 +335,35 @@ extern "C"
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_initSwapChain( JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_initSwapChain(
JNIEnv *env, jobject redrawer, jlong devicePtr) JNIEnv *env, jobject redrawer, jlong devicePtr)
{ {
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr); __try
{
// Make the swapchain DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr);
gr_cp<IDXGIFactory4> swapChainFactory; d3dDevice->initSwapChain();
GR_D3D_CALL_ERRCHECK(CreateDXGIFactory2(0, IID_PPV_ARGS(&swapChainFactory))); }
__except(EXCEPTION_EXECUTE_HANDLER) {
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; auto code = GetExceptionCode();
swapChainDesc.BufferCount = BuffersCount; logJavaException(env, __FUNCTION__, code);
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);
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_initFence( JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_initFence(
JNIEnv *env, jobject redrawer, jlong devicePtr) JNIEnv *env, jobject redrawer, jlong devicePtr)
{ {
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr); __try
for (int i = 0; i < BuffersCount; i++)
{ {
d3dDevice->fenceValues[i] = 10000; DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr);
for (int i = 0; i < BuffersCount; i++)
{
d3dDevice->fenceValues[i] = 10000;
}
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);
} }
GR_D3D_CALL_ERRCHECK(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);
} }
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_makeDirectXContext( JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_makeDirectXContext(
...@@ -390,43 +386,56 @@ extern "C" ...@@ -390,43 +386,56 @@ extern "C"
1, 1,
1, 1,
0); 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]; info.fResource = d3dDevice->buffers[index];
SkSurfaceProps surfaceProps(0, kRGB_H_SkPixelGeometry); SkSurfaceProps surfaceProps(0, kRGB_H_SkPixelGeometry);
GrBackendTexture backendTexture((int)d3dDevice->buffers[index]->GetDesc().Width, (int)d3dDevice->buffers[index]->GetDesc().Height, info); 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, context, backendTexture, kTopLeft_GrSurfaceOrigin, 0,
kRGBA_8888_SkColorType, SkColorSpace::MakeSRGB(), &surfaceProps) kRGBA_8888_SkColorType, SkColorSpace::MakeSRGB(), &surfaceProps)
.release()); .release();
return toJavaPointer(result);
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_resizeBuffers( JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_resizeBuffers(
JNIEnv *env, jobject redrawer, jlong devicePtr, jint width, jint height) JNIEnv *env, jobject redrawer, jlong devicePtr, jint width, jint height)
{ {
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr); __try {
for (int i = 0; i < BuffersCount; i++) 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)); if (d3dDevice->fence->GetCompletedValue() < d3dDevice->fenceValues[i])
WaitForSingleObjectEx(d3dDevice->fenceEvent, INFINITE, FALSE); {
d3dDevice->fence->SetEventOnCompletion(d3dDevice->fenceValues[i], d3dDevice->fenceEvent);
WaitForSingleObjectEx(d3dDevice->fenceEvent, INFINITE, FALSE);
}
d3dDevice->buffers[i].reset(nullptr);
} }
d3dDevice->buffers[i].reset(nullptr); d3dDevice->swapChain->ResizeBuffers(BuffersCount, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
auto code = GetExceptionCode();
logJavaException(env, __FUNCTION__, code);
} }
GR_D3D_CALL_ERRCHECK(d3dDevice->swapChain->ResizeBuffers(BuffersCount, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
} }
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_swap( JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_swap(
JNIEnv *env, jobject redrawer, jlong devicePtr, jboolean isVsyncEnabled) JNIEnv *env, jobject redrawer, jlong devicePtr, jboolean isVsyncEnabled)
{ {
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr); __try
{
// 1 value in [Present(1, 0)] enables vblank wait so this is how vertical sync works in DirectX. DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr);
const UINT64 fenceValue = d3dDevice->fenceValues[d3dDevice->bufferIndex]; // 1 value in [Present(1, 0)] enables vblank wait so this is how vertical sync works in DirectX.
GR_D3D_CALL_ERRCHECK(d3dDevice->swapChain->Present((int)isVsyncEnabled, 0)); const UINT64 fenceValue = d3dDevice->fenceValues[d3dDevice->bufferIndex];
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( JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_disposeDevice(
...@@ -439,16 +448,22 @@ extern "C" ...@@ -439,16 +448,22 @@ extern "C"
JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_getBufferIndex( JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_getBufferIndex(
JNIEnv *env, jobject redrawer, jlong devicePtr) JNIEnv *env, jobject redrawer, jlong devicePtr)
{ {
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr); __try {
const UINT64 fenceValue = d3dDevice->fenceValues[d3dDevice->bufferIndex]; DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice *>(devicePtr);
d3dDevice->bufferIndex = d3dDevice->swapChain->GetCurrentBackBufferIndex(); const UINT64 fenceValue = d3dDevice->fenceValues[d3dDevice->bufferIndex];
if (d3dDevice->fence->GetCompletedValue() < fenceValue) d3dDevice->bufferIndex = d3dDevice->swapChain->GetCurrentBackBufferIndex();
{ if (d3dDevice->fence->GetCompletedValue() < fenceValue)
GR_D3D_CALL_ERRCHECK(d3dDevice->fence->SetEventOnCompletion(fenceValue, d3dDevice->fenceEvent)); {
WaitForSingleObjectEx(d3dDevice->fenceEvent, INFINITE, FALSE); 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);
} }
d3dDevice->fenceValues[d3dDevice->bufferIndex] = fenceValue + 1;
return d3dDevice->bufferIndex;
} }
JNIEXPORT jstring JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_getAdapterName(JNIEnv *env, jobject redrawer, jlong devicePtr) 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 <windows.h>
#include <wingdi.h> #include <wingdi.h>
#include <gl/GL.h> #include <gl/GL.h>
#include <jawt_md.h> #include <jawt_md.h>
#include <dwmapi.h> #include <dwmapi.h>
#include "jni_helpers.h" #include "jni_helpers.h"
#include "exceptions_handler.h"
extern "C" extern "C"
{ {
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_getDevice(JNIEnv *env, jobject redrawer, jlong platformInfoPtr) JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_getDevice(JNIEnv *env, jobject redrawer, jlong platformInfoPtr)
{ {
JAWT_Win32DrawingSurfaceInfo* dsi_win = fromJavaPointer<JAWT_Win32DrawingSurfaceInfo *>(platformInfoPtr); __try
{
HWND hwnd = dsi_win->hwnd; JAWT_Win32DrawingSurfaceInfo* dsi_win = fromJavaPointer<JAWT_Win32DrawingSurfaceInfo *>(platformInfoPtr);
HDC device = GetDC(hwnd); HWND hwnd = dsi_win->hwnd;
HDC device = GetDC(hwnd);
PIXELFORMATDESCRIPTOR pixFormatDscr;
memset(&pixFormatDscr, 0, sizeof(PIXELFORMATDESCRIPTOR));
pixFormatDscr.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pixFormatDscr.nVersion = 1;
pixFormatDscr.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pixFormatDscr.iPixelType = PFD_TYPE_RGBA; PIXELFORMATDESCRIPTOR pixFormatDscr;
pixFormatDscr.cColorBits = 32; memset(&pixFormatDscr, 0, sizeof(PIXELFORMATDESCRIPTOR));
int iPixelFormat = ChoosePixelFormat(device, &pixFormatDscr); pixFormatDscr.nSize = sizeof(PIXELFORMATDESCRIPTOR);
SetPixelFormat(device, iPixelFormat, &pixFormatDscr); pixFormatDscr.nVersion = 1;
DescribePixelFormat(device, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pixFormatDscr); pixFormatDscr.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
return toJavaPointer(device); pixFormatDscr.iPixelType = PFD_TYPE_RGBA;
pixFormatDscr.cColorBits = 32;
int iPixelFormat = ChoosePixelFormat(device, &pixFormatDscr);
SetPixelFormat(device, iPixelFormat, &pixFormatDscr);
DescribePixelFormat(device, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pixFormatDscr);
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) JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_setSwapInterval(JNIEnv *env, jobject redrawer, jint interval)
{ {
typedef BOOL (WINAPI *PFNWGLSWAPINTERVALEXTPROC)(int interval); __try
// according to [https://opengl.gpuinfo.org/listreports.php?extension=WGL_EXT_swap_control&option=not] (filter by OS=windows)
// there a very few devices that doesn't support swap control
static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");
if (wglSwapIntervalEXT != NULL)
{ {
wglSwapIntervalEXT(interval); 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)
// there a very few devices that doesn't support swap control
static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");
if (wglSwapIntervalEXT != NULL)
{
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) JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_swapBuffers(JNIEnv *env, jobject redrawer, jlong devicePtr)
{ {
HDC device = fromJavaPointer<HDC>(devicePtr); __try
SwapBuffers(device); {
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) JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_makeCurrent(JNIEnv *env, jobject redrawer, jlong devicePtr, jlong contextPtr)
...@@ -59,8 +78,16 @@ extern "C" ...@@ -59,8 +78,16 @@ extern "C"
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_createContext(JNIEnv *env, jobject redrawer, jlong devicePtr) JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_WindowsOpenGLRedrawerKt_createContext(JNIEnv *env, jobject redrawer, jlong devicePtr)
{ {
HDC device = fromJavaPointer<HDC>(devicePtr); __try
return toJavaPointer(wglCreateContext(device)); {
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) 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 ...@@ -10,8 +10,8 @@ import java.util.concurrent.atomic.AtomicBoolean
object Library { object Library {
internal const val SKIKO_LIBRARY_PATH_PROPERTY = "skiko.library.path" 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 skikoLibraryPath = System.getProperty(SKIKO_LIBRARY_PATH_PROPERTY)
private val cacheRoot = "${System.getProperty("user.home")}/.skiko/"
private var copyDir: File? = null private var copyDir: File? = null
// Same native library cannot be loaded in several classloaders, so we have to clone // 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.* ...@@ -4,6 +4,7 @@ import org.jetbrains.skia.*
import org.jetbrains.skiko.context.ContextHandler import org.jetbrains.skiko.context.ContextHandler
import org.jetbrains.skiko.context.createContextHandler import org.jetbrains.skiko.context.createContextHandler
import org.jetbrains.skiko.redrawer.Redrawer import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.withExceptionHandler
import java.awt.Graphics import java.awt.Graphics
import java.awt.event.* import java.awt.event.*
import java.awt.im.InputMethodRequests import java.awt.im.InputMethodRequests
...@@ -130,6 +131,10 @@ open class SkiaLayer( ...@@ -130,6 +131,10 @@ open class SkiaLayer(
private var picture: PictureHolder? = null private var picture: PictureHolder? = null
private val pictureRecorder = PictureRecorder() private val pictureRecorder = PictureRecorder()
private val pictureLock = Any() private val pictureLock = Any()
private val onExceptionAction: () -> Boolean = {
findNextWorkingRenderApi(false)
false
}
private fun findNextWorkingRenderApi(redraw: Boolean) { private fun findNextWorkingRenderApi(redraw: Boolean) {
var thrown: Boolean var thrown: Boolean
...@@ -142,7 +147,7 @@ open class SkiaLayer( ...@@ -142,7 +147,7 @@ open class SkiaLayer(
contextHandler = createContextHandler(this, renderApi) contextHandler = createContextHandler(this, renderApi)
redrawer = platformOperations.createRedrawer(this, renderApi, properties) redrawer = platformOperations.createRedrawer(this, renderApi, properties)
if (redraw) redrawer!!.redrawImmediately() if (redraw) redrawer!!.redrawImmediately()
} catch (e: IllegalArgumentException) { } catch (e: Exception) {
println(e.message) println(e.message)
thrown = true thrown = true
} }
...@@ -206,10 +211,12 @@ open class SkiaLayer( ...@@ -206,10 +211,12 @@ open class SkiaLayer(
// such as `jframe.isEnabled = false` on Linux // such as `jframe.isEnabled = false` on Linux
// //
// To avoid recursive call of `draw` (we don't support recursive calls) we just schedule redrawing. // To avoid recursive call of `draw` (we don't support recursive calls) we just schedule redrawing.
if (isRendering) { withExceptionHandler(onExceptionAction) {
redrawer?.needRedraw() if (isRendering) {
} else { redrawer?.needRedraw()
redrawer?.redrawImmediately() } else {
redrawer?.redrawImmediately()
}
} }
} }
...@@ -308,7 +315,7 @@ open class SkiaLayer( ...@@ -308,7 +315,7 @@ open class SkiaLayer(
val pictureHeight = (height * contentScale).toInt().coerceAtLeast(0) val pictureHeight = (height * contentScale).toInt().coerceAtLeast(0)
val bounds = Rect.makeWH(pictureWidth.toFloat(), pictureHeight.toFloat()) val bounds = Rect.makeWH(pictureWidth.toFloat(), pictureHeight.toFloat())
val canvas = pictureRecorder.beginRecording(bounds)!! val canvas = pictureRecorder.beginRecording(bounds)
// clipping // clipping
for (component in clipComponents) { for (component in clipComponents) {
...@@ -339,7 +346,10 @@ open class SkiaLayer( ...@@ -339,7 +346,10 @@ open class SkiaLayer(
findNextWorkingRenderApi(true) findNextWorkingRenderApi(true)
return false return false
} }
initCanvas() withExceptionHandler(onExceptionAction) {
initCanvas()
true
}
} }
return 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