diff --git a/CMakeLists.txt b/CMakeLists.txt index 301748804..a5e91fb00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -739,11 +739,15 @@ endif() if (WITH_RENDER_DX11) if (WIN32) - list(APPEND GUI_SOURCES src/gui/render/renderDX11.cpp) - list(APPEND GUI_SOURCES extern/imgui_patched/backends/imgui_impl_dx11.cpp) - list(APPEND DEPENDENCIES_DEFINES HAVE_RENDER_DX11) - list(APPEND DEPENDENCIES_LIBRARIES d3d11 d3dcompiler) - message(STATUS "UI render backend: DirectX 11") + if (SUPPORT_XP) + message(FATAL_ERROR "SUPPORT_XP is on. cannot enable DirectX 11 backend.") + else() + list(APPEND GUI_SOURCES src/gui/render/renderDX11.cpp) + list(APPEND GUI_SOURCES extern/imgui_patched/backends/imgui_impl_dx11.cpp) + list(APPEND DEPENDENCIES_DEFINES HAVE_RENDER_DX11) + list(APPEND DEPENDENCIES_LIBRARIES d3d11) + message(STATUS "UI render backend: DirectX 11") + endif() else() message(FATAL_ERROR "DirectX 11 render backend only for Windows!") endif() diff --git a/extern/imgui_patched/backends/imgui_impl_dx11.cpp b/extern/imgui_patched/backends/imgui_impl_dx11.cpp index fa60d9ebc..862769686 100644 --- a/extern/imgui_patched/backends/imgui_impl_dx11.cpp +++ b/extern/imgui_patched/backends/imgui_impl_dx11.cpp @@ -32,16 +32,16 @@ // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2016-05-07: DirectX11: Disabling depth-write. +// DISCLAIMER: modified with d3dcompiler patch (see https://github.com/ocornut/imgui/pull/638). + #include "imgui.h" #include "imgui_impl_dx11.h" // DirectX #include #include -#include -#ifdef _MSC_VER -#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. -#endif + +typedef HRESULT (__stdcall *D3DCompile_t)(LPCVOID, SIZE_T, LPCSTR, D3D_SHADER_MACRO*, ID3DInclude*, LPCSTR, LPCSTR, UINT, UINT, ID3DBlob**, ID3DBlob*); // DirectX11 data struct ImGui_ImplDX11_Data @@ -380,11 +380,22 @@ bool ImGui_ImplDX11_CreateDeviceObjects() if (bd->pFontSampler) ImGui_ImplDX11_InvalidateDeviceObjects(); - // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) - // If you would like to use this DX11 sample code but remove this dependency you can: - // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution] - // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. // See https://github.com/ocornut/imgui/pull/638 for sources and details. + // Detect which d3dcompiler_XX.dll is present in the system and grab a pointer to D3DCompile. + // Without this, you must link d3dcompiler.lib with the project. + D3DCompile_t D3DCompile = NULL; + { + char dllBuffer[20]; + for (int i = 47; i > 30 && !D3DCompile; i--) + { + sprintf(dllBuffer, "d3dcompiler_%d.dll", i); + HMODULE hDll = LoadLibraryA(dllBuffer); + if (hDll) + D3DCompile = (D3DCompile_t)GetProcAddress(hDll, "D3DCompile"); + } + if (!D3DCompile) + return false; + } // Create the vertex shader { diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 6ea8a941a..620ad36ae 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -6237,9 +6237,9 @@ bool FurnaceGUI::init() { if (!rend->init(sdlWin)) { if (settings.renderBackend!="SDL" && !settings.renderBackend.empty()) { settings.renderBackend=""; - e->setConf("renderBackend",""); - e->saveConf(); - lastError=fmt::sprintf("\r\nthe render backend has been set to a safe value. please restart Furnace."); + //e->setConf("renderBackend",""); + //e->saveConf(); + //lastError=fmt::sprintf("\r\nthe render backend has been set to a safe value. please restart Furnace."); } else { lastError=fmt::sprintf("could not init renderer! %s",SDL_GetError()); if (!settings.renderDriver.empty()) { diff --git a/src/gui/render/renderDX11.cpp b/src/gui/render/renderDX11.cpp index 1133f842a..1ed6c9cce 100644 --- a/src/gui/render/renderDX11.cpp +++ b/src/gui/render/renderDX11.cpp @@ -23,6 +23,47 @@ #include "backends/imgui_impl_dx11.h" #include "../../ta-log.h" +typedef HRESULT (__stdcall *D3DCompile_t)(LPCVOID,SIZE_T,LPCSTR,D3D_SHADER_MACRO*,ID3DInclude*,LPCSTR,LPCSTR,UINT,UINT,ID3DBlob**,ID3DBlob*); + +const char* shD3D11_wipe_srcV= + "cbuffer WipeUniform: register(b0) {\n" + " float alpha;\n" + " float padding1;\n" + " float padding2;\n" + " float padding3;\n" + " float4 padding4;\n" + "};\n" + "\n" + "struct vsInput {\n" + " float4 pos: POSITION;\n" + "};\n" + "\n" + "struct fsInput {\n" + " float4 pos: SV_POSITION;\n" + " float4 color: COLOR0;\n" + "};\n" + "\n" + "fsInput main(vsInput input) {\n" + " fsInput output;\n" + " output.pos=input.pos;\n" + " output.color=float4(0.0f,0.0f,0.0f,alpha);\n" + " return output;\n" + "}"; + +const char* shD3D11_wipe_srcF= + "struct fsInput {\n" + " float4 pos: SV_POSITION;\n" + " float4 color: COLOR0;\n" + "};\n" + "\n" + "float4 main(fsInput input): SV_Target {\n" + " return input.color;\n" + "}"; + +const D3D11_INPUT_ELEMENT_DESC shD3D11_wipe_inputLayout={ + "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 +}; + const D3D_FEATURE_LEVEL possibleFeatureLevels[2]={ D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0 @@ -237,8 +278,43 @@ void FurnaceGUIRenderDX11::renderGUI() { ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); } +const float blendFactor[4]={ + 1.0f, 1.0f, 1.0f, 1.0f +}; + void FurnaceGUIRenderDX11::wipe(float alpha) { - // TODO + D3D11_VIEWPORT viewPort; + unsigned int strides=4*sizeof(float); + unsigned int offsets=0; + + memset(&viewPort,0,sizeof(viewPort)); + viewPort.TopLeftX=0.0f; + viewPort.TopLeftY=0.0f; + viewPort.Width=outW; + viewPort.Height=outH; + viewPort.MinDepth=0.0f; + viewPort.MaxDepth=1.0f; + + D3D11_MAPPED_SUBRESOURCE mappedUniform; + if (context->Map(sh_wipe_uniform,0,D3D11_MAP_WRITE_DISCARD,0,&mappedUniform)!=S_OK) { + logW("could not map constant"); + } + WipeUniform* sh_wipe_uniformState=(WipeUniform*)mappedUniform.pData; + sh_wipe_uniformState->alpha=alpha; + context->Unmap(sh_wipe_uniform,0); + + context->RSSetViewports(1,&viewPort); + context->RSSetState(rsState); + + context->OMSetBlendState(omBlendState,blendFactor,0xffffffff); + context->IASetInputLayout(sh_wipe_inputLayout); + context->IASetVertexBuffers(0,1,&quadVertex,&strides,&offsets); + context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + context->VSSetShader(sh_wipe_vertex,NULL,0); + context->VSSetConstantBuffers(0,1,&sh_wipe_uniform); + context->PSSetShader(sh_wipe_fragment,NULL,0); + + context->Draw(4,0); } void FurnaceGUIRenderDX11::present() { @@ -258,6 +334,13 @@ int FurnaceGUIRenderDX11::getWindowFlags() { void FurnaceGUIRenderDX11::preInit() { } +const float wipeVertices[4][4]={ + -1.0, -1.0, 0.0, 1.0, + 1.0, -1.0, 0.0, 1.0, + -1.0, 1.0, 0.0, 1.0, + 1.0, 1.0, 0.0, 1.0 +}; + bool FurnaceGUIRenderDX11::init(SDL_Window* win) { SDL_SysWMinfo sysWindow; D3D_FEATURE_LEVEL featureLevel; @@ -291,6 +374,131 @@ bool FurnaceGUIRenderDX11::init(SDL_Window* win) { return false; } + // https://github.com/ocornut/imgui/pull/638 + D3DCompile_t D3DCompile=NULL; + char dllBuffer[20]; + for (int i=47; (i>30 && !D3DCompile); i--) { + snprintf(dllBuffer,20,"d3dcompiler_%d.dll",i); + HMODULE hDll=LoadLibraryA(dllBuffer); + if (hDll) { + D3DCompile=(D3DCompile_t)GetProcAddress(hDll,"D3DCompile"); + } + } + if (!D3DCompile) { + logE("could not find D3DCompile!"); + return false; + } + + // create wipe shader + ID3DBlob* wipeBlobV=NULL; + ID3DBlob* wipeBlobF=NULL; + D3D11_BUFFER_DESC wipeConstantDesc; + + result=D3DCompile(shD3D11_wipe_srcV,strlen(shD3D11_wipe_srcV),NULL,NULL,NULL,"main","vs_4_0",0,0,&wipeBlobV,NULL); + if (result!=S_OK) { + logE("could not compile vertex shader! %.8x",result); + return false; + } + result=D3DCompile(shD3D11_wipe_srcF,strlen(shD3D11_wipe_srcF),NULL,NULL,NULL,"main","ps_4_0",0,0,&wipeBlobF,NULL); + if (result!=S_OK) { + logE("could not compile pixel shader! %.8x",result); + return false; + } + + result=device->CreateVertexShader(wipeBlobV->GetBufferPointer(),wipeBlobV->GetBufferSize(),NULL,&sh_wipe_vertex); + if (result!=S_OK) { + logE("could not create vertex shader! %.8x",result); + return false; + } + result=device->CreatePixelShader(wipeBlobF->GetBufferPointer(),wipeBlobF->GetBufferSize(),NULL,&sh_wipe_fragment); + if (result!=S_OK) { + logE("could not create pixel shader! %.8x",result); + return false; + } + + result=device->CreateInputLayout(&shD3D11_wipe_inputLayout,1,wipeBlobV->GetBufferPointer(),wipeBlobV->GetBufferSize(),&sh_wipe_inputLayout); + if (result!=S_OK) { + logE("could not create input layout! %.8x",result); + return false; + } + + memset(&wipeConstantDesc,0,sizeof(wipeConstantDesc)); + wipeConstantDesc.ByteWidth=sizeof(WipeUniform); + wipeConstantDesc.Usage=D3D11_USAGE_DYNAMIC; + wipeConstantDesc.BindFlags=D3D11_BIND_CONSTANT_BUFFER; + wipeConstantDesc.CPUAccessFlags=D3D11_CPU_ACCESS_WRITE; + wipeConstantDesc.MiscFlags=0; + wipeConstantDesc.StructureByteStride=0; + + result=device->CreateBuffer(&wipeConstantDesc,NULL,&sh_wipe_uniform); + if (result!=S_OK) { + logE("could not create constant buffer! %.8x",result); + return false; + } + + // create wipe vertices + D3D11_BUFFER_DESC vertexDesc; + D3D11_SUBRESOURCE_DATA vertexRes; + + memset(&vertexDesc,0,sizeof(vertexDesc)); + memset(&vertexRes,0,sizeof(vertexRes)); + + vertexDesc.ByteWidth=4*4*sizeof(float); + vertexDesc.Usage=D3D11_USAGE_DEFAULT; + vertexDesc.BindFlags=D3D11_BIND_VERTEX_BUFFER; + vertexDesc.CPUAccessFlags=0; + vertexDesc.MiscFlags=0; + vertexDesc.StructureByteStride=0; + + vertexRes.pSysMem=wipeVertices; + vertexRes.SysMemPitch=0; + vertexRes.SysMemSlicePitch=0; + + result=device->CreateBuffer(&vertexDesc,&vertexRes,&quadVertex); + if (result!=S_OK) { + logE("could not create vertex buffer! %.8x",result); + return false; + } + + // initialize the rest + D3D11_RASTERIZER_DESC rasterDesc; + D3D11_BLEND_DESC blendDesc; + + memset(&rasterDesc,0,sizeof(rasterDesc)); + memset(&blendDesc,0,sizeof(blendDesc)); + + rasterDesc.FillMode=D3D11_FILL_SOLID; + rasterDesc.CullMode=D3D11_CULL_NONE; + rasterDesc.FrontCounterClockwise=false; + rasterDesc.DepthBias=0; + rasterDesc.DepthBiasClamp=0.0f; + rasterDesc.SlopeScaledDepthBias=0.0f; + rasterDesc.DepthClipEnable=false; + rasterDesc.ScissorEnable=false; + rasterDesc.MultisampleEnable=false; + rasterDesc.AntialiasedLineEnable=false; + result=device->CreateRasterizerState(&rasterDesc,&rsState); + if (result!=S_OK) { + logE("could not create rasterizer state! %.8x",result); + return false; + } + + blendDesc.AlphaToCoverageEnable=false; + blendDesc.IndependentBlendEnable=false; + blendDesc.RenderTarget[0].BlendEnable=true; + blendDesc.RenderTarget[0].SrcBlend=D3D11_BLEND_SRC_ALPHA; + blendDesc.RenderTarget[0].DestBlend=D3D11_BLEND_INV_SRC_ALPHA; + blendDesc.RenderTarget[0].BlendOp=D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].SrcBlendAlpha=D3D11_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlendAlpha=D3D11_BLEND_INV_SRC_ALPHA; + blendDesc.RenderTarget[0].BlendOpAlpha=D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].RenderTargetWriteMask=D3D11_COLOR_WRITE_ENABLE_ALL; + result=device->CreateBlendState(&blendDesc,&omBlendState); + if (result!=S_OK) { + logE("could not create blend state! %.8x",result); + return false; + } + createRenderTarget(); return true; } diff --git a/src/gui/render/renderDX11.h b/src/gui/render/renderDX11.h index 19cfb6f00..18d8673dd 100644 --- a/src/gui/render/renderDX11.h +++ b/src/gui/render/renderDX11.h @@ -23,6 +23,12 @@ #else typedef void ID3D11DeviceContext; typedef void ID3D11RenderTargetView; +typedef void ID3D11Buffer; +typedef void ID3D11RasterizerState; +typedef void ID3D11BlendState; +typedef void ID3D11VertexShader; +typedef void ID3D11PixelShader; +typedef void ID3D11InputLayout; typedef void IDXGISwapChain; #endif @@ -33,8 +39,22 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender { ID3D11DeviceContext* context; ID3D11RenderTargetView* renderTarget; IDXGISwapChain* swapchain; + ID3D11RasterizerState* rsState; + ID3D11BlendState* omBlendState; + + ID3D11Buffer* quadVertex; int outW, outH; - float quadVertex[4][3]; + + // SHADERS // + // -> wipe + ID3D11VertexShader* sh_wipe_vertex; + ID3D11PixelShader* sh_wipe_fragment; + ID3D11InputLayout* sh_wipe_inputLayout; + ID3D11Buffer* sh_wipe_uniform; + struct WipeUniform { + float alpha; + float padding[7]; + }; bool destroyRenderTarget(); bool createRenderTarget(); @@ -70,8 +90,14 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender { context(NULL), renderTarget(NULL), swapchain(NULL), + rsState(NULL), + omBlendState(NULL), + quadVertex(NULL), outW(0), - outH(0) { - memset(quadVertex,0,4*3*sizeof(float)); + outH(0), + sh_wipe_vertex(NULL), + sh_wipe_fragment(NULL), + sh_wipe_inputLayout(NULL), + sh_wipe_uniform(NULL) { } };