Unverified Commit f1ce1d44 authored by Roman Sedaikin's avatar Roman Sedaikin Committed by GitHub

Merge pull request #63 from JetBrains/directx

DirectX rendering implementation.
parents 20bba69b 21f890db
...@@ -268,6 +268,7 @@ tasks.withType(CppCompile::class.java).configureEach { ...@@ -268,6 +268,7 @@ tasks.withType(CppCompile::class.java).configureEach {
"-DWIN32_LEAN_AND_MEAN", "-DWIN32_LEAN_AND_MEAN",
"-DNOMINMAX", "-DNOMINMAX",
"-DSK_GAMMA_APPLY_TO_A8", "-DSK_GAMMA_APPLY_TO_A8",
"-DSK_DIRECT3D",
"/utf-8", "/utf-8",
"/GR-", // no-RTTI. "/GR-", // no-RTTI.
*buildType.msvcFlags *buildType.msvcFlags
......
...@@ -2,9 +2,9 @@ kotlin.code.style=official ...@@ -2,9 +2,9 @@ kotlin.code.style=official
deploy.version=0.0.0 deploy.version=0.0.0
dependencies.skija.git.commit=b85e3e7a70204281cf0036d5595a8eed68bacd2c dependencies.skija.git.commit=b85e3e7a70204281cf0036d5595a8eed68bacd2c
dependencies.skia.windows-x64=m89-15595ea39c/Skia-m89-15595ea39c-windows-Release-x64 dependencies.skia.windows-x64=m89-19889d74fa/Skia-m89-19889d74fa-windows-Release-x64
dependencies.skia.linux-x64=m89-15595ea39c/Skia-m89-15595ea39c-linux-Release-x64 dependencies.skia.linux-x64=m89-19889d74fa/Skia-m89-19889d74fa-linux-Release-x64
dependencies.skia.macos-x64=m89-15595ea39c/Skia-m89-15595ea39c-macos-Release-x64 dependencies.skia.macos-x64=m89-19889d74fa/Skia-m89-19889d74fa-macos-Release-x64
dependencies.skia.macos-arm64=m89-15595ea39c/Skia-m89-15595ea39c-macos-Release-arm64 dependencies.skia.macos-arm64=m89-19889d74fa/Skia-m89-19889d74fa-macos-Release-arm64
# signer=Apple Distribution: Nikolay Igotti (N462MKSJ7M) # signer=Apple Distribution: Nikolay Igotti (N462MKSJ7M)
#ifdef SK_DIRECT3D
#include <stdexcept>
#include <Windows.h>
#include <jawt_md.h>
#include "jni_helpers.h"
#include "GrBackendSurface.h"
#include "GrDirectContext.h"
#include "SkSurface.h"
#include "d3d/GrD3DTypes.h"
#include <d3d12sdklayers.h>
#include "d3d/GrD3DBackendContext.h"
#include <d3d12.h>
#include <dxgi1_4.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;
class DirectXDevice
{
public:
GrD3DBackendContext backendContext;
gr_cp<ID3D12Device> device;
gr_cp<IDXGISwapChain3> swapChain;
gr_cp<ID3D12CommandQueue> queue;
gr_cp<ID3D12Fence> fence;
HANDLE fenceEvent = NULL;
uint64_t fenceValue;
unsigned int bufferIndex = 1;
unsigned int bufferWidth = 0;
unsigned int bufferHeight = 0;
bool isSizeEqualTo(unsigned int width, unsigned int height)
{
return width == bufferWidth && height == bufferHeight;
}
~DirectXDevice()
{
if (fenceEvent != NULL)
{
CloseHandle(fenceEvent);
}
}
};
extern "C"
{
HRESULT D3D12CreateDevice(
IUnknown *pAdapter,
D3D_FEATURE_LEVEL MinimumFeatureLevel,
REFIID riid,
void **ppDevice
) {
typedef HRESULT (*D3D12CreateDevice_t)(
IUnknown *pAdapter,
D3D_FEATURE_LEVEL MinimumFeatureLevel,
REFIID riid,
void **ppDevice
);
static D3D12CreateDevice_t impl = nullptr;
if (!impl) {
auto d3d12dll = LoadLibrary(TEXT("D3D12.dll"));
if (!d3d12dll)
return E_NOTIMPL;
impl = (D3D12CreateDevice_t)GetProcAddress(d3d12dll, "D3D12CreateDevice");
if (!impl)
return E_NOTIMPL;
}
return impl(pAdapter, MinimumFeatureLevel, riid, ppDevice);
}
HRESULT D3D12SerializeRootSignature(
const D3D12_ROOT_SIGNATURE_DESC *pRootSignature,
D3D_ROOT_SIGNATURE_VERSION Version,
ID3DBlob **ppBlob,
ID3DBlob **ppErrorBlob
) {
typedef HRESULT (*D3D12SerializeRootSignature_t)(
const D3D12_ROOT_SIGNATURE_DESC *pRootSignature,
D3D_ROOT_SIGNATURE_VERSION Version,
ID3DBlob **ppBlob,
ID3DBlob **ppErrorBlob
);
static D3D12SerializeRootSignature_t impl = nullptr;
if (!impl) {
auto d3d12dll = LoadLibrary(TEXT("D3D12.dll"));
if (!d3d12dll)
return E_NOTIMPL;
impl = (D3D12SerializeRootSignature_t)GetProcAddress(d3d12dll, "D3D12SerializeRootSignature");
if (!impl)
return E_NOTIMPL;
}
return impl(pRootSignature, Version, ppBlob, ppErrorBlob);
}
HRESULT CreateDXGIFactory1(
REFIID riid,
void **ppFactory
) {
typedef HRESULT (*CreateDXGIFactory1_t)(
REFIID riid,
void **ppFactory
);
static CreateDXGIFactory1_t impl = nullptr;
if (!impl) {
auto dxgidll = LoadLibrary(TEXT("Dxgi.dll"));
if (!dxgidll)
return E_NOTIMPL;
impl = (CreateDXGIFactory1_t)GetProcAddress(dxgidll, "CreateDXGIFactory1");
if (!impl)
return E_NOTIMPL;
}
return impl(riid, ppFactory);
}
HRESULT CreateDXGIFactory2(
UINT Flags,
REFIID riid,
void **ppFactory
) {
typedef HRESULT (*CreateDXGIFactory2_t)(
UINT Flags,
REFIID riid,
void **ppFactory
);
static CreateDXGIFactory2_t impl = nullptr;
if (!impl) {
auto dxgidll = LoadLibrary(TEXT("Dxgi.dll"));
if (!dxgidll)
return E_NOTIMPL;
impl = (CreateDXGIFactory2_t)GetProcAddress(dxgidll, "CreateDXGIFactory2");
if (!impl)
return E_NOTIMPL;
}
return impl(Flags, riid, ppFactory);
}
HRESULT D3DCompile(
LPCVOID pSrcData,
SIZE_T SrcDataSize,
LPCSTR pSourceName,
const D3D_SHADER_MACRO *pDefines,
ID3DInclude *pInclude,
LPCSTR pEntrypoint,
LPCSTR pTarget,
UINT Flags1,
UINT Flags2,
ID3DBlob **ppCode,
ID3DBlob **ppErrorMsgs
) {
typedef HRESULT (*D3DCompile_t)(
LPCVOID pSrcData,
SIZE_T SrcDataSize,
LPCSTR pSourceName,
const D3D_SHADER_MACRO *pDefines,
ID3DInclude *pInclude,
LPCSTR pEntrypoint,
LPCSTR pTarget,
UINT Flags1,
UINT Flags2,
ID3DBlob **ppCode,
ID3DBlob **ppErrorMsgs
);
static D3DCompile_t impl = nullptr;
if (!impl) {
auto d3dcompilerdll = LoadLibrary(TEXT("d3dcompiler_47.dll"));
if (!d3dcompilerdll)
return E_NOTIMPL;
impl = (D3DCompile_t)GetProcAddress(d3dcompilerdll, "D3DCompile");
if (!impl)
return E_NOTIMPL;
}
return impl(pSrcData, SrcDataSize, pSourceName, pDefines, pInclude, pEntrypoint, pTarget, Flags1, Flags2, ppCode, ppErrorMsgs);
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_makeDirectXContext(
JNIEnv* env, jobject redrawer, jlong devicePtr)
{
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice*>(devicePtr);
GrD3DBackendContext backendContext = d3dDevice->backendContext;
return toJavaPointer(GrDirectContext::MakeDirect3D(backendContext).release());
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_makeDirectXRenderTarget(
JNIEnv * env, jobject redrawer, jlong devicePtr, jint width, jint height)
{
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice*>(devicePtr);
if (!d3dDevice->isSizeEqualTo(width, height))
{
GR_D3D_CALL_ERRCHECK(d3dDevice->swapChain->ResizeBuffers(BuffersCount, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
d3dDevice->bufferWidth = width;
d3dDevice->bufferHeight = height;
}
d3dDevice->bufferIndex = d3dDevice->swapChain->GetCurrentBackBufferIndex();
ID3D12Resource* buffer;
GR_D3D_CALL_ERRCHECK(d3dDevice->swapChain->GetBuffer(d3dDevice->bufferIndex, IID_PPV_ARGS(&buffer)));
GrD3DTextureResourceInfo info(buffer,
nullptr,
D3D12_RESOURCE_STATE_PRESENT,
DXGI_FORMAT_R8G8B8A8_UNORM,
1,
1,
0);
GrBackendRenderTarget* renderTarget = new GrBackendRenderTarget(width, height, info);
return toJavaPointer(renderTarget);
}
bool defineHardwareAdapter(IDXGIFactory4 *pFactory, IDXGIAdapter1 **ppAdapter)
{
*ppAdapter = nullptr;
for (UINT adapterIndex = 0;; ++adapterIndex)
{
IDXGIAdapter1 *pAdapter = nullptr;
if (DXGI_ERROR_NOT_FOUND == pFactory->EnumAdapters1(adapterIndex, &pAdapter))
{
break;
}
if (SUCCEEDED(D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
{
*ppAdapter = pAdapter;
return true;
}
pAdapter->Release();
}
return false;
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_createDirectXDevice(
JNIEnv *env, jobject redrawer, jlong windowHandle)
{
gr_cp<IDXGIFactory4> deviceFactory;
if (!SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&deviceFactory))))
{
return 0;
}
gr_cp<IDXGIAdapter1> hardwareAdapter;
if (!defineHardwareAdapter(deviceFactory.get(), &hardwareAdapter))
{
return 0;
}
gr_cp<ID3D12Device> device;
if (!SUCCEEDED(D3D12CreateDevice(hardwareAdapter.get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device))))
{
return 0;
}
// Create the command queue
gr_cp<ID3D12CommandQueue> queue;
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
if (!SUCCEEDED(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&queue))))
{
return 0;
}
DirectXDevice *d3dDevice = new DirectXDevice();
d3dDevice->backendContext.fAdapter = hardwareAdapter;
d3dDevice->backendContext.fDevice = device;
d3dDevice->backendContext.fQueue = queue;
d3dDevice->device = device;
d3dDevice->queue = queue;
// Make the swapchain
HWND fWindow = (HWND)windowHandle;
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_SEQUENTIAL;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.Scaling = DXGI_SCALING_NONE;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC swapChainFSDesc = {};
swapChainFSDesc.Windowed = TRUE;
gr_cp<IDXGISwapChain1> swapChain;
GR_D3D_CALL_ERRCHECK(swapChainFactory->CreateSwapChainForHwnd(d3dDevice->queue.get(), fWindow, &swapChainDesc, &swapChainFSDesc, nullptr, &swapChain));
DXGI_RGBA background = {1.0f, 1.0f, 1.0f, 1.0f};
swapChain->SetBackgroundColor(&background);
GR_D3D_CALL_ERRCHECK(swapChain->QueryInterface(IID_PPV_ARGS(&d3dDevice->swapChain)));
GR_D3D_CALL_ERRCHECK(d3dDevice->device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&d3dDevice->fence)));
d3dDevice->fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
return toJavaPointer(d3dDevice);
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_resizeBuffers(
JNIEnv *env, jobject redrawer, jlong devicePtr, jint width, jint height)
{
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice*>(devicePtr);
if (!d3dDevice->isSizeEqualTo(width, height))
{
GR_D3D_CALL_ERRCHECK(d3dDevice->swapChain->ResizeBuffers(BuffersCount, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0));
d3dDevice->bufferWidth = width;
d3dDevice->bufferHeight = height;
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_finishFrame(
JNIEnv *env, jobject redrawer, jlong devicePtr, jlong contextPtr, jlong surfacePtr, jboolean isVsyncEnabled)
{
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice*>(devicePtr);
SkSurface *surface = fromJavaPointer<SkSurface*>(surfacePtr);
surface->flushAndSubmit();
GrDirectContext *fContext = fromJavaPointer<GrDirectContext*>(contextPtr);
surface->flush(SkSurface::BackendSurfaceAccess::kPresent, GrFlushInfo());
fContext->flush({});
fContext->submit(true);
// 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 fence = d3dDevice->fenceValue;
GR_D3D_CALL_ERRCHECK(d3dDevice->queue->Signal(d3dDevice->fence.get(), fence));
d3dDevice->fenceValue++;
if (d3dDevice->fence->GetCompletedValue() < fence)
{
GR_D3D_CALL_ERRCHECK(d3dDevice->fence->SetEventOnCompletion(fence, d3dDevice->fenceEvent));
WaitForSingleObjectEx(d3dDevice->fenceEvent, INFINITE, FALSE);
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_disposeDevice(
JNIEnv *env, jobject redrawer, jlong devicePtr)
{
DirectXDevice *d3dDevice = fromJavaPointer<DirectXDevice*>(devicePtr);
delete d3dDevice;
}
}
#endif
package org.jetbrains.skiko package org.jetbrains.skiko
import org.jetbrains.skiko.SkikoProperties.renderApi
import org.jetbrains.skiko.redrawer.LinuxOpenGLRedrawer import org.jetbrains.skiko.redrawer.LinuxOpenGLRedrawer
import org.jetbrains.skiko.redrawer.MacOsOpenGLRedrawer import org.jetbrains.skiko.redrawer.MacOsOpenGLRedrawer
import org.jetbrains.skiko.redrawer.RasterRedrawer import org.jetbrains.skiko.redrawer.SoftwareRedrawer
import org.jetbrains.skiko.redrawer.Redrawer import org.jetbrains.skiko.redrawer.Redrawer
import org.jetbrains.skiko.redrawer.WindowsOpenGLRedrawer import org.jetbrains.skiko.redrawer.WindowsOpenGLRedrawer
import org.jetbrains.skiko.redrawer.Direct3DRedrawer
import java.awt.Component import java.awt.Component
import java.awt.Window import java.awt.Window
import javax.swing.SwingUtilities import javax.swing.SwingUtilities
...@@ -14,7 +14,7 @@ internal interface PlatformOperations { ...@@ -14,7 +14,7 @@ internal interface PlatformOperations {
fun isFullscreen(component: Component): Boolean fun isFullscreen(component: Component): Boolean
fun setFullscreen(component: Component, value: Boolean) fun setFullscreen(component: Component, value: Boolean)
fun getDpiScale(component: Component): Float fun getDpiScale(component: Component): Float
fun createRedrawer(layer: HardwareLayer, properties: SkiaLayerProperties): Redrawer fun createRedrawer(layer: HardwareLayer, renderApi: GraphicsApi, properties: SkiaLayerProperties): Redrawer
} }
internal val platformOperations: PlatformOperations by lazy { internal val platformOperations: PlatformOperations by lazy {
...@@ -32,8 +32,12 @@ internal val platformOperations: PlatformOperations by lazy { ...@@ -32,8 +32,12 @@ internal val platformOperations: PlatformOperations by lazy {
return component.graphicsConfiguration.defaultTransform.scaleX.toFloat() return component.graphicsConfiguration.defaultTransform.scaleX.toFloat()
} }
override fun createRedrawer(layer: HardwareLayer, properties: SkiaLayerProperties) = when(renderApi) { override fun createRedrawer(
GraphicsApi.SOFTWARE -> RasterRedrawer(layer) layer: HardwareLayer,
renderApi: GraphicsApi,
properties: SkiaLayerProperties
) = when(renderApi) {
GraphicsApi.SOFTWARE -> SoftwareRedrawer(layer)
else -> MacOsOpenGLRedrawer(layer, properties) else -> MacOsOpenGLRedrawer(layer, properties)
} }
} }
...@@ -55,8 +59,13 @@ internal val platformOperations: PlatformOperations by lazy { ...@@ -55,8 +59,13 @@ internal val platformOperations: PlatformOperations by lazy {
return component.graphicsConfiguration.defaultTransform.scaleX.toFloat() return component.graphicsConfiguration.defaultTransform.scaleX.toFloat()
} }
override fun createRedrawer(layer: HardwareLayer, properties: SkiaLayerProperties) = when(renderApi) { override fun createRedrawer(
GraphicsApi.SOFTWARE -> RasterRedrawer(layer) layer: HardwareLayer,
renderApi: GraphicsApi,
properties: SkiaLayerProperties
) = when(renderApi) {
GraphicsApi.SOFTWARE -> SoftwareRedrawer(layer)
GraphicsApi.DIRECT3D -> Direct3DRedrawer(layer, properties)
else -> WindowsOpenGLRedrawer(layer, properties) else -> WindowsOpenGLRedrawer(layer, properties)
} }
} }
...@@ -80,19 +89,23 @@ internal val platformOperations: PlatformOperations by lazy { ...@@ -80,19 +89,23 @@ internal val platformOperations: PlatformOperations by lazy {
// TODO doesn't work well because java doesn't scale windows (content has offset with 200% scale) // TODO doesn't work well because java doesn't scale windows (content has offset with 200% scale)
// //
// Two solutions: // Two solutions:
// 1. dynamically change sun.java2d.uiScale (it is global property, so we have to be careful) and update all windows // 1. dynamically change sun.java2d.uiScale (it is global property,
// so we have to be careful) and update all windows
// //
// 2. apply contentScale manually to all windows // 2. apply contentScale manually to all windows
// (it is not good, because on different platform windows will have different size. // (it is not good, because on different platform windows will have different size.
// Maybe we will apply contentScale manually on all platforms?) // Maybe we will apply contentScale manually on all platforms?)
// see also comment for HardwareLayer.checkContentScale // see also comment for HardwareLayer.checkContentScale
// return component.useDrawingSurfacePlatformInfo(::linuxGetDpiScaleNative)
// return component.useDrawingSurfacePlatformInfo(::linuxGetDpiScaleNative)
} }
override fun createRedrawer(layer: HardwareLayer, properties: SkiaLayerProperties) = when(renderApi) { override fun createRedrawer(
GraphicsApi.SOFTWARE -> RasterRedrawer(layer) layer: HardwareLayer,
renderApi: GraphicsApi,
properties: SkiaLayerProperties
) = when(renderApi) {
GraphicsApi.SOFTWARE -> SoftwareRedrawer(layer)
else -> LinuxOpenGLRedrawer(layer, properties) else -> LinuxOpenGLRedrawer(layer, properties)
} }
} }
......
...@@ -5,12 +5,15 @@ import org.jetbrains.skija.ClipMode ...@@ -5,12 +5,15 @@ import org.jetbrains.skija.ClipMode
import org.jetbrains.skija.Picture import org.jetbrains.skija.Picture
import org.jetbrains.skija.PictureRecorder import org.jetbrains.skija.PictureRecorder
import org.jetbrains.skija.Rect import org.jetbrains.skija.Rect
import org.jetbrains.skiko.context.SoftwareContextHandler import org.jetbrains.skiko.context.ContextHandler
import org.jetbrains.skiko.context.createContextHandler import org.jetbrains.skiko.context.createContextHandler
import org.jetbrains.skiko.redrawer.RasterRedrawer import org.jetbrains.skiko.context.SoftwareContextHandler
import org.jetbrains.skiko.redrawer.SoftwareRedrawer
import org.jetbrains.skiko.redrawer.Redrawer import org.jetbrains.skiko.redrawer.Redrawer
import java.awt.Graphics import java.awt.Graphics
import javax.swing.SwingUtilities.isEventDispatchThread import javax.swing.SwingUtilities.isEventDispatchThread
import kotlin.collections.MutableList
import kotlin.collections.toMutableList
interface SkiaRenderer { interface SkiaRenderer {
fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long)
...@@ -24,11 +27,12 @@ open class SkiaLayer( ...@@ -24,11 +27,12 @@ open class SkiaLayer(
var renderer: SkiaRenderer? = null var renderer: SkiaRenderer? = null
val clipComponents = mutableListOf<ClipRectangle>() val clipComponents = mutableListOf<ClipRectangle>()
internal var skijaState = createContextHandler(this)
@Volatile @Volatile
private var isDisposed = false private var isDisposed = false
private var redrawer: Redrawer? = null internal var redrawer: Redrawer? = null
private var contextHandler: ContextHandler? = null
private val fallbackRenderApiQueue = SkikoProperties.fallbackRenderApiQueue.toMutableList()
@Volatile @Volatile
private var picture: PictureHolder? = null private var picture: PictureHolder? = null
...@@ -37,7 +41,9 @@ open class SkiaLayer( ...@@ -37,7 +41,9 @@ open class SkiaLayer(
override fun init() { override fun init() {
super.init() super.init()
redrawer = platformOperations.createRedrawer(this, properties) val initialRenderApi = fallbackRenderApiQueue.removeAt(0)
contextHandler = createContextHandler(this, initialRenderApi)
redrawer = platformOperations.createRedrawer(this, initialRenderApi, properties)
redrawer?.syncSize() redrawer?.syncSize()
redrawer?.redrawImmediately() redrawer?.redrawImmediately()
} }
...@@ -45,6 +51,7 @@ open class SkiaLayer( ...@@ -45,6 +51,7 @@ open class SkiaLayer(
override fun dispose() { override fun dispose() {
check(!isDisposed) check(!isDisposed)
check(isEventDispatchThread()) check(isEventDispatchThread())
contextHandler?.dispose()
redrawer?.dispose() redrawer?.dispose()
picture?.instance?.close() picture?.instance?.close()
pictureRecorder.close() pictureRecorder.close()
...@@ -104,9 +111,9 @@ open class SkiaLayer( ...@@ -104,9 +111,9 @@ open class SkiaLayer(
override fun draw() { override fun draw() {
check(!isDisposed) check(!isDisposed)
skijaState.apply { contextHandler?.apply {
if (!initContext()) { if (!initContext()) {
fallbackToRaster() fallbackToNextApi()
return return
} }
initCanvas() initCanvas()
...@@ -135,11 +142,13 @@ open class SkiaLayer( ...@@ -135,11 +142,13 @@ open class SkiaLayer(
) )
} }
private fun fallbackToRaster() { private fun fallbackToNextApi() {
println("Falling back to software rendering...") val nextApi = fallbackRenderApiQueue.removeAt(0)
println("Falling back to $nextApi rendering...")
contextHandler?.dispose()
redrawer?.dispose() redrawer?.dispose()
skijaState = SoftwareContextHandler(this) contextHandler = createContextHandler(this, nextApi)
redrawer = RasterRedrawer(this) redrawer = platformOperations.createRedrawer(this, nextApi, properties)
needRedraw() needRedraw()
} }
} }
...@@ -29,8 +29,38 @@ internal object SkikoProperties { ...@@ -29,8 +29,38 @@ internal object SkikoProperties {
when(text) { when(text) {
"SOFTWARE" -> return GraphicsApi.SOFTWARE "SOFTWARE" -> return GraphicsApi.SOFTWARE
"OPENGL" -> return GraphicsApi.OPENGL "OPENGL" -> return GraphicsApi.OPENGL
else -> return GraphicsApi.OPENGL "DIRECT3D" -> {
return if (hostOs == OS.Windows) GraphicsApi.DIRECT3D
else throw Exception("$hostOs does not support DirectX rendering API.")
} }
"METAL" -> {
return if (hostOs == OS.MacOS) GraphicsApi.METAL
else throw Exception("$hostOs does not support Metal rendering API.")
}
else -> return bestRenderApiForCurrentOS()
}
}
private fun bestRenderApiForCurrentOS(): GraphicsApi {
when(hostOs) {
OS.MacOS -> return GraphicsApi.OPENGL
OS.Linux -> return GraphicsApi.OPENGL
OS.Windows -> return GraphicsApi.DIRECT3D
}
}
val fallbackRenderApiQueue : List<GraphicsApi> by lazy {
val head = renderApi
var renderApiList = mutableListOf<GraphicsApi>()
when (hostOs) {
OS.MacOS -> renderApiList = mutableListOf(GraphicsApi.OPENGL, GraphicsApi.SOFTWARE)
OS.Linux -> renderApiList = mutableListOf(GraphicsApi.OPENGL, GraphicsApi.SOFTWARE)
OS.Windows -> renderApiList = mutableListOf(GraphicsApi.DIRECT3D, GraphicsApi.OPENGL, GraphicsApi.SOFTWARE)
}
renderApiList.remove(head)
listOf(head) + renderApiList
} }
private fun property(name: String, default: Boolean) = lazy { private fun property(name: String, default: Boolean) = lazy {
......
...@@ -6,20 +6,22 @@ import org.jetbrains.skija.DirectContext ...@@ -6,20 +6,22 @@ import org.jetbrains.skija.DirectContext
import org.jetbrains.skija.Picture import org.jetbrains.skija.Picture
import org.jetbrains.skija.Surface import org.jetbrains.skija.Surface
import org.jetbrains.skiko.GraphicsApi import org.jetbrains.skiko.GraphicsApi
import org.jetbrains.skiko.HardwareLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.OS import org.jetbrains.skiko.OS
import org.jetbrains.skiko.SkikoProperties import org.jetbrains.skiko.SkikoProperties
import org.jetbrains.skiko.hostOs import org.jetbrains.skiko.hostOs
import org.jetbrains.skiko.redrawer.Redrawer
internal fun createContextHandler(layer: HardwareLayer): ContextHandler { internal fun createContextHandler(layer: SkiaLayer, renderApi: GraphicsApi): ContextHandler {
return when (SkikoProperties.renderApi) { return when (renderApi) {
GraphicsApi.SOFTWARE -> SoftwareContextHandler(layer) GraphicsApi.SOFTWARE -> SoftwareContextHandler(layer)
GraphicsApi.OPENGL -> OpenGLContextHandler(layer) GraphicsApi.OPENGL -> OpenGLContextHandler(layer)
else -> TODO("Unsupported yet") GraphicsApi.DIRECT3D -> Direct3DContextHandler(layer)
else -> TODO("Unsupported yet.")
} }
} }
internal abstract class ContextHandler(val layer: HardwareLayer) { internal abstract class ContextHandler(val layer: SkiaLayer) {
open val bleachConstant = if (hostOs == OS.MacOS) 0 else -1 open val bleachConstant = if (hostOs == OS.MacOS) 0 else -1
var context: DirectContext? = null var context: DirectContext? = null
var renderTarget: BackendRenderTarget? = null var renderTarget: BackendRenderTarget? = null
......
package org.jetbrains.skiko.context
import java.lang.ref.Reference
import org.jetbrains.skija.ColorSpace
import org.jetbrains.skija.FramebufferFormat
import org.jetbrains.skija.Picture
import org.jetbrains.skija.Surface
import org.jetbrains.skija.SurfaceColorFormat
import org.jetbrains.skija.SurfaceOrigin
import org.jetbrains.skija.impl.Native
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.redrawer.Direct3DRedrawer
import org.jetbrains.skiko.redrawer.Redrawer
internal class Direct3DContextHandler(layer: SkiaLayer) : ContextHandler(layer) {
val directXRedrawer: Direct3DRedrawer
get() = layer.redrawer!! as Direct3DRedrawer
var device: Long = 0
override fun initContext(): Boolean {
try {
if (context == null) {
device = directXRedrawer.createDevice()
if (device == 0L) {
throw Exception("Failed to create DirectX12 device.")
}
context = directXRedrawer.makeContext(device)
}
} catch (e: Exception) {
println("${e.message}\nFailed to create Skia Direct3D context!")
return false
}
return true
}
override fun initCanvas() {
dispose()
val scale = layer.contentScale
val w = (layer.width * scale).toInt().coerceAtLeast(0)
val h = (layer.height * scale).toInt().coerceAtLeast(0)
renderTarget = directXRedrawer.makeRenderTarget(device, w, h)
surface = Surface.makeFromBackendRenderTarget(
context!!,
renderTarget!!,
SurfaceOrigin.TOP_LEFT,
SurfaceColorFormat.RGBA_8888,
ColorSpace.getSRGB()
)
canvas = surface!!.canvas
}
override fun flush() {
try {
directXRedrawer.finishFrame(
device,
Native.getPtr(context!!),
Native.getPtr(surface!!)
)
} finally {
Reference.reachabilityFence(context!!)
Reference.reachabilityFence(surface!!)
}
}
}
...@@ -5,11 +5,12 @@ import org.jetbrains.skija.Surface ...@@ -5,11 +5,12 @@ import org.jetbrains.skija.Surface
import org.jetbrains.skija.SurfaceColorFormat import org.jetbrains.skija.SurfaceColorFormat
import org.jetbrains.skija.SurfaceOrigin import org.jetbrains.skija.SurfaceOrigin
import org.jetbrains.skiko.GraphicsApi import org.jetbrains.skiko.GraphicsApi
import org.jetbrains.skiko.HardwareLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.makeMetalContext import org.jetbrains.skiko.makeMetalContext
import org.jetbrains.skiko.makeMetalRenderTarget import org.jetbrains.skiko.makeMetalRenderTarget
import org.jetbrains.skiko.redrawer.Redrawer
internal class MetalContextHandler(layer: HardwareLayer) : ContextHandler(layer) { internal class MetalContextHandler(layer: SkiaLayer) : ContextHandler(layer) {
override fun initContext(): Boolean { override fun initContext(): Boolean {
try { try {
if (context == null) { if (context == null) {
......
...@@ -6,12 +6,13 @@ import org.jetbrains.skija.Picture ...@@ -6,12 +6,13 @@ import org.jetbrains.skija.Picture
import org.jetbrains.skija.Surface import org.jetbrains.skija.Surface
import org.jetbrains.skija.SurfaceColorFormat import org.jetbrains.skija.SurfaceColorFormat
import org.jetbrains.skija.SurfaceOrigin import org.jetbrains.skija.SurfaceOrigin
import org.jetbrains.skiko.HardwareLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.makeGLContext import org.jetbrains.skiko.makeGLContext
import org.jetbrains.skiko.makeGLRenderTarget import org.jetbrains.skiko.makeGLRenderTarget
import org.jetbrains.skiko.OpenGLApi import org.jetbrains.skiko.OpenGLApi
import org.jetbrains.skiko.redrawer.Redrawer
internal class OpenGLContextHandler(layer: HardwareLayer) : ContextHandler(layer) { internal class OpenGLContextHandler(layer: SkiaLayer) : ContextHandler(layer) {
override fun initContext(): Boolean { override fun initContext(): Boolean {
try { try {
if (context == null) { if (context == null) {
......
...@@ -14,11 +14,12 @@ import java.awt.image.Raster ...@@ -14,11 +14,12 @@ import java.awt.image.Raster
import org.jetbrains.skija.Bitmap import org.jetbrains.skija.Bitmap
import org.jetbrains.skija.ColorAlphaType import org.jetbrains.skija.ColorAlphaType
import org.jetbrains.skija.Canvas import org.jetbrains.skija.Canvas
import org.jetbrains.skiko.HardwareLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skija.ImageInfo import org.jetbrains.skija.ImageInfo
import org.jetbrains.skija.Picture import org.jetbrains.skija.Picture
import org.jetbrains.skiko.redrawer.Redrawer
internal class SoftwareContextHandler(layer: HardwareLayer) : ContextHandler(layer) { internal class SoftwareContextHandler(layer: SkiaLayer) : ContextHandler(layer) {
override val bleachConstant = -1 // it looks like java.awt.Canvas doesn't support transparency override val bleachConstant = -1 // it looks like java.awt.Canvas doesn't support transparency
val colorModel = ComponentColorModel( val colorModel = ComponentColorModel(
......
package org.jetbrains.skiko.redrawer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.swing.Swing
import org.jetbrains.skija.BackendRenderTarget
import org.jetbrains.skija.DirectContext
import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.SkiaLayerProperties
internal class Direct3DRedrawer(
private val layer: HardwareLayer,
private val properties: SkiaLayerProperties
) : Redrawer {
private var isDisposed = false
private var device: Long = 0
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
update(System.nanoTime())
draw()
}
override fun dispose() {
frameDispatcher.cancel()
disposeDevice(device)
isDisposed = true
}
override fun needRedraw() {
check(!isDisposed)
frameDispatcher.scheduleFrame()
}
override fun redrawImmediately() {
check(!isDisposed)
update(System.nanoTime())
draw()
}
private fun update(nanoTime: Long) {
layer.update(nanoTime)
}
private fun draw() {
layer.draw()
}
fun makeContext(device: Long) = DirectContext(
makeDirectXContext(device)
)
fun makeRenderTarget(device: Long, width: Int, height: Int) = BackendRenderTarget(
makeDirectXRenderTarget(device, width, height)
)
fun createDevice(): Long {
device = createDirectXDevice(layer.windowHandle)
return device
}
fun finishFrame(device: Long, context: Long, surface: Long) {
finishFrame(device, context, surface, properties.isVsyncEnabled)
}
external fun createDirectXDevice(windowHandle: Long): Long
external fun makeDirectXContext(device: Long): Long
external fun makeDirectXRenderTarget(device: Long, width: Int, height: Int): Long
external fun resizeBuffers(device: Long, width: Int, height: Int)
private external fun finishFrame(device: Long, context: Long, surface: Long, isVsyncEnabled: Boolean)
external fun disposeDevice(device: Long)
}
...@@ -5,7 +5,7 @@ import kotlinx.coroutines.swing.Swing ...@@ -5,7 +5,7 @@ import kotlinx.coroutines.swing.Swing
import org.jetbrains.skiko.FrameDispatcher import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.HardwareLayer import org.jetbrains.skiko.HardwareLayer
internal class RasterRedrawer( internal class SoftwareRedrawer(
private val layer: HardwareLayer private val layer: HardwareLayer
) : Redrawer { ) : Redrawer {
......
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