diff --git a/CMakeLists.txt b/CMakeLists.txt index b55f514f..a5e91fb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,11 @@ if (APPLE) else() set(WITH_RENDER_OPENGL_DEFAULT ON) endif() +if (WIN32) + set(WITH_RENDER_DX11_DEFAULT ON) +else() + set(WITH_RENDER_DX11_DEFAULT OFF) +endif() if (ANDROID) set(USE_GLES_DEFAULT ON) @@ -75,6 +80,7 @@ option(USE_BACKWARD "Use backward-cpp to print a backtrace on crash/abort." ${US option(WITH_JACK "Whether to build with JACK support. Auto-detects if JACK is available" ${WITH_JACK_DEFAULT}) option(WITH_RENDER_SDL "Whether to build with the SDL_Renderer render backend." ${WITH_RENDER_SDL_DEFAULT}) option(WITH_RENDER_OPENGL "Whether to build with the OpenGL render backend." ${WITH_RENDER_OPENGL_DEFAULT}) +option(WITH_RENDER_DX11 "Whether to build with the DirectX 11 render backend." ${WITH_RENDER_DX11_DEFAULT}) option(USE_GLES "Use OpenGL ES for the OpenGL render backend." ${USE_GLES_DEFAULT}) option(SYSTEM_FFTW "Use a system-installed version of FFTW instead of the vendored one" OFF) option(SYSTEM_FMT "Use a system-installed version of fmt instead of the vendored one" OFF) @@ -302,7 +308,7 @@ else() endif() if (BUILD_GUI) - if (NOT WITH_RENDER_SDL AND NOT WITH_RENDER_OPENGL) + if (NOT WITH_RENDER_SDL AND NOT WITH_RENDER_OPENGL AND NOT WITH_RENDER_DX11) message(FATAL_ERROR "No render backends selected!") endif() endif() @@ -731,6 +737,22 @@ if (WITH_RENDER_OPENGL) message(STATUS "UI render backend: OpenGL") endif() +if (WITH_RENDER_DX11) + if (WIN32) + 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() +endif() + if (NOT WIN32 AND NOT APPLE) CHECK_INCLUDE_FILE(sys/io.h SYS_IO_FOUND) CHECK_INCLUDE_FILE(linux/input.h LINUX_INPUT_FOUND) diff --git a/demos/msx/OPLL_High_and_Rising.fur b/demos/msx/OPLL_High_and_Rising.fur new file mode 100644 index 00000000..2ecab85b Binary files /dev/null and b/demos/msx/OPLL_High_and_Rising.fur differ diff --git a/extern/imgui_patched/backends/imgui_impl_dx11.cpp b/extern/imgui_patched/backends/imgui_impl_dx11.cpp index fa60d9eb..86276968 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 86edad80..8c14f977 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3420,6 +3420,7 @@ bool FurnaceGUI::loop() { logV("portrait: %d (%dx%d)",portrait,scrW,scrH); logD("window resized to %dx%d",scrW,scrH); updateWindow=true; + rend->resized(ev); break; case SDL_WINDOWEVENT_MOVED: scrX=ev.window.data1; @@ -5824,8 +5825,8 @@ bool FurnaceGUI::loop() { } } } - rend->present(); drawTimeEnd=SDL_GetPerformanceCounter(); + rend->present(); if (settings.renderClearPos) { rend->clear(uiColors[GUI_COLOR_BACKGROUND]); } @@ -6149,7 +6150,7 @@ bool FurnaceGUI::init() { logV("window size: %dx%d",scrW,scrH); if (!initRender()) { - if (settings.renderBackend=="OpenGL") { + if (settings.renderBackend!="SDL" && !settings.renderBackend.empty()) { settings.renderBackend=""; e->setConf("renderBackend",""); e->saveConf(); @@ -6235,11 +6236,11 @@ bool FurnaceGUI::init() { logD("starting render backend..."); if (!rend->init(sdlWin)) { - if (settings.renderBackend=="OpenGL") { + 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/gui.h b/src/gui/gui.h index 452c1da7..3d91e405 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -71,16 +71,22 @@ enum FurnaceGUIRenderBackend { GUI_BACKEND_SDL=0, - GUI_BACKEND_GL + GUI_BACKEND_GL, + GUI_BACKEND_DX11 }; #ifdef HAVE_RENDER_SDL #define GUI_BACKEND_DEFAULT GUI_BACKEND_SDL #define GUI_BACKEND_DEFAULT_NAME "SDL" #else +#ifdef HAVE_RENDER_DX11 +#define GUI_BACKEND_DEFAULT GUI_BACKEND_DX11 +#define GUI_BACKEND_DEFAULT_NAME "DirectX 11" +#else #define GUI_BACKEND_DEFAULT GUI_BACKEND_GL #define GUI_BACKEND_DEFAULT_NAME "OpenGL" #endif +#endif // TODO: // - add colors for FM envelope and waveform @@ -1242,6 +1248,7 @@ class FurnaceGUIRender { virtual bool destroyTexture(void* which); virtual void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode); virtual void setBlendMode(FurnaceGUIBlendMode mode); + virtual void resized(const SDL_Event& ev); virtual void clear(ImVec4 color); virtual bool newFrame(); virtual void createFontsTexture(); diff --git a/src/gui/render.cpp b/src/gui/render.cpp index 908fd57d..5ad753c2 100644 --- a/src/gui/render.cpp +++ b/src/gui/render.cpp @@ -25,12 +25,17 @@ #ifdef HAVE_RENDER_GL #include "render/renderGL.h" #endif +#ifdef HAVE_RENDER_DX11 +#include "render/renderDX11.h" +#endif bool FurnaceGUI::initRender() { if (rend!=NULL) return false; if (settings.renderBackend=="OpenGL") { renderBackend=GUI_BACKEND_GL; + } else if (settings.renderBackend=="DirectX 11") { + renderBackend=GUI_BACKEND_DX11; } else if (settings.renderBackend=="SDL") { renderBackend=GUI_BACKEND_SDL; } else { @@ -44,6 +49,12 @@ bool FurnaceGUI::initRender() { rend=new FurnaceGUIRenderGL; break; #endif +#ifdef HAVE_RENDER_DX11 + case GUI_BACKEND_DX11: + logI("render backend: DirectX 11"); + rend=new FurnaceGUIRenderDX11; + break; +#endif #ifdef HAVE_RENDER_SDL case GUI_BACKEND_SDL: logI("render backend: SDL_Renderer"); diff --git a/src/gui/render/abstract.cpp b/src/gui/render/abstract.cpp index 996cf4e8..a45c4ecd 100644 --- a/src/gui/render/abstract.cpp +++ b/src/gui/render/abstract.cpp @@ -49,6 +49,9 @@ void FurnaceGUIRender::setTextureBlendMode(void* which, FurnaceGUIBlendMode mode void FurnaceGUIRender::setBlendMode(FurnaceGUIBlendMode mode) { } +void FurnaceGUIRender::resized(const SDL_Event& ev) { +} + void FurnaceGUIRender::clear(ImVec4 color) { } @@ -97,4 +100,4 @@ void FurnaceGUIRender::quitGUI() { } FurnaceGUIRender::~FurnaceGUIRender() { -} \ No newline at end of file +} diff --git a/src/gui/render/renderDX11.cpp b/src/gui/render/renderDX11.cpp new file mode 100644 index 00000000..b860890f --- /dev/null +++ b/src/gui/render/renderDX11.cpp @@ -0,0 +1,571 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define INCLUDE_D3D11 +#include "renderDX11.h" +#include +#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 +}; + +struct FurnaceDXTexture { + ID3D11Texture2D* tex; + ID3D11ShaderResourceView* view; + int width, height; + unsigned char* lockedData; + bool dynamic; + FurnaceDXTexture(): + tex(NULL), + view(NULL), + width(0), + height(0), + lockedData(NULL), + dynamic(false) {} +}; + +bool FurnaceGUIRenderDX11::destroyRenderTarget() { + if (renderTarget!=NULL) { + renderTarget->Release(); + renderTarget=NULL; + return true; + } + return false; +} + +bool FurnaceGUIRenderDX11::createRenderTarget() { + ID3D11Texture2D* screen=NULL; + HRESULT result; + + destroyRenderTarget(); + + if (swapchain==NULL || device==NULL) { + logW("createRenderTarget: swapchain or device are NULL!"); + return false; + } + + DXGI_SWAP_CHAIN_DESC chainDesc; + memset(&chainDesc,0,sizeof(chainDesc)); + if (swapchain->GetDesc(&chainDesc)!=S_OK) { + logW("createRenderTarget: could not get swapchain desc!"); + } else { + outW=chainDesc.BufferDesc.Width; + outH=chainDesc.BufferDesc.Height; + logI("DX11: buffer desc sizes: %d, %d",chainDesc.BufferDesc.Width,chainDesc.BufferDesc.Height); + } + + result=swapchain->GetBuffer(0,IID_PPV_ARGS(&screen)); + if (result!=S_OK) { + logW("createRenderTarget: could not get buffer! %.8x",result); + return false; + } + if (screen==NULL) { + logW("createRenderTarget: screen is null!"); + return false; + } + + result=device->CreateRenderTargetView(screen,NULL,&renderTarget); + if (result!=S_OK) { + logW("createRenderTarget: could not create render target view! %.8x",result); + screen->Release(); + return false; + } + if (renderTarget==NULL) { + logW("createRenderTarget: what the hell the render target is null?"); + screen->Release(); + return false; + } + + screen->Release(); + return true; +} + +ImTextureID FurnaceGUIRenderDX11::getTextureID(void* which) { + FurnaceDXTexture* t=(FurnaceDXTexture*)which; + return (ImTextureID)t->view; +} + +bool FurnaceGUIRenderDX11::lockTexture(void* which, void** data, int* pitch) { + FurnaceDXTexture* t=(FurnaceDXTexture*)which; + if (t->lockedData!=NULL) return false; + + D3D11_MAPPED_SUBRESOURCE mappedRes; + memset(&mappedRes,0,sizeof(mappedRes)); + + HRESULT result=context->Map(t->tex,D3D11CalcSubresource(0,0,1),D3D11_MAP_WRITE_DISCARD,0,&mappedRes); + if (result!=S_OK) { + logW("could not map texture! %.8x",result); + return false; + } + t->lockedData=(unsigned char*)mappedRes.pData; + *data=mappedRes.pData; + *pitch=mappedRes.RowPitch; + + logV("texture locked... pitch: %d",mappedRes.RowPitch); + return true; +} + +bool FurnaceGUIRenderDX11::unlockTexture(void* which) { + FurnaceDXTexture* t=(FurnaceDXTexture*)which; + if (t->lockedData==NULL) return false; + context->Unmap(t->tex,D3D11CalcSubresource(0,0,1)); + t->lockedData=NULL; + return true; +} + +bool FurnaceGUIRenderDX11::updateTexture(void* which, void* data, int pitch) { + FurnaceDXTexture* t=(FurnaceDXTexture*)which; + if (t->dynamic) { + unsigned char* d=NULL; + int p=0; + if (!lockTexture(t,&d,&p)) return false; + if (p==pitch) { + memcpy(d,data,p*t->height); + } else { + unsigned char* ucData=(unsigned char*)data; + int srcPos=0; + int destPos=0; + for (int i=0; iheight; i++) { + memcpy(&d[destPos],&ucData[srcPos],pitch); + srcPos+=pitch; + destPos+=p; + } + } + unlockTexture(t); + } else { + context->UpdateSubresource(t->tex,D3D11CalcSubresource(0,0,1),NULL,data,pitch,pitch*t->height); + } + return true; +} + +void* FurnaceGUIRenderDX11::createTexture(bool dynamic, int width, int height) { + D3D11_TEXTURE2D_DESC texDesc; + D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc; + ID3D11Texture2D* tex=NULL; + ID3D11ShaderResourceView* view=NULL; + HRESULT result; + + memset(&texDesc,0,sizeof(texDesc)); + memset(&viewDesc,0,sizeof(viewDesc)); + + texDesc.Width=width; + texDesc.Height=height; + texDesc.MipLevels=1; + texDesc.ArraySize=1; + texDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM; // ??? + texDesc.SampleDesc.Count=1; + texDesc.SampleDesc.Quality=0; + texDesc.Usage=dynamic?D3D11_USAGE_DYNAMIC:D3D11_USAGE_DEFAULT; + texDesc.BindFlags=D3D11_BIND_SHADER_RESOURCE; + texDesc.CPUAccessFlags=dynamic?D3D11_CPU_ACCESS_WRITE:0; + texDesc.MiscFlags=0; + + result=device->CreateTexture2D(&texDesc,NULL,&tex); + if (result!=S_OK) { + logW("could not create texture! %.8x",result); + return NULL; + } + + viewDesc.Format=texDesc.Format=texDesc.Format; + viewDesc.ViewDimension=D3D11_SRV_DIMENSION_TEXTURE2D; + viewDesc.Texture2D.MostDetailedMip=0; + viewDesc.Texture2D.MipLevels=texDesc.MipLevels; + + result=device->CreateShaderResourceView(tex,&viewDesc,&view); + if (result!=S_OK) { + logW("could not create texture view! %.8x",result); + tex->Release(); + return NULL; + } + + FurnaceDXTexture* ret=new FurnaceDXTexture; + ret->width=width; + ret->height=height; + ret->tex=tex; + ret->view=view; + ret->dynamic=dynamic; + textures.push_back(ret); + return ret; +} + +bool FurnaceGUIRenderDX11::destroyTexture(void* which) { + FurnaceDXTexture* t=(FurnaceDXTexture*)which; + t->view->Release(); + t->tex->Release(); + delete t; + + for (size_t i=0; iResizeBuffers(0,(unsigned int)ev.window.data1,(unsigned int)ev.window.data2,DXGI_FORMAT_UNKNOWN,DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH); + if (result!=S_OK) { + logW("error while resizing swapchain buffers! %.8x",result); + } + createRenderTarget(); +} + +void FurnaceGUIRenderDX11::clear(ImVec4 color) { + float floatColor[4]={ + color.x*color.w, + color.y*color.w, + color.z*color.w, + color.w, + }; + + context->OMSetRenderTargets(1,&renderTarget,NULL); + context->ClearRenderTargetView(renderTarget,floatColor); +} + +bool FurnaceGUIRenderDX11::newFrame() { + ImGui_ImplDX11_NewFrame(); + return true; +} + +void FurnaceGUIRenderDX11::createFontsTexture() { + ImGui_ImplDX11_CreateDeviceObjects(); +} + +void FurnaceGUIRenderDX11::destroyFontsTexture() { + ImGui_ImplDX11_InvalidateDeviceObjects(); +} + +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) { + 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() { + swapchain->Present(1,0); +} + +bool FurnaceGUIRenderDX11::getOutputSize(int& w, int& h) { + w=outW; + h=outH; + return true; +} + +int FurnaceGUIRenderDX11::getWindowFlags() { + return 0; +} + +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; + + SDL_VERSION(&sysWindow.version); + if (SDL_GetWindowWMInfo(win,&sysWindow)==SDL_FALSE) { + logE("could not get window WM info! %s",SDL_GetError()); + return false; + } + HWND window=(HWND)sysWindow.info.win.window; + + DXGI_SWAP_CHAIN_DESC chainDesc; + memset(&chainDesc,0,sizeof(chainDesc)); + chainDesc.BufferDesc.Width=0; + chainDesc.BufferDesc.Height=0; + chainDesc.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM; + chainDesc.BufferDesc.RefreshRate.Numerator=60; + chainDesc.BufferDesc.RefreshRate.Denominator=1; + chainDesc.SampleDesc.Count=1; + chainDesc.SampleDesc.Quality=0; + chainDesc.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT; + chainDesc.BufferCount=2; + chainDesc.OutputWindow=window; + chainDesc.Windowed=TRUE; // TODO: what if we're in full screen mode? + chainDesc.SwapEffect=DXGI_SWAP_EFFECT_DISCARD; + chainDesc.Flags=DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + HRESULT result=D3D11CreateDeviceAndSwapChain(NULL,D3D_DRIVER_TYPE_HARDWARE,NULL,0,possibleFeatureLevels,2,D3D11_SDK_VERSION,&chainDesc,&swapchain,&device,&featureLevel,&context); + if (result!=S_OK) { + logE("could not create device and/or swap chain! %.8x",result); + 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; +} + +void FurnaceGUIRenderDX11::initGUI(SDL_Window* win) { + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + + ImGui_ImplSDL2_InitForD3D(win); + ImGui_ImplDX11_Init(device,context); +} + +bool FurnaceGUIRenderDX11::quit() { + destroyRenderTarget(); + + for (FurnaceDXTexture* i: textures) { + i->view->Release(); + i->tex->Release(); + delete i; + } + textures.clear(); + + if (swapchain!=NULL) { + swapchain->Release(); + swapchain=NULL; + } + if (context!=NULL) { + context->Release(); + context=NULL; + } + if (device!=NULL) { + device->Release(); + device=NULL; + } + return true; +} + +void FurnaceGUIRenderDX11::quitGUI() { + ImGui_ImplDX11_Shutdown(); +} diff --git a/src/gui/render/renderDX11.h b/src/gui/render/renderDX11.h new file mode 100644 index 00000000..18d8673d --- /dev/null +++ b/src/gui/render/renderDX11.h @@ -0,0 +1,103 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2023 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "../gui.h" +#ifdef INCLUDE_D3D11 +#include +#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 + +struct FurnaceDXTexture; + +class FurnaceGUIRenderDX11: public FurnaceGUIRender { + ID3D11Device* device; + ID3D11DeviceContext* context; + ID3D11RenderTargetView* renderTarget; + IDXGISwapChain* swapchain; + ID3D11RasterizerState* rsState; + ID3D11BlendState* omBlendState; + + ID3D11Buffer* quadVertex; + int outW, outH; + + // 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(); + + std::vector textures; + + public: + ImTextureID getTextureID(void* which); + bool lockTexture(void* which, void** data, int* pitch); + bool unlockTexture(void* which); + bool updateTexture(void* which, void* data, int pitch); + void* createTexture(bool dynamic, int width, int height); + bool destroyTexture(void* which); + void setTextureBlendMode(void* which, FurnaceGUIBlendMode mode); + void setBlendMode(FurnaceGUIBlendMode mode); + void resized(const SDL_Event& ev); + void clear(ImVec4 color); + bool newFrame(); + void createFontsTexture(); + void destroyFontsTexture(); + void renderGUI(); + void wipe(float alpha); + void present(); + bool getOutputSize(int& w, int& h); + int getWindowFlags(); + void preInit(); + bool init(SDL_Window* win); + void initGUI(SDL_Window* win); + void quitGUI(); + bool quit(); + FurnaceGUIRenderDX11(): + device(NULL), + context(NULL), + renderTarget(NULL), + swapchain(NULL), + rsState(NULL), + omBlendState(NULL), + quadVertex(NULL), + outW(0), + outH(0), + sh_wipe_vertex(NULL), + sh_wipe_fragment(NULL), + sh_wipe_inputLayout(NULL), + sh_wipe_uniform(NULL) { + } +}; diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 8f5a9843..5d2e1d9f 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -1231,7 +1231,17 @@ void FurnaceGUI::drawSampleEdit() { } } - memcpy(dataT,data,sampleTexW*sampleTexH*sizeof(unsigned int)); + if ((pitch>>2)==sampleTexW) { + memcpy(dataT,data,sampleTexW*sampleTexH*sizeof(unsigned int)); + } else { + int srcY=0; + int destY=0; + for (int i=0; i>2; + } + } rend->unlockTexture(sampleTex); delete[] data; } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 9648de12..5e57640d 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1303,6 +1303,11 @@ void FurnaceGUI::drawSettings() { settings.renderBackend="SDL"; } #endif +#ifdef HAVE_RENDER_DX11 + if (ImGui::Selectable("DirectX 11",curRenderBackend=="DirectX 11")) { + settings.renderBackend="DirectX 11"; + } +#endif #ifdef HAVE_RENDER_GL if (ImGui::Selectable("OpenGL",curRenderBackend=="OpenGL")) { settings.renderBackend="OpenGL";