Skip to content

Commit

Permalink
Merge pull request #63 from JetBrains/directx
Browse files Browse the repository at this point in the history
DirectX rendering implementation.
  • Loading branch information
Rsedaikin authored Feb 19, 2021
2 parents 20bba69 + 21f890d commit f1ce1d4
Show file tree
Hide file tree
Showing 14 changed files with 596 additions and 41 deletions.
1 change: 1 addition & 0 deletions skiko/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions skiko/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ kotlin.code.style=official
deploy.version=0.0.0

dependencies.skija.git.commit=b85e3e7a70204281cf0036d5595a8eed68bacd2c
dependencies.skia.windows-x64=m89-15595ea39c/Skia-m89-15595ea39c-windows-Release-x64
dependencies.skia.linux-x64=m89-15595ea39c/Skia-m89-15595ea39c-linux-Release-x64
dependencies.skia.macos-x64=m89-15595ea39c/Skia-m89-15595ea39c-macos-Release-x64
dependencies.skia.macos-arm64=m89-15595ea39c/Skia-m89-15595ea39c-macos-Release-arm64
dependencies.skia.windows-x64=m89-19889d74fa/Skia-m89-19889d74fa-windows-Release-x64
dependencies.skia.linux-x64=m89-19889d74fa/Skia-m89-19889d74fa-linux-Release-x64
dependencies.skia.macos-x64=m89-19889d74fa/Skia-m89-19889d74fa-macos-Release-x64
dependencies.skia.macos-arm64=m89-19889d74fa/Skia-m89-19889d74fa-macos-Release-arm64

# signer=Apple Distribution: Nikolay Igotti (N462MKSJ7M)
357 changes: 357 additions & 0 deletions skiko/src/jvmMain/cpp/windows/directXRedrawer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,357 @@
#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
File renamed without changes.
Loading

0 comments on commit f1ce1d4

Please sign in to comment.