DX11 renderer WIP

This commit is contained in:
flyinghead 2021-11-26 18:08:41 +01:00
parent c4eaa973f0
commit f91847dad0
36 changed files with 3559 additions and 89 deletions

View File

@ -39,9 +39,10 @@ jobs:
- name: Package app
run: |
mkdir build\artifact
cd build\AppPackages\flycast\flycast_1.0.0.0_x64_Test
cd build\AppPackages\flycast\flycast_*_x64_Test
mkdir tmp
makeappx.exe unpack /p .\flycast_1.0.0.0_x64.msix /d tmp
ren *.msix flycast.msix
makeappx.exe unpack /p .\flycast.msix /d tmp
copy ..\..\..\Release\*.dll tmp
makeappx pack /d tmp /p ..\..\..\artifact\flycast.appx
signtool sign /f ..\..\..\..\shell\uwp\sign_cert.pfx /p '${{ secrets.SIGN_CERT_PWD }}' /v /fd SHA256 ..\..\..\artifact\flycast.appx

View File

@ -54,6 +54,17 @@ if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(WINDOWS_STORE)
string(REPLACE "v" "" MS_VERSION ${GIT_VERSION})
string(REPLACE "-" "." MS_VERSION ${MS_VERSION})
string(REGEX REPLACE "\.g[0-9a-f]+" "" MS_VERSION ${MS_VERSION})
string(REGEX MATCH "[0-9]+\.[0-9]+\.[0-9]+" VERSION_3PARTS ${MS_VERSION})
if (VERSION_3PARTS STREQUAL "")
string(APPEND MS_VERSION ".0.0")
else()
string(APPEND MS_VERSION ".0")
endif()
endif()
endif()
string(TIMESTAMP BUILD_TIMESTAMP UTC)
@ -1026,30 +1037,25 @@ if(USE_VULKAN)
endif()
if(WIN32 AND NOT LIBRETRO AND NOT WINDOWS_STORE)
add_subdirectory(core/rend/dx9)
target_link_libraries(${PROJECT_NAME} PRIVATE dx9renderer)
endif()
if(WIN32 AND NOT LIBRETRO)
target_sources(${PROJECT_NAME} PRIVATE
core/rend/dx9/comptr.h
core/rend/dx9/d3d_overlay.h
core/rend/dx9/d3d_overlay.cpp
core/rend/dx9/d3d_renderer.h
core/rend/dx9/d3d_renderer.cpp
core/rend/dx9/d3d_shaders.h
core/rend/dx9/d3d_shaders.cpp
core/rend/dx9/d3d_texture.h
core/rend/dx9/d3d_texture.cpp
core/rend/dx9/dx9_driver.h
core/rend/dx9/dxcontext.h
core/rend/dx9/dxcontext.cpp
core/rend/dx9/imgui_impl_dx9.h
core/rend/dx9/imgui_impl_dx9.cpp)
if(NOT MINGW)
target_include_directories(${PROJECT_NAME} PRIVATE "$ENV{DXSDK_DIR}/Include")
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
target_link_directories(${PROJECT_NAME} PRIVATE "$ENV{DXSDK_DIR}/Lib/x64")
else()
target_link_directories(${PROJECT_NAME} PRIVATE "$ENV{DXSDK_DIR}/Lib/x86")
endif()
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE d3d9 d3dx9)
core/rend/dx11/dx11_driver.h
core/rend/dx11/dx11_renderer.cpp
core/rend/dx11/dx11_renderer.h
core/rend/dx11/dx11_shaders.cpp
core/rend/dx11/dx11_shaders.h
core/rend/dx11/dx11_texture.cpp
core/rend/dx11/dx11_texture.h
core/rend/dx11/dx11context.cpp
core/rend/dx11/dx11context.h
core/rend/dx11/imgui_impl_dx11.cpp
core/rend/dx11/imgui_impl_dx11.h
core/rend/dx11/dx11_driver.h)
target_link_libraries(${PROJECT_NAME} PRIVATE d3d11 d3dcompiler)
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)" AND NOT APPLE)
@ -1353,7 +1359,10 @@ if(NOT LIBRETRO)
core/windows/xinput_gamepad.h)
endif()
if(WINDOWS_STORE)
set(ResourceFiles shell/uwp/Package.appxmanifest
file(READ shell/uwp/Package.appxmanifest MANIFEST)
string(REPLACE "9.9.9.9" ${MS_VERSION} MANIFEST ${MANIFEST})
file(WRITE ${CMAKE_BINARY_DIR}/Package.appxmanifest ${MANIFEST})
set(ResourceFiles ${CMAKE_BINARY_DIR}/Package.appxmanifest
shell/uwp/flycast150.png
shell/uwp/flycast50.png
shell/uwp/flycast44.png

View File

@ -396,14 +396,12 @@ public:
RendererOption()
#ifdef USE_DX9
: Option<RenderType>("pvr.rend", RenderType::DirectX9) {}
#elif defined(TARGET_UWP)
: Option<RenderType>("pvr.rend", RenderType::DirectX11) {}
#else
: Option<RenderType>("pvr.rend", RenderType::OpenGL) {}
#endif
bool isDirectX() const {
return value == RenderType::DirectX9;
}
RenderType& operator=(const RenderType& v) { set(v); return value; }
};
extern RendererOption RendererType;

View File

@ -11,7 +11,11 @@
#include <unistd.h>
#include <sys/time.h>
// Note: alloca is not part of POSIX
#if defined(_MSC_VER) || defined(__MINGW32__)
#include <malloc.h>
#else
#include <alloca.h>
#endif
/*
#define MEMORY_MEASURE

View File

@ -268,6 +268,7 @@ Renderer* rend_norend();
Renderer* rend_Vulkan();
Renderer* rend_OITVulkan();
Renderer* rend_DirectX9();
Renderer* rend_DirectX11();
static void rend_create_renderer()
{
@ -297,6 +298,11 @@ static void rend_create_renderer()
case RenderType::DirectX9:
renderer = rend_DirectX9();
break;
#endif
#if defined(_WIN32) && !defined(LIBRETRO)
case RenderType::DirectX11:
renderer = rend_DirectX11();
break;
#endif
}
#endif

View File

@ -1,5 +1,6 @@
#pragma once
#include "types.h"
#include <cmath>
#define pvr_RegSize (0x8000)
#define pvr_RegMask (pvr_RegSize-1)
@ -371,21 +372,35 @@ union TA_GLOB_TILE_CLIP_type
u32 full;
};
union TA_YUV_TEX_CTRL_type
{
struct
{
u32 yuv_u_size : 6;
u32 reserved1 : 2;
u32 yuv_v_size : 6;
u32 reserved2 : 2;
u32 yuv_tex : 1;
u32 reserved3 : 7;
u32 yuv_form : 1;
u32 reserved4 : 7;
};
u32 full;
};
union TA_YUV_TEX_CTRL_type
{
struct
{
u32 yuv_u_size : 6;
u32 reserved1 : 2;
u32 yuv_v_size : 6;
u32 reserved2 : 2;
u32 yuv_tex : 1;
u32 reserved3 : 7;
u32 yuv_form : 1;
u32 reserved4 : 7;
};
u32 full;
};
union FOG_DENSITY_type
{
struct
{
s8 exponent;
u8 mantissa;
};
u32 full;
float get() {
return mantissa / 128.f * std::pow(2.0f, (float)exponent);
}
};
// TA REGS
#define TA_OL_BASE_addr 0x00000124 // RW Object list write start address
@ -461,7 +476,7 @@ union TA_YUV_TEX_CTRL_type
#define FOG_COL_RAM PvrReg(FOG_COL_RAM_addr,u32) // RW Color for Look Up table Fog
#define FOG_COL_VERT PvrReg(FOG_COL_VERT_addr,u32) // RW Color for vertex Fog
#define FOG_DENSITY PvrReg(FOG_DENSITY_addr,u32) // RW Fog scale value
#define FOG_DENSITY PvrReg(FOG_DENSITY_addr, FOG_DENSITY_type) // RW Fog scale value
#define FOG_CLAMP_MAX PvrReg(FOG_CLAMP_MAX_addr,u32) // RW Color clamping maximum value
#define FOG_CLAMP_MIN PvrReg(FOG_CLAMP_MIN_addr,u32) // RW Color clamping minimum value
#define SPG_TRIGGER_POS PvrReg(SPG_TRIGGER_POS_addr,u32) // RW External trigger signal HV counter value

View File

@ -1776,7 +1776,7 @@ void FillBGP(TA_context* ctx)
float scale_x= (SCALER_CTL.hscale) ? 2.f:1.f; //if AA hack the hacked pos value hacks
for (int i=0;i<3;i++)
{
if (config::RendererType.isDirectX())
if (isDirectX(config::RendererType))
decode_pvr_vertex<2, 1, 0, 3>(strip_base,vertex_ptr,&cv[i]);
else
decode_pvr_vertex<0, 1, 2, 3>(strip_base,vertex_ptr,&cv[i]);

View File

@ -186,7 +186,7 @@ void CustomTexture::DumpTexture(u32 hash, int w, int h, TextureType textype, voi
for (int y = 0; y < h; y++)
{
if (!config::RendererType.isDirectX())
if (!isDirectX(config::RendererType))
{
switch (textype)
{

View File

@ -91,7 +91,7 @@ void palette_update()
pal_needs_update = false;
palette_updated = true;
if (!config::RendererType.isDirectX())
if (!isDirectX(config::RendererType))
{
switch(PAL_RAM_CTRL&3)
{

View File

@ -0,0 +1,38 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast 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.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "rend/imgui_driver.h"
#include "imgui_impl_dx11.h"
#include "dx11context.h"
class DX11Driver final : public ImGuiDriver
{
public:
void newFrame() override {
ImGui_ImplDX11_NewFrame();
}
void renderDrawData(ImDrawData *drawData) override {
theDX11Context.EndImGuiFrame();
}
void present() override {
theDX11Context.Present();
}
};

138
core/rend/dx11/dx11_quad.h Normal file
View File

@ -0,0 +1,138 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast 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.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "dx11context.h"
#include "dx11_shaders.h"
class Quad
{
public:
void init(const ComPtr<ID3D11Device>& device, ComPtr<ID3D11DeviceContext> deviceContext, DX11Shaders *shaders)
{
this->device = device;
this->deviceContext = deviceContext;
this->shaders = shaders;
vertexShader = shaders->getQuadVertexShader(false);
rotateVertexShader = shaders->getQuadVertexShader(true);
pixelShader = shaders->getQuadPixelShader();
// Input layout
D3D11_INPUT_ELEMENT_DESC layout[]
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 2 * sizeof(float), D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
ComPtr<ID3DBlob> blob = shaders->getQuadVertexShaderBlob();
if (FAILED(device->CreateInputLayout(layout, ARRAY_SIZE(layout), blob->GetBufferPointer(), blob->GetBufferSize(), &inputLayout.get())))
WARN_LOG(RENDERER, "Input layout creation failed");
// Rasterizer state
{
D3D11_RASTERIZER_DESC desc{};
desc.FillMode = D3D11_FILL_SOLID;
desc.CullMode = D3D11_CULL_NONE;
desc.ScissorEnable = true;
desc.DepthClipEnable = true;
device->CreateRasterizerState(&desc, &rasterizerState.get());
}
// Blend state
{
D3D11_BLEND_DESC desc{};
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
device->CreateBlendState(&desc, &blendState.get());
}
// Depth-stencil state
{
D3D11_DEPTH_STENCIL_DESC desc{};
desc.DepthEnable = false;
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace;
device->CreateDepthStencilState(&desc, &depthStencilState.get());
}
// Vertex buffer
{
D3D11_BUFFER_DESC desc{};
desc.ByteWidth = sizeof(float) * 4 * 4;
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
device->CreateBuffer(&desc, nullptr, &vertexBuffer.get());
}
}
void draw(ComPtr<ID3D11ShaderResourceView>& texView, ComPtr<ID3D11SamplerState> sampler, float x = -1.f, float y = -1.f, float w = 2.f, float h = 2.f, bool rotate = false)
{
// Vertex buffer
Vertex vertices[4] {
{ x, y, 0.f, 1.f },
{ x, y + h, 0.f, 0.f },
{ x + w, y, 1.f, 1.f },
{ x + w, y + h, 1.f, 0.f },
};
D3D11_MAPPED_SUBRESOURCE mappedSubRes{};
deviceContext->Map(vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedSubRes);
memcpy(mappedSubRes.pData, vertices, sizeof(vertices));
deviceContext->Unmap(vertexBuffer, 0);
unsigned int stride = sizeof(Vertex);
unsigned int offset = 0;
deviceContext->IASetInputLayout(inputLayout);
deviceContext->IASetVertexBuffers(0, 1, &vertexBuffer.get(), &stride, &offset);
// Render states
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
deviceContext->OMSetBlendState(blendState, blend_factor, 0xffffffff);
deviceContext->OMSetDepthStencilState(depthStencilState, 0);
deviceContext->RSSetState(rasterizerState);
deviceContext->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
deviceContext->VSSetShader(rotate ? rotateVertexShader : vertexShader, nullptr, 0);
deviceContext->PSSetShader(pixelShader, nullptr, 0);
// TODO Scissor?
//const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
//deviceContext->RSSetScissorRects(1, &r);
// Bind texture and draw
deviceContext->PSSetShaderResources(0, 1, &texView.get());
deviceContext->PSSetSamplers(0, 1, &sampler.get());
deviceContext->Draw(4, 0);
}
private:
struct Vertex {
float x, y, u, v;
};
DX11Shaders *shaders = nullptr;
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> deviceContext;
ComPtr<ID3D11InputLayout> inputLayout;
ComPtr<ID3D11Buffer> vertexBuffer;
ComPtr<ID3D11RasterizerState> rasterizerState;
ComPtr<ID3D11BlendState> blendState;
ComPtr<ID3D11DepthStencilState> depthStencilState;
ComPtr<ID3D11VertexShader> vertexShader;
ComPtr<ID3D11VertexShader> rotateVertexShader;
ComPtr<ID3D11PixelShader> pixelShader;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast 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.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"
#include "hw/pvr/Renderer_if.h"
#include <d3d11.h>
#include "dx11context.h"
#include "rend/transform_matrix.h"
#include "dx11_quad.h"
#include "dx11_texture.h"
#include "dx11_shaders.h"
#include "rend/sorter.h"
#include "dx11_renderstate.h"
struct DX11Renderer : public Renderer
{
bool Init() override;
void Resize(int w, int h) override;
void Term() override;
bool Process(TA_context* ctx) override;
bool Render() override;
bool Present() override
{
if (!frameRendered)
return false;
frameRendered = false;
return true;
}
bool RenderLastFrame() override;
void DrawOSD(bool clear_screen) override;
BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) override;
private:
void readDCFramebuffer();
void renderDCFramebuffer();
bool ensureBufferSize(ComPtr<ID3D11Buffer>& buffer, D3D11_BIND_FLAG bind, u32& currentSize, u32 minSize);
void setProvokingVertices();
void prepareRttRenderTarget(u32 texAddress);
void readRttRenderTarget(u32 texAddress);
void renderFramebuffer();
void updateFogTexture();
void updatePaletteTexture();
void setBaseScissor();
void drawStrips();
template <u32 Type, bool SortingEnabled>
void drawList(const List<PolyParam>& gply, int first, int count);
template <u32 Type, bool SortingEnabled>
void setRenderState(const PolyParam *gp);
void sortTriangles(int first, int count);
void drawSorted(bool multipass);
void drawModVols(int first, int count);
void setCullMode(int mode);
void createDepthTexAndView(ComPtr<ID3D11Texture2D>& depthTex, ComPtr<ID3D11DepthStencilView>& depthTexView, int width, int height);
void createTexAndRenderTarget(ComPtr<ID3D11Texture2D>& texture, ComPtr<ID3D11RenderTargetView>& renderTarget, int width, int height);
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> deviceContext;
ComPtr<ID3D11Buffer> vertexBuffer;
u32 vertexBufferSize = 0;
ComPtr<ID3D11Buffer> modvolBuffer;
u32 modvolBufferSize = 0;
ComPtr<ID3D11Buffer> indexBuffer;
u32 indexBufferSize = 0;
ComPtr<ID3D11Buffer> sortedTriIndexBuffer;
u32 sortedTriIndexBufferSize = 0;
ComPtr<ID3D11Texture2D> fbTex;
ComPtr<ID3D11RenderTargetView> fbRenderTarget;
ComPtr<ID3D11ShaderResourceView> fbTextureView;
ComPtr<ID3D11Texture2D> depthTex;
ComPtr<ID3D11DepthStencilView> depthTexView;
ComPtr<ID3D11Texture2D> dcfbTexture;
ComPtr<ID3D11ShaderResourceView> dcfbTextureView;
ComPtr<ID3D11Texture2D> paletteTexture;
ComPtr<ID3D11ShaderResourceView> paletteTextureView;
ComPtr<ID3D11Texture2D> fogTexture;
ComPtr<ID3D11ShaderResourceView> fogTextureView;
ComPtr<ID3D11Texture2D> rttTexture;
ComPtr<ID3D11RenderTargetView> rttRenderTarget;
ComPtr<ID3D11Texture2D> rttDepthTex;
ComPtr<ID3D11DepthStencilView> rttDepthTexView;
ComPtr<ID3D11RasterizerState> rasterCullNone, rasterCullFront, rasterCullBack;
u32 width = 0;
u32 height = 0;
TransformMatrix<COORD_DIRECTX> matrices;
DX11TextureCache texCache;
DX11Shaders shaders;
Samplers samplers;
DepthStencilStates depthStencilStates;
BlendStates blendStates;
std::vector<SortTrigDrawParam> pidx_sort;
std::unique_ptr<Quad> quad;
ComPtr<ID3D11InputLayout> mainInputLayout;
ComPtr<ID3D11InputLayout> modVolInputLayout;
ComPtr<ID3D11Buffer> vtxConstants;
ComPtr<ID3D11Buffer> pxlConstants;
ComPtr<ID3D11Buffer> pxlPolyConstants;
D3D11_RECT scissorRect{};
bool scissorEnable = false;
bool frameRendered = false;
bool frameRenderedOnce = false;
};

View File

@ -0,0 +1,180 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast 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.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "dx11context.h"
#include <unordered_map>
const D3D11_COMPARISON_FUNC Zfunction[]
{
D3D11_COMPARISON_NEVER, //0 Never
D3D11_COMPARISON_LESS, //1 Less
D3D11_COMPARISON_EQUAL, //2 Equal
D3D11_COMPARISON_LESS_EQUAL, //3 Less Or Equal
D3D11_COMPARISON_GREATER, //4 Greater
D3D11_COMPARISON_NOT_EQUAL, //5 Not Equal
D3D11_COMPARISON_GREATER_EQUAL, //6 Greater Or Equal
D3D11_COMPARISON_ALWAYS, //7 Always
};
const D3D11_BLEND DestBlend[]
{
D3D11_BLEND_ZERO,
D3D11_BLEND_ONE,
D3D11_BLEND_SRC_COLOR,
D3D11_BLEND_INV_SRC_COLOR,
D3D11_BLEND_SRC_ALPHA,
D3D11_BLEND_INV_SRC_ALPHA,
D3D11_BLEND_DEST_ALPHA,
D3D11_BLEND_INV_DEST_ALPHA
};
const D3D11_BLEND SrcBlend[]
{
D3D11_BLEND_ZERO,
D3D11_BLEND_ONE,
D3D11_BLEND_DEST_COLOR,
D3D11_BLEND_INV_DEST_COLOR,
D3D11_BLEND_SRC_ALPHA,
D3D11_BLEND_INV_SRC_ALPHA,
D3D11_BLEND_DEST_ALPHA,
D3D11_BLEND_INV_DEST_ALPHA
};
class DepthStencilStates
{
public:
enum ModifierVolumeMode { Xor, Or, Inclusion, Exclusion, Final, Count };
ComPtr<ID3D11DepthStencilState> getState(bool depth, bool depthWrite, int depthFunc, bool stencil)
{
int hash = (depthFunc << 3) | depth | (depthWrite << 1) | (stencil << 2);
auto& state = states[hash];
if (!state)
{
D3D11_DEPTH_STENCIL_DESC desc{};
desc.DepthEnable = depth;
desc.DepthWriteMask = depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
desc.DepthFunc = Zfunction[depthFunc];
desc.StencilEnable = stencil;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace;
desc.StencilWriteMask = 0xFF;
desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE;
theDX11Context.getDevice()->CreateDepthStencilState(&desc, &state.get());
}
return state;
}
ComPtr<ID3D11DepthStencilState> getMVState(ModifierVolumeMode mode)
{
auto& state = mvStates[mode];
if (!state)
{
D3D11_DEPTH_STENCIL_DESC desc{};
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
desc.DepthFunc = D3D11_COMPARISON_GREATER;
desc.StencilEnable = true;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
switch (mode)
{
case Xor:
desc.DepthEnable = true;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_INVERT;
desc.StencilWriteMask = 2;
break;
case Or:
desc.DepthEnable = true;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE;
desc.StencilWriteMask = 2;
break;
case Inclusion:
desc.FrontFace.StencilFunc = D3D11_COMPARISON_LESS_EQUAL;
desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE;
desc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_ZERO;
desc.StencilReadMask = 3;
desc.StencilWriteMask = 3;
break;
case Exclusion:
desc.FrontFace.StencilFunc = D3D11_COMPARISON_LESS_EQUAL;
desc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_ZERO;
desc.StencilReadMask = 3;
desc.StencilWriteMask = 3;
break;
case Final:
desc.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL;
desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_ZERO;
desc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_ZERO;
desc.StencilReadMask = 0x81;
desc.StencilWriteMask = 3;
break;
default:
break;
}
desc.BackFace = desc.FrontFace;
theDX11Context.getDevice()->CreateDepthStencilState(&desc, &state.get());
}
return state;
}
void term()
{
states.clear();
for (auto& state : mvStates)
state.reset();
}
private:
std::unordered_map<int, ComPtr<ID3D11DepthStencilState>> states;
std::array<ComPtr<ID3D11DepthStencilState>, ModifierVolumeMode::Count> mvStates;
};
class BlendStates
{
public:
ComPtr<ID3D11BlendState> getState(bool enable, int srcBlend = 0, int destBlend = 0, bool disableWrite = false)
{
int hash = enable | (srcBlend << 1) | (destBlend << 4) | (disableWrite << 7);
auto& state = states[hash];
if (!state)
{
D3D11_BLEND_DESC desc{};
desc.RenderTarget[0].RenderTargetWriteMask = disableWrite ? 0 : D3D11_COLOR_WRITE_ENABLE_ALL;
desc.RenderTarget[0].BlendEnable = enable;
desc.RenderTarget[0].SrcBlend = SrcBlend[srcBlend];
desc.RenderTarget[0].DestBlend = DestBlend[destBlend];
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
theDX11Context.getDevice()->CreateBlendState(&desc, &state.get());
}
return state;
}
void term() {
states.clear();
}
private:
std::unordered_map<int, ComPtr<ID3D11BlendState>> states;
};

View File

@ -0,0 +1,497 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast 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.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "dx11_shaders.h"
const char * const VertexShader = R"(
struct VertexIn
{
float4 pos : POSITION;
float4 col : COLOR0;
float4 spec : COLOR1;
float2 uv : TEXCOORD0;
};
struct VertexOut
{
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
float4 col : COLOR0;
float4 spec : COLOR1;
};
cbuffer constantBuffer : register(b0)
{
float4x4 transMatrix;
};
VertexOut main(in VertexIn vin)
{
VertexOut vo;
vo.pos = mul(transMatrix, float4(vin.pos.xyz, 1.f));
#if pp_Gouraud == 1
vo.col = vin.col * vo.pos.z;
vo.spec = vin.spec * vo.pos.z;
#else
// flat shading: no interpolation
vo.col = vin.col;
vo.spec = vin.spec;
#endif
vo.uv = float4(vin.uv * vo.pos.z, 0.f, vo.pos.z);
vo.pos.w = 1.f;
vo.pos.z = 0.f;
return vo;
}
)";
const char * const ModVolVertexShader = R"(
struct VertexIn
{
float4 pos : POSITION;
};
struct VertexOut
{
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
};
cbuffer constantBuffer : register(b0)
{
float4x4 transMatrix;
};
VertexOut main(in VertexIn vin)
{
VertexOut vo;
vo.pos = mul(transMatrix, float4(vin.pos.xyz, 1.f));
vo.uv = float4(0.f, 0.f, 0.f, vo.pos.z);
vo.pos.w = 1.f;
vo.pos.z = 0.f;
return vo;
}
)";
const char * const PixelShader = R"(
#define PI 3.1415926f
struct Pixel
{
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
float4 col : COLOR0;
float4 spec : COLOR1;
};
Texture2D texture0 : register(t0);
sampler sampler0 : register(s0);
Texture2D paletteTexture : register(t1);
sampler paletteSampler : register(s1);
Texture2D fogTexture : register(t2);
sampler fogSampler : register(s2);
cbuffer constantBuffer : register(b0)
{
float4 colorClampMin;
float4 colorClampMax;
float4 FOG_COL_VERT;
float4 FOG_COL_RAM;
float fogDensity;
float fogScale;
float alphaTestValue;
};
cbuffer polyConstantBuffer : register(b1)
{
float paletteIndex;
float trilinearAlpha;
};
float fog_mode2(float w)
{
float z = clamp(w * fogDensity, 1.0f, 255.9999f);
float exp = floor(log2(z));
float m = z * 16.0f / pow(2.0, exp) - 16.0f;
float idx = floor(m) + exp * 16.0f + 0.5f;
float4 fogCoef = fogTexture.Sample(fogSampler, float2(idx / 128.0f, 0.75f - (m - floor(m)) / 2.0f));
return fogCoef.a;
}
float4 clampColor(float4 color)
{
#if FogClamping == 1
return clamp(color, colorClampMin, colorClampMax);
#else
return color;
#endif
}
#if pp_Palette == 1
float4 palettePixel(float2 coords)
{
int colorIdx = int(floor(texture0.Sample(sampler0, coords).a * 255.0f + 0.5f) + paletteIndex.x);
float2 c = float2((fmod(float(colorIdx), 32.0f) * 2.0f + 1.0f) / 64.0f, (float(colorIdx / 32) * 2.0f + 1.0f) / 64.0f);
return paletteTexture.Sample(paletteSampler, c);
}
#endif
struct PSO
{
float4 col : SV_TARGET;
float z : SV_DEPTH;
};
PSO main(in Pixel inpix)
{
#if pp_Gouraud == 1
float4 color = inpix.col / inpix.uv.w;
#if pp_BumpMap == 1 || pp_Offset == 1
float4 specular = inpix.spec / inpix.uv.w;
#endif
#else
float4 color = inpix.col;
#if pp_BumpMap == 1 || pp_Offset == 1
float4 specular = inpix.spec;
#endif
#endif
#if pp_UseAlpha == 0
color.a = 1.0f;
#endif
#if pp_FogCtrl == 3
color = float4(FOG_COL_RAM.rgb, fog_mode2(inpix.uv.w));
#endif
#if pp_Texture == 1
{
float2 uv = inpix.uv.xy / inpix.uv.w;
#if pp_Palette == 0
float4 texcol = texture0.Sample(sampler0, uv);
#else
float4 texcol = palettePixel(uv);
#endif
#if pp_BumpMap == 1
float s = PI / 2.0f * (texcol.a * 15.0f * 16.0f + texcol.r * 15.0f) / 255.0f;
float r = 2.0f * PI * (texcol.g * 15.0f * 16.0f + texcol.b * 15.0f) / 255.0f;
texcol[3] = clamp(specular.a + specular.r * sin(s) + specular.g * cos(s) * cos(r - 2.0f * PI * specular.b), 0.0f, 1.0f);
texcol.rgb = float3(1.0f, 1.0f, 1.0f);
#else
#if pp_IgnoreTexA == 1
texcol.a = 1.0f;
#endif
#if cp_AlphaTest == 1
if (alphaTestValue > texcol.a)
discard;
texcol.a = 1.0f;
#endif
#endif
#if pp_ShadInstr == 0
color = texcol;
#endif
#if pp_ShadInstr == 1
color.rgb *= texcol.rgb;
color.a = texcol.a;
#endif
#if pp_ShadInstr == 2
color.rgb = lerp(color.rgb, texcol.rgb, texcol.a);
#endif
#if pp_ShadInstr == 3
color *= texcol;
#endif
#if pp_Offset == 1 && pp_BumpMap == 0
color.rgb += specular.rgb;
#endif
}
#endif
color = clampColor(color);
#if pp_FogCtrl == 0
color.rgb = lerp(color.rgb, FOG_COL_RAM.rgb, fog_mode2(inpix.uv.w));
#endif
#if pp_FogCtrl == 1 && pp_Offset == 1 && pp_BumpMap == 0
color.rgb = lerp(color.rgb, FOG_COL_VERT.rgb, specular.a);
#endif
#if pp_TriLinear == 1
color *= trilinearAlpha;
#endif
PSO pso;
float w = inpix.uv.w * 100000.0f;
pso.z = log2(1.0f + w) / 34.0f;
pso.col = color;
return pso;
}
struct MVPixel
{
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
};
PSO modifierVolume(in MVPixel inpix)
{
PSO pso;
float w = inpix.uv.w * 100000.0f;
pso.z = log2(1.0f + w) / 34.0f;
pso.col = float4(0, 0, 0, fogScale);
return pso;
}
)";
const char * const QuadVertexShader = R"(
struct VertexIn
{
float2 pos : POSITION;
float2 uv : TEXCOORD0;
};
struct VertexOut
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
VertexOut main(in VertexIn vin)
{
VertexOut vo;
#if ROTATE == 0
vo.pos = float4(vin.pos, 0.f, 1.f);
#else
vo.pos = float4(vin.pos.y, -vin.pos.x, 0.f, 1.f);
#endif
vo.uv = vin.uv;
return vo;
}
)";
const char * const QuadPixelShader = R"(
struct VertexIn
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler sampler0;
Texture2D texture0;
float4 main(in VertexIn vin) : SV_Target
{
return texture0.Sample(sampler0, vin.uv);
}
)";
const char * const MacroValues[] { "0", "1", "2", "3" };
static D3D_SHADER_MACRO VertexMacros[]
{
{ "pp_Gouraud", "1" },
{ nullptr, nullptr }
};
enum PixelMacroEnum {
MacroGouraud,
MacroTexture,
MacroUseAlpha,
MacroIgnoreTexA,
MacroShadInstr,
MacroOffset,
MacroFogCtrl,
MacroBumpMap,
MacroFogClamping,
MacroTriLinear,
MacroPalette,
MacroAlphaTest
};
static D3D_SHADER_MACRO PixelMacros[]
{
{ "pp_Gouraud", "1" },
{ "pp_Texture", "0" },
{ "pp_UseAlpha", "0" },
{ "pp_IgnoreTexA", "0" },
{ "pp_ShadInstr", "0" },
{ "pp_Offset", "0" },
{ "pp_FogCtrl", "0" },
{ "pp_BumpMap", "0" },
{ "FogClamping", "0" },
{ "pp_TriLinear", "0" },
{ "pp_Palette", "0" },
{ "cp_AlphaTest", "0" },
{ nullptr, nullptr }
};
const ComPtr<ID3D11PixelShader>& DX11Shaders::getShader(bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr,
bool pp_Offset, u32 pp_FogCtrl, bool pp_BumpMap, bool fog_clamping,
bool trilinear, bool palette, bool gouraud, bool alphaTest)
{
const u32 hash = (int)pp_Texture
| (pp_UseAlpha << 1)
| (pp_IgnoreTexA << 2)
| (pp_ShadInstr << 3)
| (pp_Offset << 5)
| (pp_FogCtrl << 6)
| (pp_BumpMap << 8)
| (fog_clamping << 9)
| (trilinear << 10)
| (palette << 11)
| (gouraud << 12)
| (alphaTest << 13);
auto& shader = shaders[hash];
if (shader == nullptr)
{
verify(pp_ShadInstr < ARRAY_SIZE(MacroValues));
verify(pp_FogCtrl < ARRAY_SIZE(MacroValues));
PixelMacros[MacroGouraud].Definition = MacroValues[gouraud];
PixelMacros[MacroTexture].Definition = MacroValues[pp_Texture];
PixelMacros[MacroUseAlpha].Definition = MacroValues[pp_UseAlpha];
PixelMacros[MacroIgnoreTexA].Definition = MacroValues[pp_IgnoreTexA];
PixelMacros[MacroShadInstr].Definition = MacroValues[pp_ShadInstr];
PixelMacros[MacroOffset].Definition = MacroValues[pp_Offset];
PixelMacros[MacroFogCtrl].Definition = MacroValues[pp_FogCtrl];
PixelMacros[MacroBumpMap].Definition = MacroValues[pp_BumpMap];
PixelMacros[MacroFogClamping].Definition = MacroValues[fog_clamping];
PixelMacros[MacroTriLinear].Definition = MacroValues[trilinear];
PixelMacros[MacroPalette].Definition = MacroValues[palette];
PixelMacros[MacroAlphaTest].Definition = MacroValues[alphaTest];
shader = compilePS(PixelShader, "main", PixelMacros);
verify(shader != nullptr);
}
return shader;
}
const ComPtr<ID3D11VertexShader>& DX11Shaders::getVertexShader(bool gouraud)
{
ComPtr<ID3D11VertexShader>& vertexShader = gouraud ? gouraudVertexShader : flatVertexShader;
if (!vertexShader)
{
VertexMacros[0].Definition = MacroValues[gouraud];
vertexShader = compileVS(VertexShader, "main", VertexMacros);
}
return vertexShader;
}
const ComPtr<ID3D11VertexShader>& DX11Shaders::getMVVertexShader()
{
if (!modVolVertexShader)
modVolVertexShader = compileVS(ModVolVertexShader, "main", nullptr);
return modVolVertexShader;
}
const ComPtr<ID3D11PixelShader>& DX11Shaders::getModVolShader()
{
if (!modVolShader)
modVolShader = compilePS(PixelShader, "modifierVolume", PixelMacros);
return modVolShader;
}
const ComPtr<ID3D11VertexShader>& DX11Shaders::getQuadVertexShader(bool rotate)
{
ComPtr<ID3D11VertexShader>& shader = rotate ? quadRotateVertexShader : quadVertexShader;
if (!shader)
{
D3D_SHADER_MACRO macros[]
{
{ "ROTATE", rotate ? "1" : "0" },
{ nullptr, nullptr }
};
shader = compileVS(QuadVertexShader, "main", macros);
}
return shader;
}
const ComPtr<ID3D11PixelShader>& DX11Shaders::getQuadPixelShader()
{
if (!quadPixelShader)
quadPixelShader = compilePS(QuadPixelShader, "main", nullptr);
return quadPixelShader;
}
ComPtr<ID3DBlob> DX11Shaders::compileShader(const char* source, const char* function, const char* profile, const D3D_SHADER_MACRO *pDefines)
{
ComPtr<ID3DBlob> shaderBlob;
ComPtr<ID3DBlob> errorBlob;
if (FAILED(D3DCompile(source, strlen(source), nullptr, pDefines, nullptr, function, profile, 0, 0, &shaderBlob.get(), &errorBlob.get())))
ERROR_LOG(RENDERER, "Shader compilation failed: %s", errorBlob->GetBufferPointer());
return shaderBlob;
}
ComPtr<ID3D11VertexShader> DX11Shaders::compileVS(const char* source, const char* function, const D3D_SHADER_MACRO *pDefines)
{
ComPtr<ID3DBlob> blob = compileShader(source, function, "vs_4_0", pDefines);
ComPtr<ID3D11VertexShader> shader;
if (blob)
{
if (FAILED(device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, &shader.get())))
ERROR_LOG(RENDERER, "Vertex shader creation failed");
}
return shader;
}
ComPtr<ID3D11PixelShader> DX11Shaders::compilePS(const char* source, const char* function, const D3D_SHADER_MACRO *pDefines)
{
ComPtr<ID3DBlob> blob = compileShader(source, function, "ps_4_0", pDefines);
ComPtr<ID3D11PixelShader> shader;
if (blob)
{
if (device->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, &shader.get()) != S_OK)
ERROR_LOG(RENDERER, "Pixel shader creation failed");
}
return shader;
}
ComPtr<ID3DBlob> DX11Shaders::getVertexShaderBlob()
{
VertexMacros[0].Definition = MacroValues[0];
return compileShader(VertexShader, "main", "vs_4_0", VertexMacros);
}
ComPtr<ID3DBlob> DX11Shaders::getMVVertexShaderBlob()
{
return compileShader(ModVolVertexShader, "main", "vs_4_0", nullptr);
}
ComPtr<ID3DBlob> DX11Shaders::getQuadVertexShaderBlob()
{
return compileShader(QuadVertexShader, "main", "vs_4_0", nullptr);
}

View File

@ -0,0 +1,71 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast 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.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <unordered_map>
#include "dx11context.h"
#include <d3dcompiler.h>
class DX11Shaders
{
public:
void init(const ComPtr<ID3D11Device>& device)
{
this->device = device;
}
const ComPtr<ID3D11PixelShader>& getShader(bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr,
bool pp_Offset, u32 pp_FogCtrl, bool pp_BumpMap, bool fog_clamping, bool trilinear, bool palette, bool gouraud,
bool alphaTest);
const ComPtr<ID3D11VertexShader>& getVertexShader(bool gouraud);
const ComPtr<ID3D11PixelShader>& getModVolShader();
const ComPtr<ID3D11VertexShader>& getMVVertexShader();
const ComPtr<ID3D11PixelShader>& getQuadPixelShader();
const ComPtr<ID3D11VertexShader>& getQuadVertexShader(bool rotate);
void term()
{
shaders.clear();
gouraudVertexShader.reset();
flatVertexShader.reset();
modVolShader.reset();
modVolVertexShader.reset();
quadVertexShader.reset();
quadRotateVertexShader.reset();
quadPixelShader.reset();
device.reset();
}
ComPtr<ID3DBlob> getVertexShaderBlob();
ComPtr<ID3DBlob> getMVVertexShaderBlob();
ComPtr<ID3DBlob> getQuadVertexShaderBlob();
private:
ComPtr<ID3DBlob> compileShader(const char *source, const char* function, const char* profile, const D3D_SHADER_MACRO *pDefines);
ComPtr<ID3D11VertexShader> compileVS(const char *source, const char* function, const D3D_SHADER_MACRO *pDefines);
ComPtr<ID3D11PixelShader> compilePS(const char *source, const char* function, const D3D_SHADER_MACRO *pDefines);
ComPtr<ID3D11Device> device;
std::unordered_map<u32, ComPtr<ID3D11PixelShader>> shaders;
ComPtr<ID3D11VertexShader> gouraudVertexShader;
ComPtr<ID3D11VertexShader> flatVertexShader;
ComPtr<ID3D11PixelShader> modVolShader;
ComPtr<ID3D11VertexShader> modVolVertexShader;
ComPtr<ID3D11PixelShader> quadPixelShader;
ComPtr<ID3D11VertexShader> quadVertexShader;
ComPtr<ID3D11VertexShader> quadRotateVertexShader;
};

View File

@ -0,0 +1,120 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast 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.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "dx11_texture.h"
void DX11Texture::UploadToGPU(int width, int height, u8* temp_tex_buffer, bool mipmapped, bool mipmapsIncluded)
{
D3D11_TEXTURE2D_DESC desc{};
desc.Width = width;
desc.Height = height;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
u32 bpp = 2;
switch (tex_type)
{
case TextureType::_5551:
desc.Format = DXGI_FORMAT_B5G5R5A1_UNORM;
break;
case TextureType::_4444:
desc.Format = DXGI_FORMAT_B4G4R4A4_UNORM;
break;
case TextureType::_565:
desc.Format = DXGI_FORMAT_B5G6R5_UNORM;
break;
case TextureType::_8888:
bpp = 4;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
break;
case TextureType::_8:
bpp = 1;
desc.Format = DXGI_FORMAT_A8_UNORM;
break;
default:
return;
}
int mipmapLevels = 1;
if (mipmapsIncluded)
{
mipmapLevels = 0;
int dim = width;
while (dim != 0)
{
mipmapLevels++;
dim >>= 1;
}
}
desc.MipLevels = mipmapLevels;
if (texture == nullptr)
{
if (mipmapped && !mipmapsIncluded)
{
desc.MipLevels = 0;
desc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS;
desc.BindFlags |= D3D11_BIND_RENDER_TARGET;
mipmapLevels = 1;
}
if (SUCCEEDED(theDX11Context.getDevice()->CreateTexture2D(&desc, nullptr, &texture.get())))
{
D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{};
viewDesc.Format = desc.Format;
viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
viewDesc.Texture2D.MipLevels = desc.MipLevels == 0 ? -1 : desc.MipLevels;
theDX11Context.getDevice()->CreateShaderResourceView(texture, &viewDesc, &textureView.get());
}
verify(texture != nullptr);
verify(textureView != nullptr);
}
for (int i = 0; i < mipmapLevels; i++)
{
u32 w = mipmapLevels == 1 ? width : 1 << i;
u32 h = mipmapLevels == 1 ? height : 1 << i;
theDX11Context.getDeviceContext()->UpdateSubresource(texture, mipmapLevels - i - 1, nullptr, temp_tex_buffer, w * bpp, w * bpp * h);
temp_tex_buffer += (1 << (2 * i)) * bpp;
}
if (mipmapped && !mipmapsIncluded)
theDX11Context.getDeviceContext()->GenerateMips(textureView);
}
bool DX11Texture::Delete()
{
if (!BaseTextureCacheData::Delete())
return false;
textureView.reset();
texture.reset();
return true;
}
void DX11Texture::loadCustomTexture()
{
u32 size = custom_width * custom_height;
u8 *p = custom_image_data;
while (size--)
{
// RGBA -> BGRA
std::swap(p[0], p[2]);
p += 4;
}
CheckCustomTexture();
}

View File

@ -0,0 +1,87 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast 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.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "rend/TexCache.h"
#include <d3d11.h>
#include "dx11context.h"
#include <unordered_map>
class DX11Texture final : public BaseTextureCacheData
{
public:
ComPtr<ID3D11Texture2D> texture;
ComPtr<ID3D11ShaderResourceView> textureView;
std::string GetId() override { return std::to_string((uintptr_t)texture.get()); }
void UploadToGPU(int width, int height, u8* temp_tex_buffer, bool mipmapped,
bool mipmapsIncluded = false) override;
bool Delete() override;
void loadCustomTexture();
};
class DX11TextureCache final : public BaseTextureCache<DX11Texture>
{
public:
DX11TextureCache() {
DX11Texture::SetDirectXColorOrder(true);
}
~DX11TextureCache() {
Clear();
}
void Cleanup()
{
texturesToDelete.clear();
CollectCleanup();
}
void DeleteLater(ComPtr<ID3D11Texture2D> tex) { texturesToDelete.push_back(tex); }
private:
std::vector<ComPtr<ID3D11Texture2D>> texturesToDelete;
};
class Samplers
{
public:
ComPtr<ID3D11SamplerState> getSampler(bool linear, bool clampU = true, bool clampV = true, bool flipU = false, bool flipV = false)
{
int hash = clampU | (clampV << 1) | (flipU << 2) | (flipV << 3) | (linear << 4);
auto& sampler = samplers[hash];
if (!sampler)
{
// Create texture sampler
D3D11_SAMPLER_DESC desc{};
desc.Filter = linear ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_MAG_MIP_POINT;
desc.AddressU = flipU ? D3D11_TEXTURE_ADDRESS_MIRROR : clampU ? D3D11_TEXTURE_ADDRESS_CLAMP : D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressV = flipV ? D3D11_TEXTURE_ADDRESS_MIRROR : clampV ? D3D11_TEXTURE_ADDRESS_CLAMP : D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
desc.MaxAnisotropy = 1;
desc.MaxLOD = D3D11_FLOAT32_MAX;
theDX11Context.getDevice()->CreateSamplerState(&desc, &sampler.get());
}
return sampler;
}
void term() {
samplers.clear();
}
private:
std::unordered_map<int, ComPtr<ID3D11SamplerState>> samplers;
};

View File

@ -0,0 +1,294 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast 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.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "dx11context.h"
#include "rend/gui.h"
#include "rend/osd.h"
#ifdef USE_SDL
#include "sdl/sdl.h"
#endif
#include "hw/pvr/Renderer_if.h"
#include "emulator.h"
#include "dx11_driver.h"
#ifdef TARGET_UWP
#include <windows.h>
#include <gamingdeviceinformation.h>
#endif
DX11Context theDX11Context;
bool DX11Context::init(bool keepCurrentWindow)
{
NOTICE_LOG(RENDERER, "DX11 Context initializing");
GraphicsContext::instance = this;
#ifdef USE_SDL
if (!keepCurrentWindow && !sdl_recreate_window(0))
return false;
#endif
#ifdef TARGET_UWP
GAMING_DEVICE_MODEL_INFORMATION info {};
GetGamingDeviceModelInformation(&info);
if (info.vendorId == GAMING_DEVICE_VENDOR_ID_MICROSOFT)
{
switch (info.deviceId)
{
case GAMING_DEVICE_DEVICE_ID_XBOX_ONE:
case GAMING_DEVICE_DEVICE_ID_XBOX_ONE_S:
NOTICE_LOG(RENDERER, "XBox One [S] detected. Setting resolution to 1920x1080.");
settings.display.width = 1920;
settings.display.height = 1080;
break;
case GAMING_DEVICE_DEVICE_ID_XBOX_ONE_X:
case GAMING_DEVICE_DEVICE_ID_XBOX_ONE_X_DEVKIT:
default:
NOTICE_LOG(RENDERER, "XBox One X detected. Setting resolution to 3840x2160.");
settings.display.width = 3840;
settings.display.height = 2160;
break;
}
}
#endif
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
};
D3D11CreateDevice(
nullptr, // Specify nullptr to use the default adapter.
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
D3D11_CREATE_DEVICE_BGRA_SUPPORT, // | D3D11_CREATE_DEVICE_DEBUG, // FIXME
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION, // UWP apps must set this to D3D11_SDK_VERSION.
&pDevice.get(),
nullptr,
&pDeviceContext.get());
ComPtr<IDXGIDevice2> dxgiDevice;
pDevice.as(dxgiDevice);
ComPtr<IDXGIAdapter> dxgiAdapter;
dxgiDevice->GetAdapter(&dxgiAdapter.get());
DXGI_ADAPTER_DESC desc;
dxgiAdapter->GetDesc(&desc);
nowide::stackstring wdesc;
wdesc.convert(desc.Description);
adapterDesc = wdesc.c_str();
adapterVersion = std::to_string(desc.Revision);
ComPtr<IDXGIFactory1> dxgiFactory;
dxgiAdapter->GetParent(__uuidof(IDXGIFactory1), (void **)&dxgiFactory.get());
ComPtr<IDXGIFactory2> dxgiFactory2;
dxgiFactory.as(dxgiFactory2);
HRESULT hr;
if (dxgiFactory2)
{
// DX 11.1
DXGI_SWAP_CHAIN_DESC1 desc{};
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = 2;
desc.SampleDesc.Count = 1;
desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
#ifdef TARGET_UWP
desc.Width = settings.display.width;
desc.Height = settings.display.height;
hr = dxgiFactory2->CreateSwapChainForCoreWindow(pDevice, (IUnknown *)window, &desc, nullptr, &swapchain1.get());
#else
hr = dxgiFactory2->CreateSwapChainForHwnd(pDevice, (HWND)window, &desc, nullptr, nullptr, &swapchain1.get());
#endif
if (SUCCEEDED(hr))
swapchain1.as(swapchain);
}
else
{
// DX 11.0
swapchain1.reset();
#ifdef TARGET_UWP
return false;
#endif
DXGI_SWAP_CHAIN_DESC desc{};
desc.BufferCount = 2;
desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.BufferDesc.RefreshRate.Numerator = 60;
desc.BufferDesc.RefreshRate.Denominator = 1;
desc.OutputWindow = (HWND)window;
desc.Windowed = TRUE;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.BufferCount = 2;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
hr = dxgiFactory->CreateSwapChain(pDevice, &desc, &swapchain.get());
}
if (FAILED(hr))
return false;
imguiDriver = std::unique_ptr<ImGuiDriver>(new DX11Driver());
resize();
gui_init();
// TODO overlay.init(pDevice);
return ImGui_ImplDX11_Init(pDevice, pDeviceContext);
}
void DX11Context::term()
{
NOTICE_LOG(RENDERER, "DX11 Context terminating");
GraphicsContext::instance = nullptr;
ID3D11RenderTargetView* views[1] {};
pDeviceContext->OMSetRenderTargets(ARRAY_SIZE(views), views, nullptr);
//TODO overlay.term();
imguiDriver.reset();
ImGui_ImplDX11_Shutdown();
gui_term();
renderTargetView.reset();
swapchain1.reset();
swapchain.reset();
pDeviceContext.reset();
pDevice.reset();
}
void DX11Context::Present()
{
if (!frameRendered)
return;
frameRendered = false;
bool swapOnVSync = !settings.input.fastForwardMode && config::VSync;
HRESULT hr = swapchain->Present(swapOnVSync ? 1 : 0, 0);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
WARN_LOG(RENDERER, "Present failed: device removed/reset");
handleDeviceLost();
}
else if (FAILED(hr))
WARN_LOG(RENDERER, "Present failed %x", hr);
}
void DX11Context::EndImGuiFrame()
{
verify((bool)pDevice);
if (!overlayOnly)
{
pDeviceContext->OMSetRenderTargets(1, &renderTargetView.get(), nullptr);
const FLOAT black[4] { 0.f, 0.f, 0.f, 1.f };
pDeviceContext->ClearRenderTargetView(renderTargetView, black);
if (renderer != nullptr)
renderer->RenderLastFrame();
}
// if (overlayOnly)
// {
// if (crosshairsNeeded() || config::FloatVMUs)
// overlay.draw(settings.display.width, settings.display.height, config::FloatVMUs, true);
// }
// else
// {
// overlay.draw(settings.display.width, settings.display.height, true, false);
// }
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
frameRendered = true;
}
void DX11Context::resize()
{
if (!pDevice)
return;
if (swapchain)
{
BOOL fullscreen;
swapchain->GetFullscreenState(&fullscreen, nullptr);
NOTICE_LOG(RENDERER, "DX11Context::resize: current display is %d x %d fullscreen %d", settings.display.width, settings.display.height, fullscreen);
ID3D11RenderTargetView* views[1] {};
pDeviceContext->OMSetRenderTargets(ARRAY_SIZE(views), views, nullptr);
renderTargetView.reset();
#ifdef TARGET_UWP
// FIXME how to get correct width/height?
HRESULT hr = swapchain->ResizeBuffers(2, settings.display.width, settings.display.height, DXGI_FORMAT_R8G8B8A8_UNORM, 0);
#else
DXGI_SWAP_CHAIN_DESC swapchainDesc;
swapchain->GetDesc(&swapchainDesc);
NOTICE_LOG(RENDERER, "current swapchain desc: %d x %d windowed %d", swapchainDesc.BufferDesc.Width, swapchainDesc.BufferDesc.Height, swapchainDesc.Windowed);
HRESULT hr = swapchain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
handleDeviceLost();
return;
}
#endif
if (FAILED(hr))
{
WARN_LOG(RENDERER, "ResizeBuffers failed");
return;
}
// Create a render target view
ComPtr<ID3D11Texture2D> backBuffer;
hr = swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void **)&backBuffer.get());
if (FAILED(hr))
{
WARN_LOG(RENDERER, "swapChain->GetBuffer() failed");
return;
}
hr = pDevice->CreateRenderTargetView(backBuffer, nullptr, &renderTargetView.get());
if (FAILED(hr))
{
WARN_LOG(RENDERER, "CreateRenderTargetView failed");
return;
}
pDeviceContext->OMSetRenderTargets(1, &renderTargetView.get(), nullptr);
if (swapchain1)
{
DXGI_SWAP_CHAIN_DESC1 desc;
swapchain1->GetDesc1(&desc);
#ifndef TARGET_UWP
settings.display.width = desc.Width;
settings.display.height = desc.Height;
#endif
NOTICE_LOG(RENDERER, "swapchain desc: %d x %d", desc.Width, desc.Height);
}
else
{
DXGI_SWAP_CHAIN_DESC desc;
swapchain->GetDesc(&desc);
settings.display.width = desc.BufferDesc.Width;
settings.display.height = desc.BufferDesc.Height;
}
}
// TODO minimized window
}
void DX11Context::handleDeviceLost()
{
rend_term_renderer();
term();
init(true);
rend_init_renderer();
rend_resize_renderer();
}

View File

@ -0,0 +1,70 @@
/*
Copyright 2021 flyinghead
This file is part of Flycast.
Flycast 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.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "build.h"
#if defined(_WIN32)
#include "types.h"
#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <dxgi1_2.h>
#include "imgui_impl_dx11.h"
#include "../dx9/comptr.h"
// TODO #include "d3d_overlay.h"
#include "wsi/context.h"
class DX11Context : public GraphicsContext
{
public:
bool init(bool keepCurrentWindow = false);
void term() override;
void EndImGuiFrame();
void Present();
const ComPtr<ID3D11Device>& getDevice() const { return pDevice; }
const ComPtr<ID3D11DeviceContext>& getDeviceContext() const { return pDeviceContext; }
ComPtr<ID3D11RenderTargetView>& getRenderTarget() { return renderTargetView; }
void resize() override;
void setOverlay(bool overlayOnly) { this->overlayOnly = overlayOnly; }
std::string getDriverName() override {
return adapterDesc;
}
std::string getDriverVersion() override {
return adapterVersion;
}
void setFrameRendered() {
frameRendered = true;
}
private:
void handleDeviceLost();
ComPtr<ID3D11Device> pDevice;
ComPtr<ID3D11DeviceContext> pDeviceContext;
ComPtr<IDXGISwapChain> swapchain;
ComPtr<IDXGISwapChain1> swapchain1;
ComPtr<ID3D11RenderTargetView> renderTargetView;
bool overlayOnly = false;
// TODO D3DOverlay overlay;
bool swapOnVSync = false;
bool frameRendered = false;
std::string adapterDesc;
std::string adapterVersion;
};
extern DX11Context theDX11Context;
#endif

View File

@ -0,0 +1,552 @@
// dear imgui: Renderer Backend for DirectX11
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer.
// 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled).
// 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore.
// 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility.
// 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions.
// 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example.
// 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
// 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.
#include "imgui/imgui.h"
#include "imgui_impl_dx11.h"
// DirectX
#include <stdio.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#ifdef _MSC_VER
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
#endif
// DirectX data
static ID3D11Device* g_pd3dDevice = NULL;
static ID3D11DeviceContext* g_pd3dDeviceContext = NULL;
static IDXGIFactory* g_pFactory = NULL;
static ID3D11Buffer* g_pVB = NULL;
static ID3D11Buffer* g_pIB = NULL;
static ID3D11VertexShader* g_pVertexShader = NULL;
static ID3D11InputLayout* g_pInputLayout = NULL;
static ID3D11Buffer* g_pVertexConstantBuffer = NULL;
static ID3D11PixelShader* g_pPixelShader = NULL;
ID3D11SamplerState* g_pFontSampler = NULL;
ID3D11ShaderResourceView*g_pFontTextureView = NULL;
static ID3D11RasterizerState* g_pRasterizerState = NULL;
static ID3D11BlendState* g_pBlendState = NULL;
static ID3D11DepthStencilState* g_pDepthStencilState = NULL;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
struct VERTEX_CONSTANT_BUFFER
{
float mvp[4][4];
};
static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
{
// Setup viewport
D3D11_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D11_VIEWPORT));
vp.Width = draw_data->DisplaySize.x;
vp.Height = draw_data->DisplaySize.y;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = vp.TopLeftY = 0;
ctx->RSSetViewports(1, &vp);
// Setup shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0;
ctx->IASetInputLayout(g_pInputLayout);
ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset);
ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->VSSetShader(g_pVertexShader, NULL, 0);
ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer);
ctx->PSSetShader(g_pPixelShader, NULL, 0);
ctx->PSSetSamplers(0, 1, &g_pFontSampler);
ctx->GSSetShader(NULL, NULL, 0);
ctx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
ctx->DSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
ctx->CSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
// Setup blend state
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff);
ctx->OMSetDepthStencilState(g_pDepthStencilState, 0);
ctx->RSSetState(g_pRasterizerState);
}
// Render function
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
ID3D11DeviceContext* ctx = g_pd3dDeviceContext;
// Create and grow vertex/index buffers if needed
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
{
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert);
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0)
return;
}
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
{
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx);
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0)
return;
}
// Upload vertex/index data into a single contiguous GPU buffer
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
return;
if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
return;
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
ctx->Unmap(g_pVB, 0);
ctx->Unmap(g_pIB, 0);
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
{
D3D11_MAPPED_SUBRESOURCE mapped_resource;
if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
return;
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData;
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
ctx->Unmap(g_pVertexConstantBuffer, 0);
}
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
struct BACKUP_DX11_STATE
{
UINT ScissorRectsCount, ViewportsCount;
D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
ID3D11RasterizerState* RS;
ID3D11BlendState* BlendState;
FLOAT BlendFactor[4];
UINT SampleMask;
UINT StencilRef;
ID3D11DepthStencilState* DepthStencilState;
ID3D11ShaderResourceView* PSShaderResource;
ID3D11SamplerState* PSSampler;
ID3D11PixelShader* PS;
ID3D11VertexShader* VS;
ID3D11GeometryShader* GS;
UINT PSInstancesCount, VSInstancesCount, GSInstancesCount;
ID3D11ClassInstance *PSInstances[256], *VSInstances[256], *GSInstances[256]; // 256 is max according to PSSetShader documentation
D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology;
ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
DXGI_FORMAT IndexBufferFormat;
ID3D11InputLayout* InputLayout;
};
BACKUP_DX11_STATE old = {};
old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
ctx->RSGetState(&old.RS);
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
ctx->PSGetSamplers(0, 1, &old.PSSampler);
old.PSInstancesCount = old.VSInstancesCount = old.GSInstancesCount = 256;
ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
ctx->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount);
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
ctx->IAGetInputLayout(&old.InputLayout);
// Setup desired DX state
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_idx_offset = 0;
int global_vtx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Apply scissor/clipping rectangle
const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
ctx->RSSetScissorRects(1, &r);
// Bind texture, Draw
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->TextureId;
ctx->PSSetShaderResources(0, 1, &texture_srv);
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Restore modified DX state
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release();
ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
ctx->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release();
for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release();
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
}
static void ImGui_ImplDX11_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
{
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
ID3D11Texture2D* pTexture = NULL;
D3D11_SUBRESOURCE_DATA subResource;
subResource.pSysMem = pixels;
subResource.SysMemPitch = desc.Width * 4;
subResource.SysMemSlicePitch = 0;
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
IM_ASSERT(pTexture != NULL);
// Create texture view
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView);
pTexture->Release();
}
// Store our identifier
io.Fonts->SetTexID((ImTextureID)g_pFontTextureView);
// Create texture sampler
{
D3D11_SAMPLER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
desc.MipLODBias = 0.f;
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
desc.MinLOD = 0.f;
desc.MaxLOD = 0.f;
g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler);
}
}
bool ImGui_ImplDX11_CreateDeviceObjects()
{
if (!g_pd3dDevice)
return false;
if (g_pFontSampler)
ImGui_ImplDX11_InvalidateDeviceObjects();
// By using D3DCompile() from <d3dcompiler.h> / 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.
// Create the vertex shader
{
static const char* vertexShader =
"cbuffer vertexBuffer : register(b0) \
{\
float4x4 ProjectionMatrix; \
};\
struct VS_INPUT\
{\
float2 pos : POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
PS_INPUT main(VS_INPUT input)\
{\
PS_INPUT output;\
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
output.col = input.col;\
output.uv = input.uv;\
return output;\
}";
ID3DBlob* vertexShaderBlob;
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL)))
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
if (g_pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK)
{
vertexShaderBlob->Release();
return false;
}
// Create the input layout
D3D11_INPUT_ELEMENT_DESC local_layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK)
{
vertexShaderBlob->Release();
return false;
}
vertexShaderBlob->Release();
// Create the constant buffer
{
D3D11_BUFFER_DESC desc;
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER);
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer);
}
}
// Create the pixel shader
{
static const char* pixelShader =
"struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
sampler sampler0;\
Texture2D texture0;\
\
float4 main(PS_INPUT input) : SV_Target\
{\
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
return out_col; \
}";
ID3DBlob* pixelShaderBlob;
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL)))
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
if (g_pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK)
{
pixelShaderBlob->Release();
return false;
}
pixelShaderBlob->Release();
}
// Create the blending setup
{
D3D11_BLEND_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.AlphaToCoverageEnable = false;
desc.RenderTarget[0].BlendEnable = true;
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState);
}
// Create the rasterizer state
{
D3D11_RASTERIZER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.FillMode = D3D11_FILL_SOLID;
desc.CullMode = D3D11_CULL_NONE;
desc.ScissorEnable = true;
desc.DepthClipEnable = true;
g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState);
}
// Create depth-stencil State
{
D3D11_DEPTH_STENCIL_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.DepthEnable = false;
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace;
g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState);
}
ImGui_ImplDX11_CreateFontsTexture();
return true;
}
void ImGui_ImplDX11_InvalidateDeviceObjects()
{
if (!g_pd3dDevice)
return;
if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; }
if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; }
if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; }
if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; }
if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; }
if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; }
if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; }
if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; }
}
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
{
// Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_dx11";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
// Get factory from device
IDXGIDevice* pDXGIDevice = NULL;
IDXGIAdapter* pDXGIAdapter = NULL;
IDXGIFactory* pFactory = NULL;
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
{
g_pd3dDevice = device;
g_pd3dDeviceContext = device_context;
g_pFactory = pFactory;
}
if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release();
g_pd3dDevice->AddRef();
g_pd3dDeviceContext->AddRef();
return true;
}
void ImGui_ImplDX11_Shutdown()
{
ImGui_ImplDX11_InvalidateDeviceObjects();
if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; }
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; }
}
void ImGui_ImplDX11_NewFrame()
{
if (!g_pFontSampler)
ImGui_ImplDX11_CreateDeviceObjects();
}

View File

@ -0,0 +1,26 @@
// dear imgui: Renderer Backend for DirectX11
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#pragma once
#include "imgui/imgui.h" // IMGUI_IMPL_API
struct ID3D11Device;
struct ID3D11DeviceContext;
IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown();
IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing Dear ImGui state.
IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects();

View File

@ -0,0 +1,44 @@
project(dx9renderer)
add_library(${PROJECT_NAME} STATIC dxcontext.cpp)
target_compile_definitions(${PROJECT_NAME} PRIVATE
$<$<BOOL:MSVC>:_CRT_NONSTDC_NO_WARNINGS>
$<$<BOOL:MSVC>:_CRT_SECURE_NO_WARNINGS>
$<$<BOOL:MSVC>:_USE_MATH_DEFINES>
$<$<BOOL:MSVC>:NOMINMAX>
$<$<BOOL:${TEST_AUTOMATION}>:TEST_AUTOMATION>
$<$<BOOL:${ENABLE_LOG}>:DEBUGFAST>
USE_SDL)
target_sources(${PROJECT_NAME} PRIVATE
comptr.h
d3d_overlay.h
d3d_overlay.cpp
d3d_renderer.h
d3d_renderer.cpp
d3d_shaders.h
d3d_shaders.cpp
d3d_texture.h
d3d_texture.cpp
dx9_driver.h
dxcontext.h
imgui_impl_dx9.h
imgui_impl_dx9.cpp)
target_include_directories(${PROJECT_NAME} PRIVATE
../..
../../deps
../../deps/nowide/include
../../deps/glm
../../deps/SDL/include)
if(NOT MINGW)
target_include_directories(${PROJECT_NAME} PRIVATE "$ENV{DXSDK_DIR}/Include")
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
target_link_directories(${PROJECT_NAME} PUBLIC "$ENV{DXSDK_DIR}/Lib/x64")
else()
target_link_directories(${PROJECT_NAME} PUBLIC "$ENV{DXSDK_DIR}/Lib/x86")
endif()
endif()
target_link_libraries(${PROJECT_NAME} PUBLIC d3d9 d3dx9)

View File

@ -66,6 +66,11 @@ public:
ptr->Release();
}
template<typename I>
HRESULT as(ComPtr<I>& p) const {
return ptr->QueryInterface(IID_PPV_ARGS(&p.get()));
}
private:
T *ptr = nullptr;
};

View File

@ -642,6 +642,7 @@ void D3DRenderer::drawModVols(int first, int count)
device->SetVertexDeclaration(modVolVtxDecl);
device->SetStreamSource(0, modvolBuffer, 0, 3 * sizeof(float));
devCache.SetRenderState(D3DRS_ZFUNC, D3DCMP_GREATER);
devCache.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
devCache.SetRenderState(D3DRS_STENCILENABLE, TRUE);
devCache.SetRenderState(D3DRS_ZWRITEENABLE, D3DZB_FALSE);
@ -1066,10 +1067,7 @@ bool D3DRenderer::Render()
device->SetPixelShaderConstantF(2, ps_FOG_COL_RAM, 1);
// Fog density and scale constants
u8* fog_density = (u8*)&FOG_DENSITY;
float fog_den_mant = fog_density[1] / 128.0f; //bit 7 -> x. bit, so [6:0] -> fraction -> /128
s32 fog_den_exp = (s8)fog_density[0];
float fog_den_float = fog_den_mant * powf(2.0f, (float)fog_den_exp) * config::ExtraDepthScale;
float fog_den_float = FOG_DENSITY.get() * config::ExtraDepthScale;
float fogDensityAndScale[4]= { fog_den_float, 1.f - FPU_SHAD_SCALE.scale_factor / 256.f, 0, 1 };
device->SetPixelShaderConstantF(3, fogDensityAndScale, 1);

View File

@ -20,7 +20,7 @@
#define SHADER_DEBUG 0 // D3DXSHADER_DEBUG|D3DXSHADER_SKIPOPTIMIZATION
const char *VertexShader = R"(
const char * const VertexShader = R"(
struct vertex_in
{
float4 pos : POSITION;
@ -61,7 +61,7 @@ vertex_out main(in vertex_in vin)
)";
const char *PixelShader = R"(
const char * const PixelShader = R"(
#define PI 3.1415926f
@ -69,7 +69,7 @@ struct pixel
{
float4 uv : TEXCOORD0;
float4 col : COLOR0;
#if pp_Texture == 1 && (pp_BumpMap == 1 || pp_Offset == 1)
#if pp_BumpMap == 1 || pp_Offset == 1
float4 offs : COLOR1;
#endif
@ -137,12 +137,12 @@ PSO main(in pixel inpix)
#if pp_Gouraud == 1
float4 color = inpix.col / inpix.uv.w;
#if pp_Texture == 1 && (pp_BumpMap == 1 || pp_Offset == 1)
#if pp_BumpMap == 1 || pp_Offset == 1
float4 offset = inpix.offs / inpix.uv.w;
#endif
#else
float4 color = inpix.col;
#if pp_Texture == 1 && (pp_BumpMap == 1 || pp_Offset == 1)
#if pp_BumpMap == 1 || pp_Offset == 1
float4 offset = inpix.offs;
#endif
#endif
@ -223,7 +223,7 @@ PSO modifierVolume(float4 uv : TEXCOORD0)
}
)";
const char *MacroValues[] { "0", "1", "2", "3" };
const char * const MacroValues[] { "0", "1", "2", "3" };
static D3DXMACRO VertexMacros[]
{

View File

@ -16,6 +16,7 @@
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "rend/TexCache.h"
#include <d3d9.h>
#include "dxcontext.h"

View File

@ -736,10 +736,7 @@ static bool RenderFrame(int width, int height)
gl4ShaderUniforms.ps_FOG_COL_RAM[2]=fog_colram_bgra [0]/255.0f;
//Fog density constant
u8* fog_density=(u8*)&FOG_DENSITY;
float fog_den_mant=fog_density[1]/128.0f; //bit 7 -> x. bit, so [6:0] -> fraction -> /128
s32 fog_den_exp=(s8)fog_density[0];
gl4ShaderUniforms.fog_den_float = fog_den_mant * powf(2.0f,fog_den_exp) * config::ExtraDepthScale;
gl4ShaderUniforms.fog_den_float = FOG_DENSITY.get() * config::ExtraDepthScale;
gl4ShaderUniforms.fog_clamp_min[0] = ((pvrrc.fog_clamp_min >> 16) & 0xFF) / 255.0f;
gl4ShaderUniforms.fog_clamp_min[1] = ((pvrrc.fog_clamp_min >> 8) & 0xFF) / 255.0f;

View File

@ -1191,10 +1191,7 @@ bool RenderFrame(int width, int height)
ShaderUniforms.ps_FOG_COL_RAM[2] = fog_colram_bgra[0] / 255.0f;
//Fog density constant
u8* fog_density = (u8*)&FOG_DENSITY;
float fog_den_mant = fog_density[1] / 128.0f; //bit 7 -> x. bit, so [6:0] -> fraction -> /128
s32 fog_den_exp = (s8)fog_density[0];
ShaderUniforms.fog_den_float = fog_den_mant * powf(2.0f, fog_den_exp) * config::ExtraDepthScale;
ShaderUniforms.fog_den_float = FOG_DENSITY.get() * config::ExtraDepthScale;
ShaderUniforms.fog_clamp_min[0] = ((pvrrc.fog_clamp_min >> 16) & 0xFF) / 255.0f;
ShaderUniforms.fog_clamp_min[1] = ((pvrrc.fog_clamp_min >> 8) & 0xFF) / 255.0f;

View File

@ -1551,6 +1551,10 @@ static void gui_display_settings()
renderApi = 2;
perPixel = false;
break;
case RenderType::DirectX11:
renderApi = 3;
perPixel = false;
break;
}
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, normal_padding);
@ -1643,12 +1647,17 @@ static void gui_display_settings()
OptionCheckbox("Rotate Screen 90°", config::Rotate90, "Rotate the screen 90° counterclockwise");
OptionCheckbox("Delay Frame Swapping", config::DelayFrameSwapping,
"Useful to avoid flashing screen or glitchy videos. Not recommended on slow platforms");
#if defined(USE_VULKAN) || defined(USE_DX9)
#if defined(USE_VULKAN) || defined(USE_DX9) || defined(_WIN32)
ImGui::Text("Graphics API:");
#if defined(USE_VULKAN) && defined(USE_DX9)
constexpr u32 columns = 3;
#else
constexpr u32 columns = 2;
u32 columns = 1;
#ifdef USE_VULKAN
columns++;
#endif
#ifdef _WIN32
columns++;
#ifdef USE_DX9
columns++;
#endif
#endif
ImGui::Columns(columns, "renderApi", false);
ImGui::RadioButton("Open GL", &renderApi, 0);
@ -1657,8 +1666,12 @@ static void gui_display_settings()
ImGui::RadioButton("Vulkan", &renderApi, 1);
ImGui::NextColumn();
#endif
#ifdef _WIN32
#ifdef USE_DX9
ImGui::RadioButton("DirectX", &renderApi, 2);
ImGui::RadioButton("DirectX 9", &renderApi, 2);
ImGui::NextColumn();
#endif
ImGui::RadioButton("DirectX 11", &renderApi, 3);
ImGui::NextColumn();
#endif
ImGui::Columns(1, NULL, false);
@ -1755,6 +1768,9 @@ static void gui_display_settings()
case 2:
config::RendererType = RenderType::DirectX9;
break;
case 3:
config::RendererType = RenderType::DirectX11;
break;
}
}
if (ImGui::BeginTabItem("Audio"))
@ -2020,6 +2036,8 @@ static void gui_display_settings()
#else
"macOS"
#endif
#elif defined(TARGET_UWP)
"Windows Universal Platform"
#elif defined(_WIN32)
"Windows"
#elif defined(__SWITCH__)
@ -2038,7 +2056,7 @@ static void gui_display_settings()
header("Open GL");
else if (isVulkan(config::RendererType))
header("Vulkan");
else if (config::RendererType == RenderType::DirectX9)
else if (isDirectX(config::RendererType))
header("DirectX");
ImGui::Text("Driver Name: %s", GraphicsContext::Instance()->getDriverName().c_str());
ImGui::Text("Version: %s", GraphicsContext::Instance()->getDriverVersion().c_str());
@ -2098,12 +2116,17 @@ static void gui_display_content()
ImGui::Unindent(10 * scaling);
static ImGuiTextFilter filter;
#if !defined(__ANDROID__) && !defined(TARGET_IPHONE)
#if !defined(__ANDROID__) && !defined(TARGET_IPHONE) && !defined(TARGET_UWP)
ImGui::SameLine(0, 32 * scaling);
filter.Draw("Filter");
#endif
if (gui_state != GuiState::SelectDisk)
{
#if 0 //defined(TARGET_UWP)
void gui_load_game();
if (ImGui::Button("Load..."))
gui_load_game();
#endif
ImGui::SameLine(ImGui::GetContentRegionMax().x - ImGui::CalcTextSize("Settings").x - ImGui::GetStyle().FramePadding.x * 2.0f);
if (ImGui::Button("Settings"))
gui_state = GuiState::Settings;

View File

@ -89,8 +89,8 @@ void mainui_loop()
if (config::RendererType != currentRenderer || forceReinit)
{
mainui_term();
int prevApi = isOpenGL(currentRenderer) ? 0 : isVulkan(currentRenderer) ? 1 : 2;
int newApi = isOpenGL(config::RendererType) ? 0 : isVulkan(config::RendererType) ? 1 : 2;
int prevApi = isOpenGL(currentRenderer) ? 0 : isVulkan(currentRenderer) ? 1 : currentRenderer == RenderType::DirectX9 ? 2 : 3;
int newApi = isOpenGL(config::RendererType) ? 0 : isVulkan(config::RendererType) ? 1 : config::RendererType == RenderType::DirectX9 ? 2 : 3;
if (newApi != prevApi || forceReinit)
switchRenderApi();
mainui_init();

View File

@ -74,10 +74,7 @@ protected:
fragUniforms.sp_FOG_COL_RAM[2] = fog_colram_bgra[0] / 255.0f;
//Fog density constant
u8* fog_density = (u8*)&FOG_DENSITY;
float fog_den_mant = fog_density[1] / 128.0f; //bit 7 -> x. bit, so [6:0] -> fraction -> /128
s32 fog_den_exp = (s8)fog_density[0];
fragUniforms.sp_FOG_DENSITY = fog_den_mant * powf(2.0f, fog_den_exp) * config::ExtraDepthScale;
fragUniforms.sp_FOG_DENSITY = FOG_DENSITY.get() * config::ExtraDepthScale;
fragUniforms.colorClampMin[0] = ((pvrrc.fog_clamp_min >> 16) & 0xFF) / 255.0f;
fragUniforms.colorClampMin[1] = ((pvrrc.fog_clamp_min >> 8) & 0xFF) / 255.0f;

View File

@ -524,12 +524,21 @@ bool sdl_recreate_window(u32 flags)
}
#endif
#ifdef USE_DX9
if (config::RendererType == RenderType::DirectX9)
GraphicsContext::Instance()->setWindow(getNativeHwnd());
else
void *windowCtx = window;
#ifdef _WIN32
if (config::RendererType == RenderType::DirectX11 || config::RendererType == RenderType::DirectX9)
#ifdef TARGET_UWP
{
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(window, &wmInfo);
windowCtx = wmInfo.info.winrt.window;
}
#else
windowCtx = getNativeHwnd();
#endif
GraphicsContext::Instance()->setWindow(window);
#endif
GraphicsContext::Instance()->setWindow(windowCtx);
int displayIndex = SDL_GetWindowDisplayIndex(window);
if (displayIndex < 0)
@ -538,8 +547,13 @@ bool sdl_recreate_window(u32 flags)
{
SDL_DisplayMode mode{};
if (SDL_GetDesktopDisplayMode(displayIndex, &mode) == 0) {
INFO_LOG(RENDERER, "Monitor refresh rate: %d Hz", mode.refresh_rate);
NOTICE_LOG(RENDERER, "Monitor refresh rate: %d Hz (%d x %d)", mode.refresh_rate, mode.w, mode.h);
settings.display.refreshRate = mode.refresh_rate;
if (flags & SDL_WINDOW_FULLSCREEN)
{
settings.display.width = mode.w;
settings.display.height = mode.h;
}
}
}

View File

@ -283,6 +283,7 @@ enum class RenderType {
Vulkan = 4,
Vulkan_OIT = 5,
DirectX9 = 1,
DirectX11 = 2,
};
static inline bool isOpenGL(RenderType renderType) {
@ -291,6 +292,9 @@ static inline bool isOpenGL(RenderType renderType) {
static inline bool isVulkan(RenderType renderType) {
return renderType == RenderType::Vulkan || renderType == RenderType::Vulkan_OIT;
}
static inline bool isDirectX(RenderType renderType) {
return renderType == RenderType::DirectX9 || renderType == RenderType::DirectX11;
}
enum class KeyboardLayout {
JP = 1,

View File

@ -25,6 +25,7 @@
#include "gl_context.h"
#include "rend/dx9/dxcontext.h"
#include "rend/dx11/dx11context.h"
#ifdef USE_VULKAN
#include "rend/vulkan/vulkan_context.h"
@ -46,6 +47,16 @@ void initRenderApi(void *window, void *display)
config::RendererType = RenderType::OpenGL;
}
#endif
#ifdef _WIN32
if (config::RendererType == RenderType::DirectX11)
{
theDX11Context.setWindow(window, display);
if (theDX11Context.init())
return;
WARN_LOG(RENDERER, "DirectX 11 init failed. Falling back to DirectX 9.");
config::RendererType = RenderType::DirectX9;
}
#endif
#ifdef USE_DX9
if (config::RendererType == RenderType::DirectX9)
{
@ -53,7 +64,7 @@ void initRenderApi(void *window, void *display)
if (theDXContext.init())
return;
// Fall back to Open GL
WARN_LOG(RENDERER, "DirectX init failed. Falling back to Open GL.");
WARN_LOG(RENDERER, "DirectX 9 init failed. Falling back to Open GL.");
config::RendererType = RenderType::OpenGL;
}
#endif

View File

@ -4,7 +4,7 @@
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
IgnorableNamespaces="uap mp">
<Identity Name="AF75D068-D5AC-3D3C-B52A-2791C2F3491A" Publisher="CN=Flyinghead" Version="1.0.0.0" />
<Identity Name="AF75D068-D5AC-3D3C-B52A-2791C2F3491A" Publisher="CN=Flyinghead" Version="9.9.9.9" />
<mp:PhoneIdentity PhoneProductId="AF75D068-D5AC-3D3C-B52A-2791C2F3491A" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>Flycast</DisplayName>