Commit e1188e24 authored by spvessel's avatar spvessel

DirectX rendering implementation.

parent b6e0411e
......@@ -268,6 +268,7 @@ tasks.withType(CppCompile::class.java).configureEach {
"-DWIN32_LEAN_AND_MEAN",
"-DNOMINMAX",
"-DSK_GAMMA_APPLY_TO_A8",
"-DSK_DIRECT3D",
"/utf-8",
"/GR-", // no-RTTI.
*buildType.msvcFlags
......@@ -380,7 +381,10 @@ tasks.withType(LinkSharedLibrary::class.java).configureEach {
"Dwmapi.lib",
"opengl32.lib",
"shcore.lib",
"user32.lib"
"user32.lib",
"d3d12.lib",
"dxgi.lib",
"d3dcompiler.lib"
)
)
}
......
#ifdef SK_DIRECT3D
#include <windows.h>
#include <jawt_md.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); \
} \
} while (false)
extern "C" jboolean Skiko_GetAWT(JNIEnv *env, JAWT *awt);
class DirectXDevice
{
public:
GrD3DBackendContext backendContext;
gr_cp<ID3D12Device> device;
gr_cp<IDXGISwapChain3> swapChain;
gr_cp<ID3D12CommandQueue> queue;
gr_cp<ID3D12Fence> fence;
HANDLE fenceEvent;
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;
}
};
extern "C"
{
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_makeDirectXContext(
JNIEnv* env, jobject redrawer, jlong devicePtr)
{
DirectXDevice *d3dDevice = reinterpret_cast<DirectXDevice *>(static_cast<uintptr_t>(devicePtr));
GrD3DBackendContext backendContext = d3dDevice->backendContext;
return reinterpret_cast<jlong>(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 = reinterpret_cast<DirectXDevice *>(static_cast<uintptr_t>(devicePtr));
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 reinterpret_cast<jlong>(renderTarget);
}
void 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;
}
pAdapter->Release();
}
}
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_createDevice(
JNIEnv *env, jobject redrawer)
{
DirectXDevice *d3dDevice = new DirectXDevice();
gr_cp<IDXGIFactory4> factory;
if (!SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&factory))))
{
return false;
}
gr_cp<IDXGIAdapter1> hardwareAdapter;
defineHardwareAdapter(factory.get(), &hardwareAdapter);
gr_cp<ID3D12Device> device;
if (!SUCCEEDED(D3D12CreateDevice(hardwareAdapter.get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device))))
{
return false;
}
// 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 false;
}
d3dDevice->backendContext.fAdapter = hardwareAdapter;
d3dDevice->backendContext.fDevice = device;
d3dDevice->backendContext.fQueue = queue;
d3dDevice->device = device;
d3dDevice->queue = queue;
return reinterpret_cast<jlong>(d3dDevice);
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_createSwapChain(
JNIEnv *env, jobject redrawer, jlong windowHandle, jlong devicePtr)
{
DirectXDevice *d3dDevice = reinterpret_cast<DirectXDevice *>(static_cast<uintptr_t>(devicePtr));
// Make the swapchain
HWND fWindow = (HWND)windowHandle;
gr_cp<IDXGIFactory4> factory;
GR_D3D_CALL_ERRCHECK(CreateDXGIFactory2(0, IID_PPV_ARGS(&factory)));
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
swapChainDesc.BufferCount = 2;
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.SampleDesc.Count = 1;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC swapChainFSDesc = {};
swapChainFSDesc.Windowed = TRUE;
gr_cp<IDXGISwapChain1> swapChain;
GR_D3D_CALL_ERRCHECK(factory->CreateSwapChainForHwnd(d3dDevice->queue.get(), fWindow, &swapChainDesc, &swapChainFSDesc, nullptr, &swapChain));
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);
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_redrawer_Direct3DRedrawer_resizeBuffers(
JNIEnv *env, jobject redrawer, jlong devicePtr, jint width, jint height)
{
DirectXDevice *d3dDevice = reinterpret_cast<DirectXDevice *>(static_cast<uintptr_t>(devicePtr));
if (!d3dDevice->isSizeEqualTo(width, height))
{
GR_D3D_CALL_ERRCHECK(d3dDevice->swapChain->ResizeBuffers(2, 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)
{
DirectXDevice *d3dDevice = reinterpret_cast<DirectXDevice *>(static_cast<uintptr_t>(devicePtr));
SkSurface *surface = reinterpret_cast<SkSurface *>(static_cast<uintptr_t>(surfacePtr));
surface->flushAndSubmit();
GrDirectContext *fContext = reinterpret_cast<GrDirectContext *>(static_cast<uintptr_t>(contextPtr));
surface->flush(SkSurface::BackendSurfaceAccess::kPresent, GrFlushInfo());
fContext->flush({});
fContext->submit(true);
GR_D3D_CALL_ERRCHECK(d3dDevice->swapChain->Present(1, 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);
}
}
}
#endif
......@@ -3,9 +3,10 @@ package org.jetbrains.skiko
import org.jetbrains.skiko.SkikoProperties.renderApi
import org.jetbrains.skiko.redrawer.LinuxOpenGLRedrawer
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.WindowsOpenGLRedrawer
import org.jetbrains.skiko.redrawer.Direct3DRedrawer
import java.awt.Component
import java.awt.Window
import javax.swing.SwingUtilities
......@@ -33,7 +34,7 @@ internal val platformOperations: PlatformOperations by lazy {
}
override fun createRedrawer(layer: HardwareLayer) = when(renderApi) {
GraphicsApi.SOFTWARE -> RasterRedrawer(layer)
GraphicsApi.SOFTWARE -> SoftwareRedrawer(layer)
else -> MacOsOpenGLRedrawer(layer)
}
}
......@@ -56,7 +57,8 @@ internal val platformOperations: PlatformOperations by lazy {
}
override fun createRedrawer(layer: HardwareLayer) = when(renderApi) {
GraphicsApi.SOFTWARE -> RasterRedrawer(layer)
GraphicsApi.SOFTWARE -> SoftwareRedrawer(layer)
GraphicsApi.DIRECT3D -> Direct3DRedrawer(layer)
else -> WindowsOpenGLRedrawer(layer)
}
}
......@@ -92,7 +94,7 @@ internal val platformOperations: PlatformOperations by lazy {
}
override fun createRedrawer(layer: HardwareLayer) = when(renderApi) {
GraphicsApi.SOFTWARE -> RasterRedrawer(layer)
GraphicsApi.SOFTWARE -> SoftwareRedrawer(layer)
else -> LinuxOpenGLRedrawer(layer)
}
}
......
......@@ -7,7 +7,7 @@ import org.jetbrains.skija.PictureRecorder
import org.jetbrains.skija.Rect
import org.jetbrains.skiko.context.SoftwareContextHandler
import org.jetbrains.skiko.context.createContextHandler
import org.jetbrains.skiko.redrawer.RasterRedrawer
import org.jetbrains.skiko.redrawer.SoftwareRedrawer
import org.jetbrains.skiko.redrawer.Redrawer
import java.awt.Graphics
import javax.swing.SwingUtilities.isEventDispatchThread
......@@ -22,7 +22,7 @@ open class SkiaLayer : HardwareLayer() {
var renderer: SkiaRenderer? = null
val clipComponents = mutableListOf<ClipRectangle>()
internal var skijaState = createContextHandler(this)
private var contextHandler = createContextHandler(this)
@Volatile
private var isDisposed = false
......@@ -102,8 +102,8 @@ open class SkiaLayer : HardwareLayer() {
override fun draw() {
check(!isDisposed)
skijaState.apply {
if (!initContext()) {
contextHandler.apply {
if (!initContext(redrawer!!)) {
fallbackToRaster()
return
}
......@@ -136,8 +136,8 @@ open class SkiaLayer : HardwareLayer() {
private fun fallbackToRaster() {
println("Falling back to software rendering...")
redrawer?.dispose()
skijaState = SoftwareContextHandler(this)
redrawer = RasterRedrawer(this)
contextHandler = SoftwareContextHandler(this)
redrawer = SoftwareRedrawer(this)
needRedraw()
}
}
......@@ -29,7 +29,21 @@ internal object SkikoProperties {
when(text) {
"SOFTWARE" -> return GraphicsApi.SOFTWARE
"OPENGL" -> return GraphicsApi.OPENGL
else -> return GraphicsApi.OPENGL
"DIRECT3D" -> {
return if (hostOs == OS.Windows) GraphicsApi.DIRECT3D else bestRenderApiForCurrentOS()
}
"METAL" -> {
return if (hostOs == OS.MacOS) GraphicsApi.METAL else bestRenderApiForCurrentOS()
}
else -> return bestRenderApiForCurrentOS()
}
}
private fun bestRenderApiForCurrentOS(): GraphicsApi {
when(hostOs) {
OS.MacOS -> return GraphicsApi.OPENGL
OS.Linux -> return GraphicsApi.OPENGL
OS.Windows -> return GraphicsApi.DIRECT3D
}
}
......
......@@ -10,11 +10,13 @@ import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.OS
import org.jetbrains.skiko.SkikoProperties
import org.jetbrains.skiko.hostOs
import org.jetbrains.skiko.redrawer.Redrawer
internal fun createContextHandler(layer: HardwareLayer): ContextHandler {
return when (SkikoProperties.renderApi) {
GraphicsApi.SOFTWARE -> SoftwareContextHandler(layer)
GraphicsApi.OPENGL -> OpenGLContextHandler(layer)
GraphicsApi.DIRECT3D -> Direct3DContextHandler(layer)
else -> TODO("Unsupported yet")
}
}
......@@ -26,7 +28,7 @@ internal abstract class ContextHandler(val layer: HardwareLayer) {
var surface: Surface? = null
var canvas: Canvas? = null
abstract fun initContext(): Boolean
abstract fun initContext(redrawer: Redrawer): Boolean
abstract fun initCanvas()
......@@ -46,4 +48,4 @@ internal abstract class ContextHandler(val layer: HardwareLayer) {
surface?.close()
renderTarget?.close()
}
}
\ No newline at end of file
}
package org.jetbrains.skiko.context
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.HardwareLayer
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.redrawer.Direct3DRedrawer
import org.jetbrains.skiko.redrawer.Redrawer
internal class Direct3DContextHandler(layer: HardwareLayer) : ContextHandler(layer) {
lateinit var directXRedrawer: Direct3DRedrawer
var device: Long = 0
override fun initContext(redrawer: Redrawer): Boolean {
try {
if (!this::directXRedrawer.isInitialized) {
directXRedrawer = redrawer as Direct3DRedrawer
}
if (context == null) {
device = directXRedrawer.createDevice()
directXRedrawer.createSwapChain(layer.windowHandle, device)
context = directXRedrawer.makeContext(device)
}
} catch (e: Exception) {
println("Failed to create Skia Direct3D context!\n ${e.stackTrace}")
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)
directXRedrawer.resizeBuffers(device, w, h)
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() {
directXRedrawer.finishFrame(
device,
Native.getPtr(context!!),
Native.getPtr(surface!!)
);
}
}
......@@ -8,9 +8,10 @@ import org.jetbrains.skiko.GraphicsApi
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.makeMetalContext
import org.jetbrains.skiko.makeMetalRenderTarget
import org.jetbrains.skiko.redrawer.Redrawer
internal class MetalContextHandler(layer: HardwareLayer) : ContextHandler(layer) {
override fun initContext(): Boolean {
override fun initContext(redrawer: Redrawer): Boolean {
try {
if (context == null) {
context = makeMetalContext()
......
......@@ -10,9 +10,10 @@ import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skiko.makeGLContext
import org.jetbrains.skiko.makeGLRenderTarget
import org.jetbrains.skiko.OpenGLApi
import org.jetbrains.skiko.redrawer.Redrawer
internal class OpenGLContextHandler(layer: HardwareLayer) : ContextHandler(layer) {
override fun initContext(): Boolean {
override fun initContext(redrawer: Redrawer): Boolean {
try {
if (context == null) {
context = makeGLContext()
......
......@@ -17,6 +17,7 @@ import org.jetbrains.skija.Canvas
import org.jetbrains.skiko.HardwareLayer
import org.jetbrains.skija.ImageInfo
import org.jetbrains.skija.Picture
import org.jetbrains.skiko.redrawer.Redrawer
internal class SoftwareContextHandler(layer: HardwareLayer) : ContextHandler(layer) {
override val bleachConstant = -1 // it looks like java.awt.Canvas doesn't support transparency
......@@ -33,7 +34,7 @@ internal class SoftwareContextHandler(layer: HardwareLayer) : ContextHandler(lay
var imageData: ByteArray? = null
var raster: WritableRaster? = null
override fun initContext(): Boolean {
override fun initContext(redrawer: Redrawer): Boolean {
// Raster does not need context
return true
}
......
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
internal class Direct3DRedrawer(
private val layer: HardwareLayer
) : Redrawer {
private val frameDispatcher = FrameDispatcher(Dispatchers.Swing) {
layer.update(System.nanoTime())
layer.draw()
}
override fun dispose() {
frameDispatcher.cancel()
}
override fun needRedraw() {
frameDispatcher.scheduleFrame()
}
override fun redrawImmediately() {
layer.update(System.nanoTime())
layer.draw()
}
fun makeContext(device: Long) = DirectContext(
makeDirectXContext(device)
)
fun makeRenderTarget(device: Long, width: Int, height: Int) = BackendRenderTarget(
makeDirectXRenderTarget(device, width, height)
)
external fun createDevice(): Long
external fun makeDirectXContext(device: Long): Long
external fun makeDirectXRenderTarget(device: Long, width: Int, height: Int): Long
external fun createSwapChain(windowHandle: Long, device: Long)
external fun resizeBuffers(device: Long, width: Int, height: Int)
external fun finishFrame(device: Long, context: Long, surface: Long)
}
......@@ -5,7 +5,7 @@ import kotlinx.coroutines.swing.Swing
import org.jetbrains.skiko.FrameDispatcher
import org.jetbrains.skiko.HardwareLayer
internal class RasterRedrawer(
internal class SoftwareRedrawer(
private val layer: HardwareLayer
) : 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