/
Mr.Stalin
/
FOnline-Engine
Обзор
Документация
Войти
/
Mr.Stalin
/
FOnline-Engine
Код
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
Source/Frontend/Rendering-Direct3D.cpp
1 733 строки
74 KB
cvet
Particles upgrade (#194)
27 июл 2026, 20:55
Не верифицирован
27 июл 2026, 20:55
0e6a422
Код
Авторство
О чём код?
// __________ ___ ______ _ // / ____/ __ \____ / (_)___ ___ / ____/___ ____ _(_)___ ___ // / /_ / / / / __ \/ / / __ \/ _ \ / __/ / __ \/ __ `/ / __ \/ _ ` // / __/ / /_/ / / / / / / / / / __/ / /___/ / / / /_/ / / / / / __/ // /_/ \____/_/ /_/_/_/_/ /_/\___/ /_____/_/ /_/\__, /_/_/ /_/\___/ // /____/ // FOnline Engine // https://fonline.ru // https://github.com/cvet/fonline // // MIT License // // Copyright (c) 2006 - 2026, Anton Tsvetinskiy aka cvet <cvet@tut.by> // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #include "Rendering.h" #if FO_HAVE_DIRECT_3D #include "Application.h" #include "SDL3/SDL_video.h" #include <d3d11_1.h> #include <d3dcompiler.h> #include "WinApiUndef.inc" FO_BEGIN_NAMESPACE class Direct3D_Texture final : public RenderTexture { public: Direct3D_Texture(isize32 size, bool linear_filtered, bool with_depth, ptr<Direct3D_Renderer::Context> ctx) : RenderTexture(size, linear_filtered, with_depth), _ctx {ctx} { } Direct3D_Texture(const Direct3D_Texture&) = delete; Direct3D_Texture(Direct3D_Texture&&) noexcept = delete; auto operator=(const Direct3D_Texture&) -> Direct3D_Texture& = delete; auto operator=(Direct3D_Texture&&) noexcept -> Direct3D_Texture& = delete; ~Direct3D_Texture() override; [[nodiscard]] auto GetTexturePixel(ipos32 pos) const -> ucolor override; [[nodiscard]] auto GetTextureRegion(ipos32 pos, isize32 size) const -> vector<ucolor> override; void UpdateTextureRegion(ipos32 pos, isize32 size, const_span<ucolor> data, bool use_dest_pitch) override; nptr<ID3D11Texture2D> TexHandle {}; nptr<ID3D11Texture2D> DepthStencil {}; nptr<ID3D11RenderTargetView> RenderTargetView {}; nptr<ID3D11DepthStencilView> DepthStencilView {}; nptr<ID3D11ShaderResourceView> ShaderTexView {}; private: ptr<Direct3D_Renderer::Context> _ctx; }; class Direct3D_DrawBuffer final : public RenderDrawBuffer { public: Direct3D_DrawBuffer(bool is_static, ptr<Direct3D_Renderer::Context> ctx) : RenderDrawBuffer(is_static), _ctx {ctx} { } Direct3D_DrawBuffer(const Direct3D_DrawBuffer&) = delete; Direct3D_DrawBuffer(Direct3D_DrawBuffer&&) noexcept = delete; auto operator=(const Direct3D_DrawBuffer&) -> Direct3D_DrawBuffer& = delete; auto operator=(Direct3D_DrawBuffer&&) noexcept -> Direct3D_DrawBuffer& = delete; ~Direct3D_DrawBuffer() override; void Upload(EffectUsage usage, optional<size_t> custom_vertices_size, optional<size_t> custom_indices_size) override; nptr<ID3D11Buffer> VertexBuf {}; nptr<ID3D11Buffer> IndexBuf {}; size_t VertexBufSize {}; size_t IndexBufSize {}; private: ptr<Direct3D_Renderer::Context> _ctx; }; class Direct3D_Effect final : public RenderEffect { friend class Direct3D_Renderer; public: Direct3D_Effect(EffectUsage usage, string_view name, const RenderEffectLoader& loader, ptr<Direct3D_Renderer::Context> ctx) : RenderEffect(usage, name, loader), _ctx {ctx} { } Direct3D_Effect(const Direct3D_Effect&) = delete; Direct3D_Effect(Direct3D_Effect&&) noexcept = delete; auto operator=(const Direct3D_Effect&) -> Direct3D_Effect& = delete; auto operator=(Direct3D_Effect&&) noexcept -> Direct3D_Effect& = delete; ~Direct3D_Effect() override; void DrawBuffer(ptr<RenderDrawBuffer> dbuf, size_t start_index, optional<size_t> indices_to_draw, nptr<const RenderTexture> custom_tex) override; nptr<ID3D11VertexShader> VertexShader[EFFECT_MAX_PASSES] {}; nptr<ID3D11InputLayout> InputLayout[EFFECT_MAX_PASSES] {}; nptr<ID3D11PixelShader> PixelShader[EFFECT_MAX_PASSES] {}; nptr<ID3D11RasterizerState> RasterizerState[EFFECT_CULL_MODES][EFFECT_MAX_PASSES] {}; nptr<ID3D11BlendState> BlendState[EFFECT_MAX_PASSES] {}; nptr<ID3D11DepthStencilState> DepthStencilState[EFFECT_MAX_PASSES][EFFECT_DEPTH_VARIANTS] {}; nptr<ID3D11Buffer> Cb_ProjBuf {}; nptr<ID3D11Buffer> Cb_MainTexBuf {}; nptr<ID3D11Buffer> Cb_EggBuf {}; nptr<ID3D11Buffer> Cb_SpriteBorderBuf {}; nptr<ID3D11Buffer> Cb_ParticleSamplingBuf {}; nptr<ID3D11Buffer> Cb_TimeBuf {}; nptr<ID3D11Buffer> Cb_RandomValueBuf {}; nptr<ID3D11Buffer> Cb_ScriptValueBuf {}; nptr<ID3D11Buffer> Cb_CameraBuf {}; #if FO_ENABLE_3D nptr<ID3D11Buffer> Cb_ModelBuf {}; nptr<ID3D11Buffer> Cb_ModelTexBuf {}; nptr<ID3D11Buffer> Cb_ModelAnimBuf {}; #endif private: ptr<Direct3D_Renderer::Context> _ctx; }; struct Direct3D_Renderer::Context { nptr<GlobalSettings> Settings {}; bool RenderDebug {}; bool VSync {}; nptr<SDL_Window> SdlWindow {}; D3D_FEATURE_LEVEL FeatureLevel {}; nptr<IDXGISwapChain> SwapChain {}; nptr<ID3D11Device> D3DDevice {}; nptr<ID3D11DeviceContext> D3DDeviceContext {}; nptr<ID3D11DeviceContext1> D3DDeviceContext1 {}; nptr<ID3D11RenderTargetView> MainRenderTarget {}; nptr<ID3D11RenderTargetView> CurRenderTarget {}; nptr<ID3D11DepthStencilView> CurDepthStencil {}; nptr<ID3D11SamplerState> PointSampler {}; nptr<ID3D11SamplerState> LinearSampler {}; nptr<ID3D11Texture2D> OnePixStagingTex {}; unique_nptr<RenderTexture> DummyTexture {}; mat44 ProjMatrix {}; float32_t OrthoNear {ORTHO_DEPTH_DEFAULT_NEAR}; float32_t OrthoFar {ORTHO_DEPTH_DEFAULT_FAR}; bool ScissorEnabled {}; D3D11_RECT ScissorRect {}; D3D11_RECT DisabledScissorRect {}; D3D11_VIEWPORT ViewPort {}; irect32 ViewPortRect {}; isize32 BackBufSize {}; isize32 TargetSize {}; }; static auto GetBlobString(ptr<ID3DBlob> blob) -> string { FO_STACK_TRACE_ENTRY(); nptr<const void> buffer = blob->GetBufferPointer(); FO_VERIFY_AND_THROW(buffer, "Shader blob buffer pointer is null"); auto chars = buffer.reinterpret_as<char>(); return string {chars.get()}; } template<typename T> static void ReleaseComObject(ptr<nptr<T>> object) noexcept { FO_NO_STACK_TRACE_ENTRY(); if (*object) { (*object)->Release(); *object = nullptr; } } template<typename T> static void ReleaseComObjectSlot(nptr<T>& object) noexcept { FO_NO_STACK_TRACE_ENTRY(); ptr<nptr<T>> object_ptr = &object; ReleaseComObject(object_ptr); } template<typename T> static void ReleaseOwnedComObject(T* raw_object) noexcept { FO_NO_STACK_TRACE_ENTRY(); if (raw_object != nullptr) { auto object = make_ptr(raw_object); object->Release(); } } template<typename T> static auto MakeComObjectHolder(ptr<T> object) noexcept -> unique_del_ptr<T> { FO_NO_STACK_TRACE_ENTRY(); return make_unique_del_ptr(object, ReleaseOwnedComObject<T>); } template<typename T> static auto MakeComObjectHolder(nptr<T> object) noexcept -> unique_del_ptr<T> { FO_NO_STACK_TRACE_ENTRY(); ptr<T> checked_object = object; return MakeComObjectHolder(checked_object); } Direct3D_Renderer::Direct3D_Renderer() = default; static auto ConvertBlend(BlendFuncType blend, bool is_alpha) -> D3D11_BLEND { FO_STACK_TRACE_ENTRY(); switch (blend) { case BlendFuncType::Zero: return D3D11_BLEND_ZERO; case BlendFuncType::One: return D3D11_BLEND_ONE; case BlendFuncType::SrcColor: return !is_alpha ? D3D11_BLEND_SRC_COLOR : D3D11_BLEND_SRC_ALPHA; case BlendFuncType::InvSrcColor: return !is_alpha ? D3D11_BLEND_INV_SRC_COLOR : D3D11_BLEND_INV_SRC_ALPHA; case BlendFuncType::DstColor: return !is_alpha ? D3D11_BLEND_DEST_COLOR : D3D11_BLEND_DEST_ALPHA; case BlendFuncType::InvDstColor: return !is_alpha ? D3D11_BLEND_INV_DEST_COLOR : D3D11_BLEND_INV_DEST_ALPHA; case BlendFuncType::SrcAlpha: return D3D11_BLEND_SRC_ALPHA; case BlendFuncType::InvSrcAlpha: return D3D11_BLEND_INV_SRC_ALPHA; case BlendFuncType::DstAlpha: return D3D11_BLEND_DEST_ALPHA; case BlendFuncType::InvDstAlpha: return D3D11_BLEND_INV_DEST_ALPHA; case BlendFuncType::ConstantColor: return D3D11_BLEND_BLEND_FACTOR; case BlendFuncType::InvConstantColor: return D3D11_BLEND_INV_BLEND_FACTOR; case BlendFuncType::SrcAlphaSaturate: return D3D11_BLEND_SRC_ALPHA_SAT; } FO_UNREACHABLE_PLACE(); } static auto ConvertBlendOp(BlendEquationType blend_op) -> D3D11_BLEND_OP { FO_STACK_TRACE_ENTRY(); switch (blend_op) { case BlendEquationType::FuncAdd: return D3D11_BLEND_OP_ADD; case BlendEquationType::FuncSubtract: return D3D11_BLEND_OP_SUBTRACT; case BlendEquationType::FuncReverseSubtract: return D3D11_BLEND_OP_REV_SUBTRACT; case BlendEquationType::Max: return D3D11_BLEND_OP_MAX; case BlendEquationType::Min: return D3D11_BLEND_OP_MIN; } FO_UNREACHABLE_PLACE(); } static auto ConvertDepthFunc(DepthFuncType depth_func) -> D3D11_COMPARISON_FUNC { FO_STACK_TRACE_ENTRY(); switch (depth_func) { case DepthFuncType::Always: return D3D11_COMPARISON_ALWAYS; case DepthFuncType::Never: return D3D11_COMPARISON_NEVER; case DepthFuncType::Less: return D3D11_COMPARISON_LESS; case DepthFuncType::LessEqual: return D3D11_COMPARISON_LESS_EQUAL; case DepthFuncType::Equal: return D3D11_COMPARISON_EQUAL; case DepthFuncType::GreaterEqual: return D3D11_COMPARISON_GREATER_EQUAL; case DepthFuncType::Greater: return D3D11_COMPARISON_GREATER; case DepthFuncType::NotEqual: return D3D11_COMPARISON_NOT_EQUAL; } FO_UNREACHABLE_PLACE(); } static auto ConvertCullMode(CullModeType cull_mode) -> D3D11_CULL_MODE { FO_STACK_TRACE_ENTRY(); switch (cull_mode) { case CullModeType::None: return D3D11_CULL_NONE; case CullModeType::Back: return D3D11_CULL_BACK; case CullModeType::Front: return D3D11_CULL_FRONT; } FO_UNREACHABLE_PLACE(); } void Direct3D_Renderer::Init(GlobalSettings& settings, nptr<WindowInternalHandle> window) { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(window, "Frontend window handle is null"); FO_VERIFY_AND_THROW(!_ctx, "Frontend context is already initialized"); _ctx = SafeAlloc::MakeUnique<Context>(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); WriteLog("Used DirectX rendering"); _ctx->Settings = &settings; _ctx->RenderDebug = settings.RenderDebug; _ctx->VSync = settings.VSync; _ctx->SdlWindow = window.reinterpret_as<SDL_Window>(); SDL_PropertiesID window_props = SDL_GetWindowProperties(_ctx->SdlWindow.get()); HWND hwnd = static_cast<HWND>(SDL_GetPointerProperty(window_props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr)); // Device { constexpr D3D_FEATURE_LEVEL feature_levels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1, }; map<D3D_FEATURE_LEVEL, string> feature_levels_str = { {D3D_FEATURE_LEVEL_11_1, "11.1"}, {D3D_FEATURE_LEVEL_11_0, "11.0"}, {D3D_FEATURE_LEVEL_10_1, "10.1"}, {D3D_FEATURE_LEVEL_10_0, "10.0"}, {D3D_FEATURE_LEVEL_9_3, "9.3"}, {D3D_FEATURE_LEVEL_9_2, "9.2"}, {D3D_FEATURE_LEVEL_9_1, "9.1"}, }; constexpr auto feature_levels_count = numeric_cast<UINT>(std::size(feature_levels)); UINT device_flags = D3D11_CREATE_DEVICE_SINGLETHREADED; if (_ctx->RenderDebug) { device_flags |= D3D11_CREATE_DEVICE_DEBUG; } auto d3d_hardware_create_device = ::D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, device_flags, feature_levels, feature_levels_count, D3D11_SDK_VERSION, _ctx->D3DDevice.get_pp(), &_ctx->FeatureLevel, _ctx->D3DDeviceContext.get_pp()); if (FAILED(d3d_hardware_create_device)) { auto d3d_warp_create_device = ::D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, device_flags, feature_levels, feature_levels_count, D3D11_SDK_VERSION, _ctx->D3DDevice.get_pp(), &_ctx->FeatureLevel, _ctx->D3DDeviceContext.get_pp()); if (FAILED(d3d_warp_create_device)) { throw AppInitException("D3D11CreateDevice failed (Hardware and Warp)", d3d_hardware_create_device, d3d_warp_create_device); } WriteLog("Warp Direct3D device created with feature level {}", feature_levels_str.at(_ctx->FeatureLevel)); } else { WriteLog("Direct3D device created with feature level {}", feature_levels_str.at(_ctx->FeatureLevel)); } if (SUCCEEDED(_ctx->D3DDeviceContext->QueryInterface(IID_PPV_ARGS(_ctx->D3DDeviceContext1.get_pp())))) { ReleaseComObjectSlot(_ctx->D3DDeviceContext); _ctx->D3DDeviceContext = _ctx->D3DDeviceContext1; } else { WriteLog("Direct3D ID3D11DeviceContext1 not found"); } } // Swap chain { nptr<IDXGIFactory> factory {}; auto d3d_create_factory = ::CreateDXGIFactory(IID_PPV_ARGS(factory.get_pp())); if (FAILED(d3d_create_factory)) { throw AppInitException("CreateDXGIFactory failed", d3d_create_factory); } FO_VERIFY_AND_THROW(factory, "DXGI factory is null"); auto factory_holder = MakeComObjectHolder(factory); DXGI_SWAP_CHAIN_DESC swap_chain_desc = {}; swap_chain_desc.BufferCount = 2; swap_chain_desc.BufferDesc.Width = 0; swap_chain_desc.BufferDesc.Height = 0; swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swap_chain_desc.BufferDesc.RefreshRate.Numerator = 0; swap_chain_desc.BufferDesc.RefreshRate.Denominator = 1; swap_chain_desc.Flags = 0; swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swap_chain_desc.OutputWindow = hwnd; swap_chain_desc.SampleDesc.Count = 1; swap_chain_desc.SampleDesc.Quality = 0; swap_chain_desc.Windowed = TRUE; if (_ctx->VSync) { swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; auto d3d_create_swap_chain = factory->CreateSwapChain(_ctx->D3DDevice.get(), &swap_chain_desc, _ctx->SwapChain.get_pp()); if (FAILED(d3d_create_swap_chain)) { swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; auto d3d_create_swap_chain_2 = factory->CreateSwapChain(_ctx->D3DDevice.get(), &swap_chain_desc, _ctx->SwapChain.get_pp()); if (FAILED(d3d_create_swap_chain_2)) { swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; auto d3d_create_swap_chain_3 = factory->CreateSwapChain(_ctx->D3DDevice.get(), &swap_chain_desc, _ctx->SwapChain.get_pp()); if (FAILED(d3d_create_swap_chain_3)) { swap_chain_desc.BufferCount = 1; auto d3d_create_swap_chain_4 = factory->CreateSwapChain(_ctx->D3DDevice.get(), &swap_chain_desc, _ctx->SwapChain.get_pp()); if (FAILED(d3d_create_swap_chain_4)) { throw AppInitException("CreateSwapChain failed", d3d_create_swap_chain, d3d_create_swap_chain_2, d3d_create_swap_chain_3, d3d_create_swap_chain_4); } else { WriteLog("Direct3D swap chain created with one buffer count"); } } else { WriteLog("Direct3D swap chain created with non-flip swap effect"); } } else { WriteLog("Direct3D swap chain created with flip sequential swap effect"); } } } else { swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; auto d3d_create_swap_chain = factory->CreateSwapChain(_ctx->D3DDevice.get(), &swap_chain_desc, _ctx->SwapChain.get_pp()); if (FAILED(d3d_create_swap_chain)) { swap_chain_desc.BufferCount = 1; auto d3d_create_swap_chain_2 = factory->CreateSwapChain(_ctx->D3DDevice.get(), &swap_chain_desc, _ctx->SwapChain.get_pp()); if (FAILED(d3d_create_swap_chain_2)) { throw AppInitException("CreateSwapChain failed", d3d_create_swap_chain, d3d_create_swap_chain_2); } else { WriteLog("Direct3D swap chain created with one buffer count"); } } } // Disable Alt+Enter nptr<IDXGIFactory> swap_chain_factory {}; if (SUCCEEDED(_ctx->SwapChain->GetParent(IID_PPV_ARGS(swap_chain_factory.get_pp())))) { FO_VERIFY_AND_THROW(swap_chain_factory, "Swap chain factory is null"); auto swap_chain_factory_holder = MakeComObjectHolder(swap_chain_factory); swap_chain_factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_PRINT_SCREEN); } } // Samplers { auto create_sampler = [&](D3D11_FILTER filter) { D3D11_SAMPLER_DESC sampler_desc = {}; sampler_desc.Filter = filter; sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; sampler_desc.MaxAnisotropy = 2; sampler_desc.MipLODBias = 0.0f; sampler_desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; sampler_desc.MinLOD = 0.0f; sampler_desc.MaxLOD = FLT_MAX; sampler_desc.BorderColor[0] = 0.0f; sampler_desc.BorderColor[1] = 0.0f; sampler_desc.BorderColor[2] = 0.0f; sampler_desc.BorderColor[3] = 0.0f; nptr<ID3D11SamplerState> sampler {}; auto d3d_create_sampler = _ctx->D3DDevice->CreateSamplerState(&sampler_desc, sampler.get_pp()); if (FAILED(d3d_create_sampler)) { throw EffectLoadException("Failed to create sampler", d3d_create_sampler); } return sampler; }; _ctx->PointSampler = create_sampler(D3D11_FILTER_MIN_MAG_MIP_POINT); _ctx->LinearSampler = create_sampler(D3D11_FILTER_MIN_MAG_MIP_LINEAR); } // Calculate atlas size int32_t atlas_w; int32_t atlas_h; if (_ctx->FeatureLevel >= D3D_FEATURE_LEVEL_11_0) { atlas_w = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; atlas_h = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; } else if (_ctx->FeatureLevel >= D3D_FEATURE_LEVEL_10_0) { atlas_w = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; atlas_h = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; } else if (_ctx->FeatureLevel >= D3D_FEATURE_LEVEL_9_3) { atlas_w = D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION; atlas_h = D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION; } else if (_ctx->FeatureLevel >= D3D_FEATURE_LEVEL_9_1) { atlas_w = D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION; atlas_h = D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION; } else { FO_UNREACHABLE_PLACE(); } FO_VERIFY_AND_THROW(atlas_w >= AppRender::MIN_ATLAS_SIZE, "Direct3D texture atlas width is below the required minimum", AppRender::MIN_ATLAS_SIZE); FO_VERIFY_AND_THROW(atlas_h >= AppRender::MIN_ATLAS_SIZE, "Direct3D texture atlas height is below the required minimum", AppRender::MIN_ATLAS_SIZE); AppRender::MAX_ATLAS_WIDTH = atlas_w; AppRender::MAX_ATLAS_HEIGHT = atlas_h; // Back buffer view nptr<ID3D11Texture2D> back_buf {}; auto d3d_get_back_buf = _ctx->SwapChain->GetBuffer(0, IID_PPV_ARGS(back_buf.get_pp())); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_get_back_buf), "Direct3D swap chain GetBuffer failed while creating the main render target", d3d_get_back_buf, settings.ScreenWidth, settings.ScreenHeight); FO_VERIFY_AND_THROW(back_buf, "Direct3D swap chain GetBuffer returned a null back buffer", settings.ScreenWidth, settings.ScreenHeight); auto back_buf_holder = MakeComObjectHolder(back_buf); auto d3d_create_back_buf_rt_view = _ctx->D3DDevice->CreateRenderTargetView(back_buf.get(), nullptr, _ctx->MainRenderTarget.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_back_buf_rt_view), "Direct3D CreateRenderTargetView failed for the swap-chain back buffer", d3d_create_back_buf_rt_view, settings.ScreenWidth, settings.ScreenHeight); _ctx->BackBufSize = {settings.ScreenWidth, settings.ScreenHeight}; // One pixel staging texture D3D11_TEXTURE2D_DESC one_pix_staging_desc; one_pix_staging_desc.Width = 1; one_pix_staging_desc.Height = 1; one_pix_staging_desc.MipLevels = 1; one_pix_staging_desc.ArraySize = 1; one_pix_staging_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; one_pix_staging_desc.SampleDesc.Count = 1; one_pix_staging_desc.SampleDesc.Quality = 0; one_pix_staging_desc.Usage = D3D11_USAGE_STAGING; one_pix_staging_desc.BindFlags = 0; one_pix_staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; one_pix_staging_desc.MiscFlags = 0; auto d3d_create_one_pix_staging_tex = _ctx->D3DDevice->CreateTexture2D(&one_pix_staging_desc, nullptr, _ctx->OnePixStagingTex.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_one_pix_staging_tex), "Direct3D CreateTexture2D failed for the one-pixel staging texture", d3d_create_one_pix_staging_tex, one_pix_staging_desc.Width, one_pix_staging_desc.Height); // Dummy texture constexpr ucolor dummy_pixel[1] = {ucolor {255, 0, 255, 255}}; _ctx->DummyTexture = CreateTexture({1, 1}, false, false); _ctx->DummyTexture->UpdateTextureRegion({}, {1, 1}, dummy_pixel); // Init render target SetRenderTarget(nullptr); DisableScissor(); } Direct3D_Renderer::~Direct3D_Renderer() { FO_STACK_TRACE_ENTRY(); if (!_ctx) { return; } _ctx->DummyTexture.reset(); _ctx->CurDepthStencil = nullptr; _ctx->CurRenderTarget = nullptr; if (_ctx->D3DDeviceContext) { _ctx->D3DDeviceContext->ClearState(); _ctx->D3DDeviceContext->Flush(); } ReleaseComObjectSlot(_ctx->MainRenderTarget); ReleaseComObjectSlot(_ctx->PointSampler); ReleaseComObjectSlot(_ctx->LinearSampler); ReleaseComObjectSlot(_ctx->OnePixStagingTex); ReleaseComObjectSlot(_ctx->SwapChain); if (_ctx->D3DDeviceContext1) { ReleaseComObjectSlot(_ctx->D3DDeviceContext1); _ctx->D3DDeviceContext = nullptr; } else { ReleaseComObjectSlot(_ctx->D3DDeviceContext); } ReleaseComObjectSlot(_ctx->D3DDevice); _ctx->Settings = nullptr; _ctx->SdlWindow = nullptr; _ctx->FeatureLevel = {}; _ctx->ProjMatrix = {}; _ctx->ScissorEnabled = false; _ctx->ScissorRect = {}; _ctx->DisabledScissorRect = {}; _ctx->ViewPort = {}; _ctx->ViewPortRect = {}; _ctx->BackBufSize = {}; _ctx->TargetSize = {}; _ctx.reset(); } void Direct3D_Renderer::Present() { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); auto d3d_swap_chain = _ctx->SwapChain->Present(_ctx->VSync ? 1 : 0, 0); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_swap_chain), "Direct3D Present failed", d3d_swap_chain, _ctx->VSync, _ctx->BackBufSize); if (_ctx->D3DDeviceContext1) { _ctx->D3DDeviceContext1->DiscardView(_ctx->CurRenderTarget.get()); } _ctx->D3DDeviceContext->OMSetRenderTargets(1, _ctx->CurRenderTarget.get_pp(), _ctx->CurDepthStencil.get()); } auto Direct3D_Renderer::CreateTexture(isize32 size, bool linear_filtered, bool with_depth) -> unique_ptr<RenderTexture> { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); auto d3d_tex = SafeAlloc::MakeUnique<Direct3D_Texture>(size, linear_filtered, with_depth, _ctx); D3D11_TEXTURE2D_DESC tex_desc = {}; tex_desc.Width = size.width; tex_desc.Height = size.height; tex_desc.MipLevels = 1; tex_desc.ArraySize = 1; tex_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; tex_desc.SampleDesc.Count = 1; tex_desc.SampleDesc.Quality = 0; tex_desc.Usage = D3D11_USAGE_DEFAULT; tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; tex_desc.CPUAccessFlags = 0; tex_desc.MiscFlags = 0; auto d3d_create_texure_2d = _ctx->D3DDevice->CreateTexture2D(&tex_desc, nullptr, d3d_tex->TexHandle.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_texure_2d), "Direct3D CreateTexture2D failed for a render texture", d3d_create_texure_2d, size, linear_filtered, with_depth); if (with_depth) { D3D11_TEXTURE2D_DESC depth_tex_desc = {}; depth_tex_desc.Width = size.width; depth_tex_desc.Height = size.height; depth_tex_desc.MipLevels = 1; depth_tex_desc.ArraySize = 1; depth_tex_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depth_tex_desc.SampleDesc.Count = 1; depth_tex_desc.SampleDesc.Quality = 0; depth_tex_desc.Usage = D3D11_USAGE_DEFAULT; depth_tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depth_tex_desc.CPUAccessFlags = 0; depth_tex_desc.MiscFlags = 0; auto d3d_create_texure_depth = _ctx->D3DDevice->CreateTexture2D(&depth_tex_desc, nullptr, d3d_tex->DepthStencil.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_texure_depth), "Direct3D CreateTexture2D failed for a depth-stencil texture", d3d_create_texure_depth, size); D3D11_DEPTH_STENCIL_VIEW_DESC depth_view_desc = {}; depth_view_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depth_view_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; depth_view_desc.Texture2D.MipSlice = 0; auto d3d_create_depth_view = _ctx->D3DDevice->CreateDepthStencilView(d3d_tex->DepthStencil.get(), &depth_view_desc, d3d_tex->DepthStencilView.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_depth_view), "Direct3D CreateDepthStencilView failed for a render texture", d3d_create_depth_view, size); } auto d3d_create_tex_rt_view = _ctx->D3DDevice->CreateRenderTargetView(d3d_tex->TexHandle.get(), nullptr, d3d_tex->RenderTargetView.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_tex_rt_view), "Direct3D CreateRenderTargetView failed for a render texture", d3d_create_tex_rt_view, size); D3D11_SHADER_RESOURCE_VIEW_DESC tex_view_desc = {}; tex_view_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; tex_view_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; tex_view_desc.Texture2D.MipLevels = 1; tex_view_desc.Texture2D.MostDetailedMip = 0; auto d3d_create_shader_res_view = _ctx->D3DDevice->CreateShaderResourceView(d3d_tex->TexHandle.get(), &tex_view_desc, d3d_tex->ShaderTexView.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_shader_res_view), "Direct3D CreateShaderResourceView failed for a render texture", d3d_create_shader_res_view, size, linear_filtered, with_depth); return std::move(d3d_tex); } auto Direct3D_Renderer::CreateDrawBuffer(bool is_static) -> unique_ptr<RenderDrawBuffer> { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); auto d3d_dbuf = SafeAlloc::MakeUnique<Direct3D_DrawBuffer>(is_static, _ctx); return std::move(d3d_dbuf); } auto Direct3D_Renderer::CreateEffect(EffectUsage usage, string_view name, const RenderEffectLoader& loader) -> unique_ptr<RenderEffect> { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); auto d3d_effect = SafeAlloc::MakeUnique<Direct3D_Effect>(usage, name, loader, _ctx); for (size_t pass = 0; pass < d3d_effect->_passCount; pass++) { // Create the vertex shader { string vertex_shader_fname = strex("{}.fofx-{}-vert-hlsl", strex(name).erase_file_extension(), pass + 1); string vertex_shader_content = loader(vertex_shader_fname); FO_VERIFY_AND_THROW(!vertex_shader_content.empty(), "Direct3D effect vertex shader content is empty after loading", name, pass + 1, vertex_shader_fname); nptr<ID3DBlob> vertex_shader_blob {}; nptr<ID3DBlob> error_blob {}; auto vertex_shader_content_cstr = make_ptr(vertex_shader_content.c_str()); ptr<const char> vertex_shader_entry_point = "main"; auto vertex_shader_profile = make_ptr(_ctx->Settings->Direct3DVertexShaderProfile.c_str()); auto d3d_compile = ::D3DCompile(vertex_shader_content_cstr.get(), vertex_shader_content.length(), nullptr, nullptr, nullptr, vertex_shader_entry_point.get(), vertex_shader_profile.get(), 0, 0, vertex_shader_blob.get_pp(), error_blob.get_pp()); if (FAILED(d3d_compile)) { FO_VERIFY_AND_THROW(error_blob, "Shader compilation failed without an error blob"); auto error_blob_holder = MakeComObjectHolder(error_blob); string error = GetBlobString(error_blob); throw EffectLoadException("Failed to compile Vertex Shader", vertex_shader_fname, vertex_shader_content, error); } if (error_blob) { auto error_blob_holder = MakeComObjectHolder(error_blob); } FO_VERIFY_AND_THROW(vertex_shader_blob, "Vertex shader blob is null"); auto vertex_shader_blob_holder = MakeComObjectHolder(vertex_shader_blob); auto d3d_create_vertex_shader = _ctx->D3DDevice->CreateVertexShader(vertex_shader_blob->GetBufferPointer(), vertex_shader_blob->GetBufferSize(), nullptr, d3d_effect->VertexShader[pass].get_pp()); if (FAILED(d3d_create_vertex_shader)) { throw EffectLoadException("Failed to create Vertex Shader from binary", d3d_create_vertex_shader, vertex_shader_fname, vertex_shader_content); } // Create the input layout #if FO_ENABLE_3D if (usage == EffectUsage::Model) { static_assert(MODEL_BONES_PER_VERTEX == 4); constexpr D3D11_INPUT_ELEMENT_DESC local_layout[] = { {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, numeric_cast<UINT>(offsetof(Vertex3D, Position)), D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 1, DXGI_FORMAT_R32G32B32_FLOAT, 0, numeric_cast<UINT>(offsetof(Vertex3D, Normal)), D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 2, DXGI_FORMAT_R32G32_FLOAT, 0, numeric_cast<UINT>(offsetof(Vertex3D, TexCoord)), D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 3, DXGI_FORMAT_R32G32_FLOAT, 0, numeric_cast<UINT>(offsetof(Vertex3D, TexCoordBase)), D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 4, DXGI_FORMAT_R32G32B32_FLOAT, 0, numeric_cast<UINT>(offsetof(Vertex3D, Tangent)), D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 5, DXGI_FORMAT_R32G32B32_FLOAT, 0, numeric_cast<UINT>(offsetof(Vertex3D, Bitangent)), D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 6, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, numeric_cast<UINT>(offsetof(Vertex3D, BlendWeights)), D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 7, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, numeric_cast<UINT>(offsetof(Vertex3D, BlendIndices)), D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 8, DXGI_FORMAT_R8G8B8A8_UNORM, 0, numeric_cast<UINT>(offsetof(Vertex3D, Color)), D3D11_INPUT_PER_VERTEX_DATA, 0}, }; auto d3d_create_input_layout = _ctx->D3DDevice->CreateInputLayout(local_layout, 9, vertex_shader_blob->GetBufferPointer(), vertex_shader_blob->GetBufferSize(), d3d_effect->InputLayout[pass].get_pp()); if (FAILED(d3d_create_input_layout)) { throw EffectLoadException("Failed to create Vertex Shader 3D layout", d3d_create_input_layout, vertex_shader_fname, vertex_shader_content); } } else #endif { constexpr D3D11_INPUT_ELEMENT_DESC local_layout[] = { {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, numeric_cast<UINT>(offsetof(Vertex2D, PosX)), D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, numeric_cast<UINT>(offsetof(Vertex2D, Color)), D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 2, DXGI_FORMAT_R32G32_FLOAT, 0, numeric_cast<UINT>(offsetof(Vertex2D, TexU)), D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 3, DXGI_FORMAT_R32G32_FLOAT, 0, numeric_cast<UINT>(offsetof(Vertex2D, EggFlags)), D3D11_INPUT_PER_VERTEX_DATA, 0}, }; auto d3d_create_input_layout = _ctx->D3DDevice->CreateInputLayout(local_layout, 4, vertex_shader_blob->GetBufferPointer(), vertex_shader_blob->GetBufferSize(), d3d_effect->InputLayout[pass].get_pp()); if (FAILED(d3d_create_input_layout)) { throw EffectLoadException("Failed to create Vertex Shader 2D layout", d3d_create_input_layout, vertex_shader_fname, vertex_shader_content); } } } // Create the pixel shader { string pixel_shader_fname = strex("{}.fofx-{}-frag-hlsl", strex(name).erase_file_extension(), pass + 1); string pixel_shader_content = loader(pixel_shader_fname); FO_VERIFY_AND_THROW(!pixel_shader_content.empty(), "Direct3D effect pixel shader content is empty after loading", name, pass + 1, pixel_shader_fname); nptr<ID3DBlob> pixel_shader_blob {}; nptr<ID3DBlob> error_blob {}; auto pixel_shader_content_cstr = make_ptr(pixel_shader_content.c_str()); ptr<const char> pixel_shader_entry_point = "main"; auto pixel_shader_profile = make_ptr(_ctx->Settings->Direct3DPixelShaderProfile.c_str()); auto d3d_compile = ::D3DCompile(pixel_shader_content_cstr.get(), pixel_shader_content.length(), nullptr, nullptr, nullptr, pixel_shader_entry_point.get(), pixel_shader_profile.get(), 0, 0, pixel_shader_blob.get_pp(), error_blob.get_pp()); if (FAILED(d3d_compile)) { FO_VERIFY_AND_THROW(error_blob, "Shader compilation failed without an error blob"); auto error_blob_holder = MakeComObjectHolder(error_blob); string error = GetBlobString(error_blob); throw EffectLoadException("Failed to compile Pixel Shader", pixel_shader_fname, pixel_shader_content, error); } if (error_blob) { auto error_blob_holder = MakeComObjectHolder(error_blob); } FO_VERIFY_AND_THROW(pixel_shader_blob, "Pixel shader blob is null"); auto pixel_shader_blob_holder = MakeComObjectHolder(pixel_shader_blob); auto d3d_create_pixel_shader = _ctx->D3DDevice->CreatePixelShader(pixel_shader_blob->GetBufferPointer(), pixel_shader_blob->GetBufferSize(), nullptr, d3d_effect->PixelShader[pass].get_pp()); if (FAILED(d3d_create_pixel_shader)) { throw EffectLoadException("Failed to create Pixel Shader from binary", d3d_create_pixel_shader, pixel_shader_fname, pixel_shader_content); } } // Create the blending setup { D3D11_BLEND_DESC blend_desc = {}; blend_desc.RenderTarget[0].BlendEnable = TRUE; blend_desc.RenderTarget[0].SrcBlend = ConvertBlend(d3d_effect->_srcBlendFunc[pass], false); blend_desc.RenderTarget[0].DestBlend = ConvertBlend(d3d_effect->_destBlendFunc[pass], false); blend_desc.RenderTarget[0].BlendOp = ConvertBlendOp(d3d_effect->_blendEquation[pass]); blend_desc.RenderTarget[0].SrcBlendAlpha = ConvertBlend(d3d_effect->_srcBlendFunc[pass], true); blend_desc.RenderTarget[0].DestBlendAlpha = ConvertBlend(d3d_effect->_destBlendFunc[pass], true); blend_desc.RenderTarget[0].BlendOpAlpha = ConvertBlendOp(d3d_effect->_blendEquation[pass]); blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; auto d3d_create_blend_state = _ctx->D3DDevice->CreateBlendState(&blend_desc, d3d_effect->BlendState[pass].get_pp()); if (FAILED(d3d_create_blend_state)) { throw EffectLoadException("Failed to call CreateBlendState", d3d_create_blend_state, name); } } // Create one rasterizer state per cull mode the effect can resolve to. for (size_t cull_mode = 0; cull_mode < EFFECT_CULL_MODES; cull_mode++) { CullModeType cull_mode_type = static_cast<CullModeType>(cull_mode); if (!d3d_effect->IsCullModeUsed(cull_mode_type)) { continue; } D3D11_RASTERIZER_DESC rasterizer_desc = {}; rasterizer_desc.FillMode = D3D11_FILL_SOLID; rasterizer_desc.CullMode = ConvertCullMode(cull_mode_type); rasterizer_desc.FrontCounterClockwise = TRUE; rasterizer_desc.DepthClipEnable = TRUE; rasterizer_desc.ScissorEnable = TRUE; rasterizer_desc.DepthBiasClamp = 0; auto d3d_create_rasterized_state = _ctx->D3DDevice->CreateRasterizerState(&rasterizer_desc, d3d_effect->RasterizerState[cull_mode][pass].get_pp()); if (FAILED(d3d_create_rasterized_state)) { throw EffectLoadException("Failed to call CreateRasterizerState", d3d_create_rasterized_state, name); } } // Create depth-stencil state, one object per depth variant slot the effect can resolve to for (size_t slot = 0; slot < EFFECT_DEPTH_VARIANTS; slot++) { if (!d3d_effect->IsDepthVariantSlotUsed(pass, slot)) { continue; } D3D11_DEPTH_STENCIL_DESC depth_stencil_desc = {}; #if FO_ENABLE_3D if (usage == EffectUsage::Model) { depth_stencil_desc.DepthEnable = TRUE; depth_stencil_desc.DepthWriteMask = d3d_effect->GetDepthVariantWrite(slot) ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; depth_stencil_desc.DepthFunc = ConvertDepthFunc(d3d_effect->GetDepthVariantFunc(pass, slot)); } #endif if (usage == EffectUsage::QuadSprite) { depth_stencil_desc.DepthEnable = TRUE; depth_stencil_desc.DepthWriteMask = d3d_effect->GetDepthVariantWrite(slot) ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; depth_stencil_desc.DepthFunc = ConvertDepthFunc(d3d_effect->GetDepthVariantFunc(pass, slot)); } auto d3d_create_depth_stencil_state = _ctx->D3DDevice->CreateDepthStencilState(&depth_stencil_desc, d3d_effect->DepthStencilState[pass][slot].get_pp()); if (FAILED(d3d_create_depth_stencil_state)) { throw EffectLoadException("Failed to call CreateDepthStencilState", d3d_create_depth_stencil_state, name); } } } return std::move(d3d_effect); } auto Direct3D_Renderer::CreateOrthoMatrix(float32_t left, float32_t right, float32_t bottom, float32_t top, float32_t nearp, float32_t farp) const -> mat44 { FO_STACK_TRACE_ENTRY(); const float32_t& l = left; const float32_t& t = top; const float32_t& r = right; const float32_t& b = bottom; const float32_t& zn = nearp; const float32_t& zf = farp; mat44 result {1.0f}; result[0][0] = 2.0f / (r - l); result[1][0] = 0.0f; result[2][0] = 0.0f; result[3][0] = (l + r) / (l - r); result[0][1] = 0.0f; result[1][1] = 2.0f / (t - b); result[2][1] = 0.0f; result[3][1] = (t + b) / (b - t); result[0][2] = 0.0f; result[1][2] = 0.0f; result[2][2] = 1.0f / (zn - zf); result[3][2] = zn / (zn - zf); result[0][3] = 0.0f; result[1][3] = 0.0f; result[2][3] = 0.0f; result[3][3] = 1.0f; return result; } auto Direct3D_Renderer::GetViewPort() const -> irect32 { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); return _ctx->ViewPortRect; } void Direct3D_Renderer::SetRenderTarget(nptr<RenderTexture> tex) { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); int32_t vp_ox; int32_t vp_oy; int32_t vp_width; int32_t vp_height; int32_t screen_width; int32_t screen_height; if (tex) { auto d3d_tex = tex.dyn_cast<Direct3D_Texture>(); FO_VERIFY_AND_THROW(d3d_tex, "Direct3D render target texture is not of the expected backend type"); _ctx->CurRenderTarget = d3d_tex->RenderTargetView; _ctx->CurDepthStencil = d3d_tex->DepthStencilView; vp_ox = 0; vp_oy = 0; vp_width = d3d_tex->Size.width; vp_height = d3d_tex->Size.height; screen_width = vp_width; screen_height = vp_height; } else { float32_t back_buf_aspect = checked_div<float32_t>(numeric_cast<float32_t>(_ctx->BackBufSize.width), numeric_cast<float32_t>(_ctx->BackBufSize.height)); float32_t screen_aspect = checked_div<float32_t>(numeric_cast<float32_t>(_ctx->Settings->ScreenWidth), numeric_cast<float32_t>(_ctx->Settings->ScreenHeight)); int32_t fit_width = iround<int32_t>(screen_aspect <= back_buf_aspect ? numeric_cast<float32_t>(_ctx->BackBufSize.height) * screen_aspect : numeric_cast<float32_t>(_ctx->BackBufSize.height) * back_buf_aspect); int32_t fit_height = iround<int32_t>(screen_aspect <= back_buf_aspect ? numeric_cast<float32_t>(_ctx->BackBufSize.width) / back_buf_aspect : numeric_cast<float32_t>(_ctx->BackBufSize.width) / screen_aspect); vp_ox = (_ctx->BackBufSize.width - fit_width) / 2; vp_oy = (_ctx->BackBufSize.height - fit_height) / 2; vp_width = fit_width; vp_height = fit_height; screen_width = _ctx->Settings->ScreenWidth; screen_height = _ctx->Settings->ScreenHeight; _ctx->CurRenderTarget = _ctx->MainRenderTarget; _ctx->CurDepthStencil = nullptr; } _ctx->D3DDeviceContext->OMSetRenderTargets(1, _ctx->CurRenderTarget.get_pp(), _ctx->CurDepthStencil.get()); _ctx->ViewPortRect = irect32 {vp_ox, vp_oy, vp_width, vp_height}; _ctx->ViewPort.Width = numeric_cast<FLOAT>(vp_width); _ctx->ViewPort.Height = numeric_cast<FLOAT>(vp_height); _ctx->ViewPort.MinDepth = 0.0f; _ctx->ViewPort.MaxDepth = 1.0f; _ctx->ViewPort.TopLeftX = numeric_cast<FLOAT>(vp_ox); _ctx->ViewPort.TopLeftY = numeric_cast<FLOAT>(vp_oy); _ctx->D3DDeviceContext->RSSetViewports(1, &_ctx->ViewPort); _ctx->ProjMatrix = CreateOrthoMatrix(0.0f, numeric_cast<float32_t>(screen_width), numeric_cast<float32_t>(screen_height), 0.0f, _ctx->OrthoNear, _ctx->OrthoFar); _ctx->DisabledScissorRect.left = vp_ox; _ctx->DisabledScissorRect.top = vp_oy; _ctx->DisabledScissorRect.right = vp_ox + vp_width; _ctx->DisabledScissorRect.bottom = vp_oy + vp_height; _ctx->TargetSize = {screen_width, screen_height}; } void Direct3D_Renderer::SetOrthoDepthRange(float32_t nearp, float32_t farp) noexcept { FO_STACK_TRACE_ENTRY(); _ctx->OrthoNear = nearp; _ctx->OrthoFar = farp; _ctx->ProjMatrix = CreateOrthoMatrix(0.0f, numeric_cast<float32_t>(_ctx->TargetSize.width), numeric_cast<float32_t>(_ctx->TargetSize.height), 0.0f, nearp, farp); } auto Direct3D_Renderer::GetProjMatrix() const -> mat44 { FO_NO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); return _ctx->ProjMatrix; } void Direct3D_Renderer::ClearRenderTarget(optional<ucolor> color, bool depth, bool stencil) { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); if (color.has_value()) { float32_t r = numeric_cast<float32_t>(color.value().comp.r) / 255.0f; float32_t g = numeric_cast<float32_t>(color.value().comp.g) / 255.0f; float32_t b = numeric_cast<float32_t>(color.value().comp.b) / 255.0f; float32_t a = numeric_cast<float32_t>(color.value().comp.a) / 255.0f; const float32_t color_rgba[] {r, g, b, a}; _ctx->D3DDeviceContext->ClearRenderTargetView(_ctx->CurRenderTarget.get(), color_rgba); } if ((depth || stencil) && _ctx->CurDepthStencil) { UINT clear_flags = 0; if (depth) { clear_flags |= D3D11_CLEAR_DEPTH; } if (stencil) { clear_flags |= D3D11_CLEAR_STENCIL; } _ctx->D3DDeviceContext->ClearDepthStencilView(_ctx->CurDepthStencil.get(), clear_flags, 1.0f, 0); } } void Direct3D_Renderer::EnableScissor(irect32 rect) { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); if (_ctx->ViewPortRect.width != _ctx->TargetSize.width || _ctx->ViewPortRect.height != _ctx->TargetSize.height) { float32_t x_ratio = numeric_cast<float32_t>(_ctx->ViewPortRect.width) / numeric_cast<float32_t>(_ctx->TargetSize.width); float32_t y_ratio = numeric_cast<float32_t>(_ctx->ViewPortRect.height) / numeric_cast<float32_t>(_ctx->TargetSize.height); _ctx->ScissorRect.left = _ctx->ViewPortRect.x + iround<int32_t>(numeric_cast<float32_t>(rect.x) * x_ratio); _ctx->ScissorRect.top = _ctx->ViewPortRect.y + iround<int32_t>(numeric_cast<float32_t>(rect.y) * y_ratio); _ctx->ScissorRect.right = _ctx->ViewPortRect.x + iround<int32_t>(numeric_cast<float32_t>(rect.x + rect.width) * x_ratio); _ctx->ScissorRect.bottom = _ctx->ViewPortRect.y + iround<int32_t>(numeric_cast<float32_t>(rect.y + rect.height) * y_ratio); } else { _ctx->ScissorRect.left = _ctx->ViewPortRect.x + rect.x; _ctx->ScissorRect.top = _ctx->ViewPortRect.y + rect.y; _ctx->ScissorRect.right = _ctx->ViewPortRect.x + rect.x + rect.width; _ctx->ScissorRect.bottom = _ctx->ViewPortRect.y + rect.y + rect.height; } _ctx->ScissorEnabled = true; } void Direct3D_Renderer::DisableScissor() { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); _ctx->ScissorEnabled = false; } void Direct3D_Renderer::OnResizeWindow(isize32 size) { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(_ctx, "Context is null"); bool is_cur_rt = _ctx->CurRenderTarget == _ctx->MainRenderTarget; if (is_cur_rt) { _ctx->CurRenderTarget = nullptr; FO_VERIFY_AND_THROW(!_ctx->CurDepthStencil, "Direct3D resize expected no current depth-stencil when the main render target is active", size, _ctx->BackBufSize); _ctx->D3DDeviceContext->OMSetRenderTargets(1, _ctx->CurRenderTarget.get_pp(), _ctx->CurDepthStencil.get()); } ReleaseComObjectSlot(_ctx->MainRenderTarget); auto d3d_resize_buffers = _ctx->SwapChain->ResizeBuffers(0, size.width, size.height, DXGI_FORMAT_UNKNOWN, 0); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_resize_buffers), "Direct3D ResizeBuffers failed while resizing the main swap chain", d3d_resize_buffers, size, _ctx->BackBufSize); nptr<ID3D11Texture2D> back_buf {}; auto d3d_get_back_buf = _ctx->SwapChain->GetBuffer(0, IID_PPV_ARGS(back_buf.get_pp())); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_get_back_buf), "Direct3D swap chain GetBuffer failed after window resize", d3d_get_back_buf, size); FO_VERIFY_AND_THROW(back_buf, "Direct3D swap chain GetBuffer returned a null back buffer after resize", size); auto back_buf_holder = MakeComObjectHolder(back_buf); auto d3d_create_back_buf_rt_view = _ctx->D3DDevice->CreateRenderTargetView(back_buf.get(), nullptr, _ctx->MainRenderTarget.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_back_buf_rt_view), "Direct3D CreateRenderTargetView failed for the resized swap-chain back buffer", d3d_create_back_buf_rt_view, size); _ctx->BackBufSize = size; if (is_cur_rt) { SetRenderTarget(nullptr); } } Direct3D_Texture::~Direct3D_Texture() { FO_STACK_TRACE_ENTRY(); ReleaseComObjectSlot(TexHandle); ReleaseComObjectSlot(DepthStencil); ReleaseComObjectSlot(RenderTargetView); ReleaseComObjectSlot(DepthStencilView); ReleaseComObjectSlot(ShaderTexView); } auto Direct3D_Texture::GetTexturePixel(ipos32 pos) const -> ucolor { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(Size.is_valid_pos(pos), "Requested Direct3D texture pixel is outside texture bounds", pos, Size); auto d3d_device_context = _ctx->D3DDeviceContext; FO_VERIFY_AND_THROW(d3d_device_context, "Direct3D device context is null"); D3D11_BOX src_box; src_box.left = pos.x; src_box.top = pos.y; src_box.right = pos.x + 1; src_box.bottom = pos.y + 1; src_box.front = 0; src_box.back = 1; d3d_device_context->CopySubresourceRegion(_ctx->OnePixStagingTex.get_no_const(), 0, 0, 0, 0, TexHandle.get_no_const(), 0, &src_box); D3D11_MAPPED_SUBRESOURCE tex_resource; auto d3d_map_staging_texture = d3d_device_context->Map(_ctx->OnePixStagingTex.get_no_const(), 0, D3D11_MAP_READ, 0, &tex_resource); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_map_staging_texture), "Direct3D Map failed for the one-pixel staging texture", d3d_map_staging_texture, pos, Size); auto staging_unmap = scope_fail([&]() noexcept { d3d_device_context->Unmap(_ctx->OnePixStagingTex.get_no_const(), 0); }); auto mapped_data = make_nptr(tex_resource.pData); FO_VERIFY_AND_THROW(mapped_data, "Mapped texture data pointer is null"); ucolor result = *mapped_data.reinterpret_as<ucolor>(); d3d_device_context->Unmap(_ctx->OnePixStagingTex.get_no_const(), 0); staging_unmap.release(); return result; } auto Direct3D_Texture::GetTextureRegion(ipos32 pos, isize32 size) const -> vector<ucolor> { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(size.width > 0, "Size width must be positive", size.width); FO_VERIFY_AND_THROW(size.height > 0, "Size height must be positive", size.height); FO_VERIFY_AND_THROW(pos.x >= 0, "Position x is negative", pos.x); FO_VERIFY_AND_THROW(pos.y >= 0, "Position y is negative", pos.y); FO_VERIFY_AND_THROW(pos.x + size.width <= Size.width, "Requested texture read rectangle right edge is outside texture bounds", pos.x, size.width, Size.width); FO_VERIFY_AND_THROW(pos.y + size.height <= Size.height, "Requested texture read rectangle bottom edge is outside texture bounds", pos.y, size.height, Size.height); auto d3d_device = _ctx->D3DDevice; FO_VERIFY_AND_THROW(d3d_device, "Direct3D device is null"); auto d3d_device_context = _ctx->D3DDeviceContext; FO_VERIFY_AND_THROW(d3d_device_context, "Direct3D device context is null"); vector<ucolor> result; result.resize(numeric_cast<size_t>(size.width) * size.height); D3D11_TEXTURE2D_DESC staging_desc; staging_desc.Width = size.width; staging_desc.Height = size.height; staging_desc.MipLevels = 1; staging_desc.ArraySize = 1; staging_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; staging_desc.SampleDesc.Count = 1; staging_desc.SampleDesc.Quality = 0; staging_desc.Usage = D3D11_USAGE_STAGING; staging_desc.BindFlags = 0; staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; staging_desc.MiscFlags = 0; nptr<ID3D11Texture2D> staging_tex {}; auto d3d_create_staging_tex = d3d_device->CreateTexture2D(&staging_desc, nullptr, staging_tex.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_staging_tex), "Direct3D CreateTexture2D failed for a texture-region staging texture", d3d_create_staging_tex, pos, size, Size); FO_VERIFY_AND_THROW(staging_tex, "Staging texture is null"); auto staging_tex_holder = MakeComObjectHolder(staging_tex); D3D11_BOX src_box; src_box.left = pos.x; src_box.top = pos.y; src_box.right = pos.x + size.width; src_box.bottom = pos.y + size.height; src_box.front = 0; src_box.back = 1; d3d_device_context->CopySubresourceRegion(staging_tex.get(), 0, 0, 0, 0, TexHandle.get_no_const(), 0, &src_box); D3D11_MAPPED_SUBRESOURCE tex_resource; auto d3d_map_staging_texture = d3d_device_context->Map(staging_tex.get(), 0, D3D11_MAP_READ, 0, &tex_resource); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_map_staging_texture), "Direct3D Map failed for a texture-region staging texture", d3d_map_staging_texture, pos, size, Size); auto mapped_data = make_nptr(tex_resource.pData); FO_VERIFY_AND_THROW(mapped_data, "Mapped texture data pointer is null"); auto mapped_bytes = mapped_data.reinterpret_as<uint8_t>(); for (int32_t i = 0; i < size.height; i++) { auto src = mapped_bytes.offset(numeric_cast<size_t>(tex_resource.RowPitch) * i); MemCopy(&result[numeric_cast<size_t>(i) * size.width], src, numeric_cast<size_t>(size.width) * 4); } d3d_device_context->Unmap(staging_tex.get(), 0); return result; } void Direct3D_Texture::UpdateTextureRegion(ipos32 pos, isize32 size, const_span<ucolor> data, bool use_dest_pitch) { FO_STACK_TRACE_ENTRY(); FO_VERIFY_AND_THROW(pos.x >= 0, "Position x is negative", pos.x); FO_VERIFY_AND_THROW(pos.y >= 0, "Position y is negative", pos.y); FO_VERIFY_AND_THROW(pos.x + size.width <= Size.width, "Texture update rectangle right edge is outside texture bounds", pos.x, size.width, Size.width); FO_VERIFY_AND_THROW(pos.y + size.height <= Size.height, "Texture update rectangle bottom edge is outside texture bounds", pos.y, size.height, Size.height); size_t src_pitch_size = numeric_cast<size_t>(use_dest_pitch ? Size.width : size.width); size_t required_size = size.height != 0 ? (numeric_cast<size_t>(size.height - 1) * src_pitch_size + numeric_cast<size_t>(size.width)) : 0; FO_VERIFY_AND_THROW(data.size() >= required_size, "Texture update source data is smaller than the required region size", data.size(), required_size, size, use_dest_pitch); D3D11_BOX dest_box; dest_box.left = pos.x; dest_box.top = pos.y; dest_box.right = pos.x + size.width; dest_box.bottom = pos.y + size.height; dest_box.front = 0; dest_box.back = 1; UINT src_pitch = (use_dest_pitch ? Size.width : size.width) * 4; auto source_data = make_nptr(data.data()); FO_VERIFY_AND_THROW(required_size == 0 || source_data, "Texture update source data is null for a non-empty region"); _ctx->D3DDeviceContext->UpdateSubresource(TexHandle.get(), 0, &dest_box, source_data.get(), src_pitch, 0); } Direct3D_DrawBuffer::~Direct3D_DrawBuffer() { FO_STACK_TRACE_ENTRY(); ReleaseComObjectSlot(VertexBuf); ReleaseComObjectSlot(IndexBuf); } void Direct3D_DrawBuffer::Upload(EffectUsage usage, optional<size_t> custom_vertices_size, optional<size_t> custom_indices_size) { FO_STACK_TRACE_ENTRY(); if (IsStatic && !StaticDataChanged) { return; } // Fill vertex buffer size_t upload_vertices; size_t vert_size; #if FO_ENABLE_3D if (usage == EffectUsage::Model) { FO_VERIFY_AND_THROW(Vertices.empty(), "Direct3D model draw buffer upload received 2D vertices alongside 3D vertices", Vertices.size(), Vertices3D.size(), VertCount); upload_vertices = custom_vertices_size.value_or(VertCount); vert_size = sizeof(Vertex3D); } else { FO_VERIFY_AND_THROW(Vertices3D.empty(), "Direct3D 2D draw buffer upload received 3D vertices alongside 2D vertices", Vertices3D.size(), Vertices.size(), VertCount); upload_vertices = custom_vertices_size.value_or(VertCount); vert_size = sizeof(Vertex2D); } #else ignore_unused(usage); upload_vertices = custom_vertices_size.value_or(VertCount); vert_size = sizeof(Vertex2D); #endif if (VertexBuf == nullptr || upload_vertices > VertexBufSize) { ReleaseComObjectSlot(VertexBuf); VertexBufSize = upload_vertices + 1024; D3D11_BUFFER_DESC vbuf_desc = {}; vbuf_desc.Usage = D3D11_USAGE_DYNAMIC; vbuf_desc.ByteWidth = numeric_cast<UINT>(VertexBufSize * vert_size); vbuf_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vbuf_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; vbuf_desc.MiscFlags = 0; auto d3d_create_vertex_buffer = _ctx->D3DDevice->CreateBuffer(&vbuf_desc, nullptr, VertexBuf.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_vertex_buffer), "Direct3D CreateBuffer failed for a dynamic vertex buffer", d3d_create_vertex_buffer, upload_vertices, vert_size, VertexBufSize, usage); } D3D11_MAPPED_SUBRESOURCE vertices_resource; auto d3d_map_vertex_buffer = _ctx->D3DDeviceContext->Map(VertexBuf.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &vertices_resource); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_map_vertex_buffer), "Direct3D Map failed for a dynamic vertex buffer", d3d_map_vertex_buffer, upload_vertices, vert_size, VertexBufSize, usage); auto vertex_unmap = scope_fail([&]() noexcept { _ctx->D3DDeviceContext->Unmap(VertexBuf.get(), 0); }); auto vertices_dst = make_nptr(vertices_resource.pData); FO_VERIFY_AND_THROW(vertices_dst, "Mapped subresource data pointer is null"); if (upload_vertices != 0) { #if FO_ENABLE_3D if (usage == EffectUsage::Model) { MemCopy(vertices_dst, Vertices3D.data(), upload_vertices * vert_size); } else { MemCopy(vertices_dst, Vertices.data(), upload_vertices * vert_size); } #else MemCopy(vertices_dst, Vertices.data(), upload_vertices * vert_size); #endif } _ctx->D3DDeviceContext->Unmap(VertexBuf.get(), 0); vertex_unmap.release(); // Fill index buffer auto upload_indices = custom_indices_size.value_or(IndCount); if (IndexBuf == nullptr || upload_indices > IndexBufSize) { ReleaseComObjectSlot(IndexBuf); IndexBufSize = upload_indices + 1024; D3D11_BUFFER_DESC ibuf_desc = {}; ibuf_desc.Usage = D3D11_USAGE_DYNAMIC; ibuf_desc.ByteWidth = numeric_cast<UINT>(IndexBufSize * sizeof(vindex_t)); ibuf_desc.BindFlags = D3D11_BIND_INDEX_BUFFER; ibuf_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; ibuf_desc.MiscFlags = 0; auto d3d_create_index_buffer = _ctx->D3DDevice->CreateBuffer(&ibuf_desc, nullptr, IndexBuf.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_index_buffer), "Direct3D CreateBuffer failed for a dynamic index buffer", d3d_create_index_buffer, upload_indices, sizeof(vindex_t), IndexBufSize); } D3D11_MAPPED_SUBRESOURCE indices_resource; auto d3d_map_index_buffer = _ctx->D3DDeviceContext->Map(IndexBuf.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &indices_resource); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_map_index_buffer), "Direct3D Map failed for a dynamic index buffer", d3d_map_index_buffer, upload_indices, sizeof(vindex_t), IndexBufSize); auto index_unmap = scope_fail([&]() noexcept { _ctx->D3DDeviceContext->Unmap(IndexBuf.get(), 0); }); if (upload_indices != 0) { auto indices_dst = make_nptr(indices_resource.pData); FO_VERIFY_AND_THROW(indices_dst, "Mapped subresource data pointer is null"); MemCopy(indices_dst, Indices.data(), upload_indices * sizeof(vindex_t)); } _ctx->D3DDeviceContext->Unmap(IndexBuf.get(), 0); index_unmap.release(); StaticDataChanged = false; } Direct3D_Effect::~Direct3D_Effect() { FO_STACK_TRACE_ENTRY(); for (size_t i = 0; i < EFFECT_MAX_PASSES; i++) { ReleaseComObjectSlot(VertexShader[i]); ReleaseComObjectSlot(InputLayout[i]); ReleaseComObjectSlot(PixelShader[i]); ReleaseComObjectSlot(BlendState[i]); for (size_t cull_mode = 0; cull_mode < EFFECT_CULL_MODES; cull_mode++) { ReleaseComObjectSlot(RasterizerState[cull_mode][i]); } for (size_t slot = 0; slot < EFFECT_DEPTH_VARIANTS; slot++) { ReleaseComObjectSlot(DepthStencilState[i][slot]); } } ReleaseComObjectSlot(Cb_ProjBuf); ReleaseComObjectSlot(Cb_MainTexBuf); ReleaseComObjectSlot(Cb_EggBuf); ReleaseComObjectSlot(Cb_SpriteBorderBuf); ReleaseComObjectSlot(Cb_ParticleSamplingBuf); ReleaseComObjectSlot(Cb_TimeBuf); ReleaseComObjectSlot(Cb_RandomValueBuf); ReleaseComObjectSlot(Cb_ScriptValueBuf); ReleaseComObjectSlot(Cb_CameraBuf); #if FO_ENABLE_3D ReleaseComObjectSlot(Cb_ModelBuf); ReleaseComObjectSlot(Cb_ModelTexBuf); ReleaseComObjectSlot(Cb_ModelAnimBuf); #endif } void Direct3D_Effect::DrawBuffer(ptr<RenderDrawBuffer> dbuf, size_t start_index, optional<size_t> indices_to_draw, nptr<const RenderTexture> custom_tex) { FO_STACK_TRACE_ENTRY(); auto d3d_dbuf = dbuf.dyn_cast<Direct3D_DrawBuffer>(); FO_VERIFY_AND_THROW(d3d_dbuf, "Direct3D draw buffer is not of the expected backend type"); #if FO_ENABLE_3D if (!custom_tex && ModelTex[0]) { custom_tex = ModelTex[0]; } #endif if (!custom_tex && MainTex) { custom_tex = MainTex; } nptr<const RenderTexture> main_tex_source = custom_tex ? custom_tex : _ctx->DummyTexture; FO_VERIFY_AND_THROW(main_tex_source, "Direct3D dummy texture is not created"); auto main_tex = main_tex_source.dyn_cast<const Direct3D_Texture>(); FO_VERIFY_AND_THROW(main_tex, "Direct3D main texture is not of the expected backend type"); auto draw_mode = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; if (_usage == EffectUsage::Primitive) { switch (d3d_dbuf->PrimType) { case RenderPrimitiveType::PointList: draw_mode = D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; break; case RenderPrimitiveType::LineList: draw_mode = D3D11_PRIMITIVE_TOPOLOGY_LINELIST; break; case RenderPrimitiveType::LineStrip: draw_mode = D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP; break; case RenderPrimitiveType::TriangleList: draw_mode = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; break; case RenderPrimitiveType::TriangleStrip: draw_mode = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; break; } } // Fill constant buffers auto setup_cbuffer = [this](auto&& buf, auto&& buf_handle) { if (buf_handle == nullptr) { D3D11_BUFFER_DESC cbuf_desc = {}; cbuf_desc.ByteWidth = sizeof(buf); cbuf_desc.Usage = D3D11_USAGE_DYNAMIC; cbuf_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; cbuf_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; cbuf_desc.MiscFlags = 0; auto d3d_create_cbuffer = _ctx->D3DDevice->CreateBuffer(&cbuf_desc, nullptr, buf_handle.get_pp()); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_create_cbuffer), "Direct3D CreateBuffer failed for an effect constant buffer", d3d_create_cbuffer, sizeof(buf)); } D3D11_MAPPED_SUBRESOURCE cbuffer_resource; auto d3d_map_cbuffer = _ctx->D3DDeviceContext->Map(buf_handle.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &cbuffer_resource); FO_VERIFY_AND_THROW(SUCCEEDED(d3d_map_cbuffer), "Direct3D Map failed for an effect constant buffer", d3d_map_cbuffer, sizeof(buf)); auto cbuffer_unmap = scope_fail([&]() noexcept { _ctx->D3DDeviceContext->Unmap(buf_handle.get(), 0); }); auto cbuffer_dst = make_nptr(cbuffer_resource.pData); FO_VERIFY_AND_THROW(cbuffer_dst, "Mapped subresource data pointer is null"); #if FO_ENABLE_3D if constexpr (std::same_as<std::decay_t<decltype(buf)>, ModelBuffer>) { auto bind_size = sizeof(ModelBuffer) - (MODEL_MAX_BONES - MatrixCount) * sizeof(float32_t) * 16; MemCopy(cbuffer_dst, &buf, bind_size); } else #endif { ignore_unused(this); MemCopy(cbuffer_dst, &buf, sizeof(buf)); } _ctx->D3DDeviceContext->Unmap(buf_handle.get(), 0); cbuffer_unmap.release(); }; if (_needProjBuf && !ProjBuf.has_value()) { auto& proj_buf = ProjBuf = ProjBuffer(); auto projection_matrix = proj_buf->ProjMatrix; auto projection_matrix_values = make_ptr(glm::value_ptr(_ctx->ProjMatrix)); MemCopy(projection_matrix, projection_matrix_values, 16 * sizeof(float32_t)); } if (_needMainTexBuf && !MainTexBuf.has_value()) { auto& main_tex_buf = MainTexBuf = MainTexBuffer(); auto main_texture_size = main_tex_buf->MainTexSize; auto main_texture_size_data = main_tex->SizeData; MemCopy(main_texture_size, main_texture_size_data, 4 * sizeof(float32_t)); } auto upload_cbuffer = [&setup_cbuffer](bool need_buf, auto& buf, auto& cbuf, bool reset_buf) { if (!need_buf || !buf.has_value()) { return; } setup_cbuffer(*buf, cbuf); if (reset_buf) { buf.reset(); } }; upload_cbuffer(_needProjBuf, ProjBuf, Cb_ProjBuf, true); upload_cbuffer(_needMainTexBuf, MainTexBuf, Cb_MainTexBuf, true); upload_cbuffer(_needEggBuf, EggBuf, Cb_EggBuf, true); upload_cbuffer(_needSpriteBorderBuf, SpriteBorderBuf, Cb_SpriteBorderBuf, true); upload_cbuffer(_needParticleSamplingBuf, ParticleSamplingBuf, Cb_ParticleSamplingBuf, true); upload_cbuffer(_needTimeBuf, TimeBuf, Cb_TimeBuf, true); upload_cbuffer(_needRandomValueBuf, RandomValueBuf, Cb_RandomValueBuf, true); upload_cbuffer(_needScriptValueBuf, ScriptValueBuf, Cb_ScriptValueBuf, false); upload_cbuffer(_needCameraBuf, CameraBuf, Cb_CameraBuf, true); #if FO_ENABLE_3D upload_cbuffer(_needModelBuf, ModelBuf, Cb_ModelBuf, true); upload_cbuffer(_needModelTexBuf, ModelTexBuf, Cb_ModelTexBuf, true); upload_cbuffer(_needModelAnimBuf, ModelAnimBuf, Cb_ModelAnimBuf, true); #endif auto draw_count = numeric_cast<UINT>(indices_to_draw.value_or(d3d_dbuf->IndCount)); for (size_t pass = 0; pass < _passCount; pass++) { #if FO_ENABLE_3D if (DisableShadow && _isShadow[pass]) { continue; } #endif // Setup shader and vertex buffers #if FO_ENABLE_3D UINT stride = _usage == EffectUsage::Model ? sizeof(Vertex3D) : sizeof(Vertex2D); #else UINT stride = sizeof(Vertex2D); #endif UINT offset = 0; _ctx->D3DDeviceContext->IASetInputLayout(InputLayout[pass].get()); _ctx->D3DDeviceContext->IASetVertexBuffers(0, 1, d3d_dbuf->VertexBuf.get_pp(), &stride, &offset); if constexpr (sizeof(vindex_t) == 2) { _ctx->D3DDeviceContext->IASetIndexBuffer(d3d_dbuf->IndexBuf.get(), DXGI_FORMAT_R16_UINT, 0); } else { _ctx->D3DDeviceContext->IASetIndexBuffer(d3d_dbuf->IndexBuf.get(), DXGI_FORMAT_R32_UINT, 0); } _ctx->D3DDeviceContext->IASetPrimitiveTopology(draw_mode); _ctx->D3DDeviceContext->VSSetShader(VertexShader[pass].get(), nullptr, 0); _ctx->D3DDeviceContext->PSSetShader(PixelShader[pass].get(), nullptr, 0); auto set_cbuffer = [ctx = _ctx](int32_t pos, auto& cbuf) mutable { if (pos != -1 && cbuf) { ctx->D3DDeviceContext->VSSetConstantBuffers(pos, 1, cbuf.get_pp()); ctx->D3DDeviceContext->PSSetConstantBuffers(pos, 1, cbuf.get_pp()); } }; set_cbuffer(_posProjBuf[pass], Cb_ProjBuf); set_cbuffer(_posMainTexBuf[pass], Cb_MainTexBuf); set_cbuffer(_posEggBuf[pass], Cb_EggBuf); set_cbuffer(_posSpriteBorderBuf[pass], Cb_SpriteBorderBuf); set_cbuffer(_posParticleSamplingBuf[pass], Cb_ParticleSamplingBuf); set_cbuffer(_posTimeBuf[pass], Cb_TimeBuf); set_cbuffer(_posRandomValueBuf[pass], Cb_RandomValueBuf); set_cbuffer(_posScriptValueBuf[pass], Cb_ScriptValueBuf); set_cbuffer(_posCameraBuf[pass], Cb_CameraBuf); #if FO_ENABLE_3D set_cbuffer(_posModelBuf[pass], Cb_ModelBuf); set_cbuffer(_posModelTexBuf[pass], Cb_ModelTexBuf); set_cbuffer(_posModelAnimBuf[pass], Cb_ModelAnimBuf); #endif auto find_tex_sampler = [ctx = _ctx](ptr<const Direct3D_Texture> tex) { if (tex->LinearFiltered) { return ctx->LinearSampler.get_pp(); } else { return ctx->PointSampler.get_pp(); } }; if (_posMainTex[pass] != -1) { _ctx->D3DDeviceContext->PSSetShaderResources(_posMainTex[pass], 1, main_tex->ShaderTexView.get_pp()); _ctx->D3DDeviceContext->PSSetSamplers(_posMainTex[pass], 1, find_tex_sampler(main_tex)); } if (_posIndoorMaskTex[pass] != -1) { nptr<const RenderTexture> indoor_tex_source = IndoorMaskTex ? IndoorMaskTex : _ctx->DummyTexture; FO_VERIFY_AND_THROW(indoor_tex_source, "Direct3D dummy texture is not created"); auto indoor_tex = indoor_tex_source.dyn_cast<const Direct3D_Texture>(); FO_VERIFY_AND_THROW(indoor_tex, "Direct3D indoor mask texture is not of the expected backend type"); _ctx->D3DDeviceContext->PSSetShaderResources(_posIndoorMaskTex[pass], 1, indoor_tex->ShaderTexView.get_pp()); _ctx->D3DDeviceContext->PSSetSamplers(_posIndoorMaskTex[pass], 1, find_tex_sampler(indoor_tex)); } if (_posBackgroundTex[pass] != -1) { nptr<const RenderTexture> background_tex_source = BackgroundTex ? BackgroundTex : _ctx->DummyTexture; FO_VERIFY_AND_THROW(background_tex_source, "Direct3D dummy texture is not created"); auto background_tex = background_tex_source.dyn_cast<const Direct3D_Texture>(); FO_VERIFY_AND_THROW(background_tex, "Direct3D background texture is not of the expected backend type"); _ctx->D3DDeviceContext->PSSetShaderResources(_posBackgroundTex[pass], 1, background_tex->ShaderTexView.get_pp()); _ctx->D3DDeviceContext->PSSetSamplers(_posBackgroundTex[pass], 1, find_tex_sampler(background_tex)); } #if FO_ENABLE_3D if (_needModelTex[pass]) { for (size_t i = 0; i < MODEL_MAX_TEXTURES; i++) { if (_posModelTex[pass][i] != -1) { nptr<RenderTexture> model_tex_source = ModelTex[i] ? ModelTex[i] : _ctx->DummyTexture; FO_VERIFY_AND_THROW(model_tex_source, "Direct3D dummy texture is not created"); auto model_tex = model_tex_source.dyn_cast<Direct3D_Texture>(); FO_VERIFY_AND_THROW(model_tex, "Direct3D model texture is not of the expected backend type"); _ctx->D3DDeviceContext->PSSetShaderResources(_posModelTex[pass][i], 1, model_tex->ShaderTexView.get_pp()); _ctx->D3DDeviceContext->PSSetSamplers(_posModelTex[pass][i], 1, find_tex_sampler(model_tex)); } } } #endif constexpr float32_t blend_factor[4] = {0.0f, 0.0f, 0.0f, 0.0f}; _ctx->D3DDeviceContext->OMSetBlendState(DisableBlending ? nullptr : BlendState[pass].get(), blend_factor, 0xFFFFFFFF); _ctx->D3DDeviceContext->OMSetDepthStencilState(DepthStencilState[pass][ResolveDepthVariantSlot(pass)].get(), 0); _ctx->D3DDeviceContext->RSSetState(RasterizerState[static_cast<size_t>(ResolveCullMode())][pass].get()); _ctx->D3DDeviceContext->RSSetScissorRects(1, _ctx->ScissorEnabled ? &_ctx->ScissorRect : &_ctx->DisabledScissorRect); if (_ctx->FeatureLevel <= D3D_FEATURE_LEVEL_9_3 && draw_mode == D3D11_PRIMITIVE_TOPOLOGY_POINTLIST) { FO_VERIFY_AND_THROW(start_index == 0, "Direct3D 9_3 point-list fallback cannot draw with a non-zero start index", start_index, draw_count); _ctx->D3DDeviceContext->Draw(draw_count, 0); } else { _ctx->D3DDeviceContext->DrawIndexed(draw_count, numeric_cast<UINT>(start_index), 0); } // Unbind constexpr ID3D11Buffer* null_buf = nullptr; constexpr ID3D11ShaderResourceView* null_res = nullptr; constexpr ID3D11SamplerState* null_sampler = nullptr; auto unset_cbuffer = [ctx = _ctx, &null_buf](int32_t pos, auto& cbuf) mutable { if (pos != -1 && cbuf) { ctx->D3DDeviceContext->VSSetConstantBuffers(pos, 1, &null_buf); ctx->D3DDeviceContext->PSSetConstantBuffers(pos, 1, &null_buf); } }; unset_cbuffer(_posProjBuf[pass], Cb_ProjBuf); unset_cbuffer(_posMainTexBuf[pass], Cb_MainTexBuf); unset_cbuffer(_posEggBuf[pass], Cb_EggBuf); unset_cbuffer(_posSpriteBorderBuf[pass], Cb_SpriteBorderBuf); unset_cbuffer(_posParticleSamplingBuf[pass], Cb_ParticleSamplingBuf); unset_cbuffer(_posTimeBuf[pass], Cb_TimeBuf); unset_cbuffer(_posRandomValueBuf[pass], Cb_RandomValueBuf); unset_cbuffer(_posScriptValueBuf[pass], Cb_ScriptValueBuf); unset_cbuffer(_posCameraBuf[pass], Cb_CameraBuf); #if FO_ENABLE_3D unset_cbuffer(_posModelBuf[pass], Cb_ModelBuf); unset_cbuffer(_posModelTexBuf[pass], Cb_ModelTexBuf); unset_cbuffer(_posModelAnimBuf[pass], Cb_ModelAnimBuf); #endif if (_posMainTex[pass] != -1) { _ctx->D3DDeviceContext->PSSetShaderResources(_posMainTex[pass], 1, &null_res); _ctx->D3DDeviceContext->PSSetSamplers(_posMainTex[pass], 1, &null_sampler); } if (_posIndoorMaskTex[pass] != -1) { _ctx->D3DDeviceContext->PSSetShaderResources(_posIndoorMaskTex[pass], 1, &null_res); _ctx->D3DDeviceContext->PSSetSamplers(_posIndoorMaskTex[pass], 1, &null_sampler); } if (_posBackgroundTex[pass] != -1) { _ctx->D3DDeviceContext->PSSetShaderResources(_posBackgroundTex[pass], 1, &null_res); _ctx->D3DDeviceContext->PSSetSamplers(_posBackgroundTex[pass], 1, &null_sampler); } #if FO_ENABLE_3D if (_needModelTex[pass]) { for (size_t i = 0; i < MODEL_MAX_TEXTURES; i++) { if (_posModelTex[pass][i] != -1) { _ctx->D3DDeviceContext->PSSetShaderResources(_posModelTex[pass][i], 0, &null_res); _ctx->D3DDeviceContext->PSSetSamplers(_posModelTex[pass][i], 1, &null_sampler); } } } #endif _ctx->D3DDeviceContext->IASetInputLayout(nullptr); _ctx->D3DDeviceContext->IASetVertexBuffers(0, 1, &null_buf, &stride, &offset); if constexpr (sizeof(vindex_t) == 2) { _ctx->D3DDeviceContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0); } else { _ctx->D3DDeviceContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_R32_UINT, 0); } _ctx->D3DDeviceContext->VSSetShader(nullptr, nullptr, 0); _ctx->D3DDeviceContext->PSSetShader(nullptr, nullptr, 0); } } FO_END_NAMESPACE #endif