From b0fb18e388b4770cbac469a7e1f676636148810f Mon Sep 17 00:00:00 2001 From: praydog Date: Sat, 10 Jun 2023 13:47:12 -0700 Subject: [PATCH] D3D12: Add 2D Screen/Cinema mode --- src/Framework.cpp | 3 + src/Framework.hpp | 5 +- src/mods/VR.cpp | 17 ++ src/mods/VR.hpp | 15 +- src/mods/vr/D3D12Component.cpp | 289 +++++++++++++++++------ src/mods/vr/D3D12Component.hpp | 19 +- src/mods/vr/FFakeStereoRenderingHook.cpp | 76 ++++-- src/mods/vr/OverlayComponent.cpp | 19 +- src/mods/vr/OverlayComponent.hpp | 6 +- src/mods/vr/d3d12/CommandContext.cpp | 27 ++- src/mods/vr/d3d12/CommandContext.hpp | 3 + src/mods/vr/d3d12/DirectXTK.cpp | 95 +++++++- src/mods/vr/d3d12/DirectXTK.hpp | 9 + src/mods/vr/runtimes/OpenXR.hpp | 1 + 14 files changed, 481 insertions(+), 103 deletions(-) diff --git a/src/Framework.cpp b/src/Framework.cpp index 52f561dc..35383212 100644 --- a/src/Framework.cpp +++ b/src/Framework.cpp @@ -1641,6 +1641,7 @@ bool Framework::init_d3d11() { m_d3d11.rt_width = backbuffer_desc.Width; m_d3d11.rt_height = backbuffer_desc.Height; + m_last_rt_size = {backbuffer_desc.Width, backbuffer_desc.Height}; spdlog::info("[D3D11] Initializing ImGui D3D11..."); @@ -1783,6 +1784,8 @@ bool Framework::init_d3d12() { m_d3d12.rt_width = (uint32_t)desc.Width; m_d3d12.rt_height = (uint32_t)desc.Height; + + m_last_rt_size = {desc.Width, desc.Height}; } spdlog::info("[D3D12] Initializing ImGui..."); diff --git a/src/Framework.hpp b/src/Framework.hpp index 22cc428d..ab0cf3aa 100644 --- a/src/Framework.hpp +++ b/src/Framework.hpp @@ -154,11 +154,11 @@ class Framework { } Vector2f get_d3d11_rt_size() const { - return Vector2f{(float)m_d3d11.rt_width, (float)m_d3d11.rt_height}; + return m_last_rt_size; } Vector2f get_d3d12_rt_size() const { - return Vector2f{(float)m_d3d12.rt_width, (float)m_d3d12.rt_height}; + return m_last_rt_size; } Vector2f get_rt_size() const { @@ -218,6 +218,7 @@ class Framework { ImVec2 m_last_window_pos{}; ImVec2 m_last_window_size{}; + Vector2f m_last_rt_size{1920, 1080}; struct AdditionalFont { std::filesystem::path filepath{}; diff --git a/src/mods/VR.cpp b/src/mods/VR.cpp index 6c335100..53f128cb 100644 --- a/src/mods/VR.cpp +++ b/src/mods/VR.cpp @@ -1829,6 +1829,22 @@ void VR::on_post_present() { } } +uint32_t VR::get_hmd_width() const { + if (m_2d_screen_mode->value()) { + return g_framework->get_rt_size().x; + } + + return get_runtime()->get_width(); +} + +uint32_t VR::get_hmd_height() const { + if (m_2d_screen_mode->value()) { + return g_framework->get_rt_size().y; + } + + return get_runtime()->get_height(); +} + void VR::on_draw_ui() { ZoneScopedN(__FUNCTION__); @@ -1928,6 +1944,7 @@ void VR::on_draw_ui() { } m_desktop_fix->draw("Desktop Spectator View"); + m_2d_screen_mode->draw("2D Screen Mode"); ImGui::TextWrapped("Render Resolution (per-eye): %d x %d", get_runtime()->get_width(), get_runtime()->get_height()); ImGui::TextWrapped("Total Render Resolution: %d x %d", get_runtime()->get_width() * 2, get_runtime()->get_height()); diff --git a/src/mods/VR.hpp b/src/mods/VR.hpp index 18de6df3..18a4578b 100644 --- a/src/mods/VR.hpp +++ b/src/mods/VR.hpp @@ -225,13 +225,8 @@ class VR : public Mod { return m_overlay_component; } - auto get_hmd_width() const { - return get_runtime()->get_width(); - } - - auto get_hmd_height() const { - return get_runtime()->get_height(); - } + uint32_t get_hmd_width() const; + uint32_t get_hmd_height() const; const auto& get_eyes() const { return get_runtime()->eyes; @@ -448,6 +443,10 @@ class VR : public Mod { return m_decoupled_pitch_data.pre_flattened_rotation; } + bool is_using_2d_screen() const { + return m_2d_screen_mode->value(); + } + private: Vector4f get_position_unsafe(uint32_t index) const; Vector4f get_velocity_unsafe(uint32_t index) const; @@ -657,6 +656,7 @@ class VR : public Mod { const ModToggle::Ptr m_decoupled_pitch{ ModToggle::create(generate_name("DecoupledPitch"), false) }; const ModToggle::Ptr m_decoupled_pitch_ui_adjust{ ModToggle::create(generate_name("DecoupledPitchUIAdjust"), true) }; const ModToggle::Ptr m_load_blueprint_code{ ModToggle::create(generate_name("LoadBlueprintCode"), false) }; + const ModToggle::Ptr m_2d_screen_mode{ ModToggle::create(generate_name("2DScreenMode"), false) }; // Aim method and movement orientation are not the same thing, but they can both have the same options const ModCombo::Ptr m_aim_method{ ModCombo::create(generate_name("AimMethod"), s_aim_method_names, AimMethod::GAME) }; @@ -719,6 +719,7 @@ class VR : public Mod { *m_decoupled_pitch, *m_decoupled_pitch_ui_adjust, *m_load_blueprint_code, + *m_2d_screen_mode, *m_aim_method, *m_movement_orientation, *m_aim_speed, diff --git a/src/mods/vr/D3D12Component.cpp b/src/mods/vr/D3D12Component.cpp index ac5aa0a6..d4591f2a 100644 --- a/src/mods/vr/D3D12Component.cpp +++ b/src/mods/vr/D3D12Component.cpp @@ -8,6 +8,8 @@ #include <../../directxtk12-src/Inc/ResourceUploadBatch.h> #include <../../directxtk12-src/Inc/RenderTargetState.h> +#include "d3d12/DirectXTK.hpp" + #include "D3D12Component.hpp" //#define AFR_DEPTH_TEMP_DISABLED @@ -17,7 +19,7 @@ constexpr auto ENGINE_SRC_COLOR = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE namespace vrmod { vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { - if (m_openvr.left_eye_tex[0].texture == nullptr || m_force_reset || m_last_afr_state != vr->is_using_afr()) { + if (m_force_reset || m_last_afr_state != vr->is_using_afr()) { if (!setup()) { spdlog::error("[D3D12 VR] Could not set up, trying again next frame"); m_force_reset = true; @@ -77,8 +79,7 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { // Set up the game tex context if (m_game_tex.texture.Get() != backbuffer.Get()) { - // We use SRGB for the RTV but not for the SRV because it screws up the colors when drawing the spectator view - if (!m_game_tex.setup(device, backbuffer.Get(), DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_FORMAT_B8G8R8A8_UNORM, L"Game Texture")) { + if (!m_game_tex.setup(device, backbuffer.Get(), DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, L"Game Texture")) { spdlog::error("[VR] Failed to fully setup game texture."); m_game_tex.reset(); } @@ -88,7 +89,7 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { if (m_game_ui_tex.texture.Get() != ui_target->get_native_resource()) { if (!m_game_ui_tex.setup(device, (ID3D12Resource*)ui_target->get_native_resource(), - DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, L"Game UI Texture")) { spdlog::error("[VR] Failed to fully setup game UI texture."); @@ -98,35 +99,111 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { } if (ui_target != nullptr && m_game_ui_tex.srv_heap != nullptr && m_game_ui_tex.rtv_heap != nullptr) { - // Draws the spectator view - auto draw_spec_and_clear_rt = [&](d3d12::CommandContext& commands) { + const float clear_color[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + const auto is_2d_screen = vr->is_using_2d_screen(); + + auto draw_2d_view = [&](d3d12::CommandContext& commands) { draw_spectator_view(commands.cmd_list.Get(), is_right_eye_frame); - const float clear_color[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - commands.clear_rtv(m_game_ui_tex.texture.Get(), m_game_ui_tex.get_rtv(), (float*)&clear_color, ENGINE_SRC_COLOR); + if (is_2d_screen && m_game_tex.texture.Get() != nullptr && m_game_tex.srv_heap != nullptr) { + // Clear previous frame + for (auto& screen : m_2d_screen_tex) { + commands.clear_rtv(screen, clear_color, ENGINE_SRC_COLOR); + } + + // Render left side to left screen tex + d3d12::render_srv_to_rtv( + m_game_batch.get(), + commands.cmd_list.Get(), + m_game_tex, + m_2d_screen_tex[0], + RECT{0, 0, (LONG)((float)m_backbuffer_size[0] / 2.0f), (LONG)m_backbuffer_size[1]}, + ENGINE_SRC_COLOR, + ENGINE_SRC_COLOR + ); + + d3d12::render_srv_to_rtv( + m_game_batch.get(), + commands.cmd_list.Get(), + m_game_ui_tex, + m_2d_screen_tex[0], + ENGINE_SRC_COLOR, + ENGINE_SRC_COLOR + ); + + if (!is_afr) { + // Render right side to right screen tex + d3d12::render_srv_to_rtv( + m_game_batch.get(), + commands.cmd_list.Get(), + m_game_tex, + m_2d_screen_tex[1], + RECT{(LONG)((float)m_backbuffer_size[0] / 2.0f), 0, (LONG)((float)m_backbuffer_size[0]), (LONG)m_backbuffer_size[1]}, + ENGINE_SRC_COLOR, + ENGINE_SRC_COLOR + ); + + d3d12::render_srv_to_rtv( + m_game_batch.get(), + commands.cmd_list.Get(), + m_game_ui_tex, + m_2d_screen_tex[1], + ENGINE_SRC_COLOR, + ENGINE_SRC_COLOR + ); + } + + // Clear the RT so the entire background is black when submitting to the compositor + commands.clear_rtv(m_game_tex, (float*)&clear_color, D3D12_RESOURCE_STATE_RENDER_TARGET); + } + }; + + // Draws the spectator view + auto clear_rt = [&](d3d12::CommandContext& commands) { + commands.clear_rtv(m_game_ui_tex, (float*)&clear_color, ENGINE_SRC_COLOR); }; - if (runtime->is_openvr() && m_ui_tex.texture.Get() != nullptr) { - m_ui_tex.commands.wait(INFINITE); + if (runtime->is_openvr() && m_openvr_ui_tex.texture.Get() != nullptr) { + m_openvr_ui_tex.commands.wait(INFINITE); + + draw_2d_view(m_openvr_ui_tex.commands); if (is_right_eye_frame) { - m_ui_tex.commands.copy((ID3D12Resource*)ui_target->get_native_resource(), m_ui_tex.texture.Get(), ENGINE_SRC_COLOR); + if (is_2d_screen) { + m_openvr_ui_tex.commands.copy(m_2d_screen_tex[0].texture.Get(), m_openvr_ui_tex.texture.Get(), ENGINE_SRC_COLOR); + } else { + m_openvr_ui_tex.commands.copy((ID3D12Resource*)ui_target->get_native_resource(), m_openvr_ui_tex.texture.Get(), ENGINE_SRC_COLOR); + } + } else if (is_2d_screen) { + m_openvr_ui_tex.commands.copy(m_2d_screen_tex[0].texture.Get(), m_openvr_ui_tex.texture.Get(), ENGINE_SRC_COLOR); } - draw_spec_and_clear_rt(m_ui_tex.commands); - m_ui_tex.commands.execute(); + clear_rt(m_openvr_ui_tex.commands); + m_openvr_ui_tex.commands.execute(); } else if (runtime->is_openxr() && runtime->ready() && vr->m_openxr->frame_began) { if (is_right_eye_frame) { - m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::UI, (ID3D12Resource*)ui_target->get_native_resource(), draw_spec_and_clear_rt, ENGINE_SRC_COLOR); + if (is_2d_screen) { + if (is_afr) { + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::UI_RIGHT, m_2d_screen_tex[0].texture.Get(), draw_2d_view, clear_rt, ENGINE_SRC_COLOR); + } else { + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::UI, m_2d_screen_tex[0].texture.Get(), draw_2d_view, std::nullopt, ENGINE_SRC_COLOR); + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::UI_RIGHT, m_2d_screen_tex[1].texture.Get(), std::nullopt, clear_rt, ENGINE_SRC_COLOR); + } + } else { + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::UI, (ID3D12Resource*)ui_target->get_native_resource(), draw_2d_view, clear_rt, ENGINE_SRC_COLOR); + } auto fw_rt = g_framework->get_rendertarget_d3d12(); if (fw_rt && g_framework->is_drawing_anything()) { - m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::FRAMEWORK_UI, g_framework->get_rendertarget_d3d12().Get(), std::nullopt, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::FRAMEWORK_UI, g_framework->get_rendertarget_d3d12().Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); } + } else if (is_2d_screen) { + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::UI, m_2d_screen_tex[0].texture.Get(), draw_2d_view, clear_rt, ENGINE_SRC_COLOR); } else { m_game_ui_tex.commands.wait(INFINITE); - draw_spec_and_clear_rt(m_game_ui_tex.commands); + draw_2d_view(m_game_ui_tex.commands); + clear_rt(m_game_ui_tex.commands); m_game_ui_tex.commands.execute(); } } @@ -174,17 +251,17 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { src_box.front = 0; src_box.back = 1; - m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_LEFT_EYE, backbuffer.Get(), {}, D3D12_RESOURCE_STATE_RENDER_TARGET, &src_box); + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_LEFT_EYE, backbuffer.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, &src_box); if (scene_depth_tex != nullptr) { - m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_DEPTH_LEFT_EYE, scene_depth_tex.Get(), {}, ENGINE_SRC_DEPTH, nullptr); + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_DEPTH_LEFT_EYE, scene_depth_tex.Get(), ENGINE_SRC_DEPTH, nullptr); } } // OpenVR texture // Copy the back buffer to the left eye texture if (runtime->is_openvr()) { - m_openvr.copy_left(backbuffer.Get()); + m_openvr.copy_left(backbuffer.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET); auto openvr = vr->get_runtime(); const auto submit_pose = openvr->get_pose_for_submit(); @@ -223,10 +300,10 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { src_box.front = 0; src_box.back = 1; - m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_LEFT_EYE, backbuffer.Get(), {}, D3D12_RESOURCE_STATE_RENDER_TARGET, &src_box); + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_LEFT_EYE, backbuffer.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, &src_box); if (scene_depth_tex != nullptr) { - m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_DEPTH_LEFT_EYE, scene_depth_tex.Get(), {}, ENGINE_SRC_DEPTH, nullptr); + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_DEPTH_LEFT_EYE, scene_depth_tex.Get(), ENGINE_SRC_DEPTH, nullptr); } } @@ -248,17 +325,17 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { src_box.back = 1; } - m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_RIGHT_EYE, backbuffer.Get(), {}, D3D12_RESOURCE_STATE_RENDER_TARGET, &src_box); + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_RIGHT_EYE, backbuffer.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, &src_box); if (scene_depth_tex != nullptr) { - m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_DEPTH_RIGHT_EYE, scene_depth_tex.Get(), {}, ENGINE_SRC_DEPTH, nullptr); + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_DEPTH_RIGHT_EYE, scene_depth_tex.Get(), ENGINE_SRC_DEPTH, nullptr); } } else { // Copy over the entire double wide instead - m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::DOUBLE_WIDE, backbuffer.Get(), {}, D3D12_RESOURCE_STATE_RENDER_TARGET, nullptr); + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::DOUBLE_WIDE, backbuffer.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, nullptr); if (scene_depth_tex != nullptr) { - m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::DEPTH, scene_depth_tex.Get(), {}, ENGINE_SRC_DEPTH, nullptr); + m_openxr.copy((uint32_t)runtimes::OpenXR::SwapchainIndex::DEPTH, scene_depth_tex.Get(), ENGINE_SRC_DEPTH, nullptr); } } } @@ -287,7 +364,7 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { if (e != vr::VRCompositorError_None) { spdlog::error("[VR] VRCompositor failed to submit left eye: {}", (int)e); - return e; + //return e; // dont return because it will just completely stop us from even getting to the right eye which could be catastrophic } } @@ -342,9 +419,24 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { std::vector quad_layers{}; auto& openxr_overlay = vr->get_overlay_component().get_openxr(); - const auto slate_quad = openxr_overlay.generate_slate_quad(); - if (slate_quad) { - quad_layers.push_back(*slate_quad); + + if (vr->m_2d_screen_mode->value()) { + const auto left_quad = openxr_overlay.generate_slate_quad(runtimes::OpenXR::SwapchainIndex::UI, XrEyeVisibility::XR_EYE_VISIBILITY_LEFT); + const auto right_quad = openxr_overlay.generate_slate_quad(runtimes::OpenXR::SwapchainIndex::UI_RIGHT, XrEyeVisibility::XR_EYE_VISIBILITY_RIGHT); + + if (left_quad) { + quad_layers.push_back(*left_quad); + } + + if (right_quad) { + quad_layers.push_back(*right_quad); + } + } else { + const auto slate_quad = openxr_overlay.generate_slate_quad(); + + if (slate_quad) { + quad_layers.push_back(*slate_quad); + } } const auto framework_quad = openxr_overlay.generate_framework_ui_quad(); @@ -395,7 +487,7 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { return e; } -void D3D12Component::setup_sprite_batch_pso(DXGI_FORMAT output_format) { +std::unique_ptr D3D12Component::setup_sprite_batch_pso(DXGI_FORMAT output_format) { spdlog::info("[D3D12] Setting up sprite batch PSO"); auto& hook = g_framework->get_d3d12_hook(); @@ -410,12 +502,14 @@ void D3D12Component::setup_sprite_batch_pso(DXGI_FORMAT output_format) { DirectX::RenderTargetState output_state{output_format, DXGI_FORMAT_UNKNOWN}; DirectX::SpriteBatchPipelineStateDescription pd{output_state}; - m_backbuffer_batch = std::make_unique(device, upload, pd); + auto batch = std::make_unique(device, upload, pd); auto result = upload.End(command_queue); result.wait(); spdlog::info("[D3D12] Sprite batch PSO setup complete"); + + return batch; } void D3D12Component::draw_spectator_view(ID3D12GraphicsCommandList* command_list, bool is_right_eye_frame) { @@ -423,7 +517,7 @@ void D3D12Component::draw_spectator_view(ID3D12GraphicsCommandList* command_list return; } - if (m_game_ui_tex.srv_heap == nullptr | m_game_ui_tex.srv_heap->Heap() == nullptr) { + if (m_game_ui_tex.srv_heap == nullptr || m_game_ui_tex.srv_heap->Heap() == nullptr) { return; } @@ -655,6 +749,8 @@ void D3D12Component::on_post_present(VR* vr) { } void D3D12Component::on_reset(VR* vr) { + m_force_reset = true; + auto runtime = vr->get_runtime(); for (auto& ctx : m_openvr.left_eye_tex) { @@ -673,10 +769,15 @@ void D3D12Component::on_reset(VR* vr) { backbuffer.reset(); } - m_ui_tex.reset(); + for (auto & screen : m_2d_screen_tex) { + screen.reset(); + } + + m_openvr_ui_tex.reset(); m_game_ui_tex.reset(); m_game_tex.reset(); m_backbuffer_batch.reset(); + m_game_batch.reset(); m_graphics_memory.reset(); if (runtime->is_openxr() && runtime->loaded) { @@ -758,6 +859,9 @@ bool D3D12Component::setup() { auto backbuffer_desc = backbuffer->GetDesc(); + backbuffer_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + backbuffer_desc.Flags &= ~D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + backbuffer_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; backbuffer_desc.Width /= 2; // The texture we get from UE is both eyes combined. we will copy the regions later. spdlog::info("[VR] D3D12 RT width: {}, height: {}, format: {}", backbuffer_desc.Width, backbuffer_desc.Height, backbuffer_desc.Format); @@ -767,28 +871,76 @@ bool D3D12Component::setup() { heap_props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heap_props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - for (auto& ctx : m_openvr.left_eye_tex) { - if (FAILED(device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &backbuffer_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, - IID_PPV_ARGS(&ctx.texture)))) { - spdlog::error("[VR] Failed to create left eye texture."); - return false; - } + if (vr->is_using_2d_screen()) { + auto screen_desc = backbuffer_desc; + screen_desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + screen_desc.Flags &= ~D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; - ctx.texture->SetName(L"OpenVR Left Eye Texture"); - if (!ctx.commands.setup(L"OpenVR Left Eye")) { - return false; + screen_desc.Width = (uint32_t)g_framework->get_d3d12_rt_size().x; + screen_desc.Height = (uint32_t)g_framework->get_d3d12_rt_size().y; + + for (auto& context : m_2d_screen_tex) { + ComPtr screen_tex{}; + if (FAILED(device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &screen_desc, ENGINE_SRC_COLOR, nullptr, + IID_PPV_ARGS(&screen_tex)))) { + spdlog::error("[VR] Failed to create 2D screen texture."); + continue; + } + + screen_tex->SetName(L"2D Screen Texture"); + + if (!context.setup(device, screen_tex.Get(), DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, L"2D Screen")) { + spdlog::error("[VR] Failed to setup 2D screen context."); + continue; + } } } - for (auto& ctx : m_openvr.right_eye_tex) { - if (FAILED(device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &backbuffer_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, - IID_PPV_ARGS(&ctx.texture)))) { - spdlog::error("[VR] Failed to create right eye texture."); + if (vr->get_runtime()->is_openvr()) { + for (auto& ctx : m_openvr.left_eye_tex) { + if (FAILED(device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &backbuffer_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, + IID_PPV_ARGS(&ctx.texture)))) { + spdlog::error("[VR] Failed to create left eye texture."); + return false; + } + + ctx.texture->SetName(L"OpenVR Left Eye Texture"); + if (!ctx.commands.setup(L"OpenVR Left Eye")) { + spdlog::error("[VR] Failed to setup left eye context."); + return false; + } + } + + for (auto& ctx : m_openvr.right_eye_tex) { + if (FAILED(device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &backbuffer_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, + IID_PPV_ARGS(&ctx.texture)))) { + spdlog::error("[VR] Failed to create right eye texture."); + return false; + } + + ctx.texture->SetName(L"OpenVR Right Eye Texture"); + if (!ctx.commands.setup(L"OpenVR Right Eye")) { + spdlog::error("[VR] Failed to setup right eye context."); + return false; + } + } + + // Set up the UI texture. it's the desktop resolution. + auto ui_desc = backbuffer_desc; + ui_desc.Width = (uint32_t)g_framework->get_d3d12_rt_size().x; + ui_desc.Height = (uint32_t)g_framework->get_d3d12_rt_size().y; + + ComPtr ui_tex{}; + if (FAILED(device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &ui_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, + IID_PPV_ARGS(&ui_tex)))) { + spdlog::error("[VR] Failed to create UI texture."); return false; } - ctx.texture->SetName(L"OpenVR Right Eye Texture"); - if (!ctx.commands.setup(L"OpenVR Right Eye")) { + ui_tex->SetName(L"OpenVR UI Texture"); + + if (!m_openvr_ui_tex.setup(device, ui_tex.Get(), DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, L"OpenVR UI")) { + spdlog::error("[VR] Failed to setup OpenVR UI context."); return false; } } @@ -802,23 +954,8 @@ bool D3D12Component::setup() { m_backbuffer_size[0] = backbuffer_desc.Width * 2; m_backbuffer_size[1] = backbuffer_desc.Height; - // Set up the UI texture. it's the desktop resolution. - backbuffer_desc.Width = (uint32_t)g_framework->get_d3d12_rt_size().x; - backbuffer_desc.Height = (uint32_t)g_framework->get_d3d12_rt_size().y; - - if (FAILED(device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &backbuffer_desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr, - IID_PPV_ARGS(&m_ui_tex.texture)))) { - spdlog::error("[VR] Failed to create UI texture."); - return false; - } - - m_ui_tex.texture->SetName(L"VR UI Texture"); - - if (!m_ui_tex.commands.setup(L"VR UI")) { - return false; - } - - setup_sprite_batch_pso(real_backbuffer_desc.Format); + m_backbuffer_batch = setup_sprite_batch_pso(real_backbuffer_desc.Format); + m_game_batch = setup_sprite_batch_pso(backbuffer_desc.Format); spdlog::info("[VR] d3d12 textures have been setup"); m_force_reset = false; @@ -1075,6 +1212,10 @@ std::optional D3D12Component::OpenXR::create_swapchains() { return err; } + if (auto err = create_swapchain((uint32_t)runtimes::OpenXR::SwapchainIndex::UI_RIGHT, desktop_rt_swapchain_create_info, desktop_rt_desc)) { + return err; + } + if (auto err = create_swapchain((uint32_t)runtimes::OpenXR::SwapchainIndex::FRAMEWORK_UI, desktop_rt_swapchain_create_info, desktop_rt_desc)) { return err; } @@ -1222,9 +1363,21 @@ void D3D12Component::OpenXR::destroy_swapchains() { vr->m_openxr->swapchains.clear(); } -void D3D12Component::OpenXR::copy(uint32_t swapchain_idx, ID3D12Resource* resource, std::optional> additional_commands, D3D12_RESOURCE_STATES src_state, D3D12_BOX* src_box) { +void D3D12Component::OpenXR::copy( + uint32_t swapchain_idx, + ID3D12Resource* resource, + std::optional> pre_commands, + std::optional> additional_commands, + D3D12_RESOURCE_STATES src_state, + D3D12_BOX* src_box) +{ std::scoped_lock _{this->mtx}; + if (resource == nullptr) { + spdlog::error("[VR] OpenXR: Trying to copy from a null resource."); + return; + } + auto vr = VR::get(); if (vr->m_openxr->frame_state.shouldRender != XR_TRUE) { @@ -1289,6 +1442,10 @@ void D3D12Component::OpenXR::copy(uint32_t swapchain_idx, ID3D12Resource* resour auto& texture_ctx = ctx.texture_contexts[texture_index]; texture_ctx->commands.wait(INFINITE); + if (pre_commands) { + (*pre_commands)(texture_ctx->commands); + } + if (src_box == nullptr) { const auto is_depth = swapchain_idx == (uint32_t)runtimes::OpenXR::SwapchainIndex::DEPTH || swapchain_idx == (uint32_t)runtimes::OpenXR::SwapchainIndex::AFR_DEPTH_LEFT_EYE || diff --git a/src/mods/vr/D3D12Component.hpp b/src/mods/vr/D3D12Component.hpp index a2c600d6..996bb96c 100644 --- a/src/mods/vr/D3D12Component.hpp +++ b/src/mods/vr/D3D12Component.hpp @@ -40,11 +40,11 @@ class D3D12Component { auto is_initialized() const { return m_openvr.left_eye_tex[0].texture != nullptr; } auto& openxr() { return m_openxr; } - auto& get_ui_tex() { return m_ui_tex; } + auto& get_ui_tex() { return m_openvr_ui_tex; } private: bool setup(); - void setup_sprite_batch_pso(DXGI_FORMAT output_format); + std::unique_ptr setup_sprite_batch_pso(DXGI_FORMAT output_format); void draw_spectator_view(ID3D12GraphicsCommandList* command_list, bool is_right_eye_frame); void clear_backbuffer(); @@ -53,13 +53,15 @@ class D3D12Component { ComPtr m_prev_backbuffer{}; std::array m_generic_commands{}; - d3d12::TextureContext m_ui_tex{}; + d3d12::TextureContext m_openvr_ui_tex{}; d3d12::TextureContext m_game_ui_tex{}; d3d12::TextureContext m_game_tex{}; + std::array m_2d_screen_tex{}; std::array m_backbuffer_textures{}; std::unique_ptr m_graphics_memory{}; std::unique_ptr m_backbuffer_batch{}; + std::unique_ptr m_game_batch{}; // Mimicking what OpenXR does. struct OpenVR { @@ -149,9 +151,16 @@ class D3D12Component { void initialize(XrSessionCreateInfo& session_info); std::optional create_swapchains(); void destroy_swapchains(); - void copy(uint32_t swapchain_idx, ID3D12Resource* src, + void copy(uint32_t swapchain_idx, ID3D12Resource* src, + std::optional> pre_commands = std::nullopt, std::optional> additional_commands = std::nullopt, D3D12_RESOURCE_STATES src_state = D3D12_RESOURCE_STATE_PRESENT, D3D12_BOX* src_box = nullptr); + + void copy(uint32_t swapchain_idx, ID3D12Resource* src, + D3D12_RESOURCE_STATES src_state = D3D12_RESOURCE_STATE_PRESENT, D3D12_BOX* src_box = nullptr) + { + this->copy(swapchain_idx, src, std::nullopt, std::nullopt, src_state, src_box); + } void wait_for_all_copies() { std::scoped_lock _{this->mtx}; @@ -182,7 +191,7 @@ class D3D12Component { uint32_t m_backbuffer_size[2]{}; uint32_t m_last_rendered_frame{0}; - bool m_force_reset{false}; + bool m_force_reset{true}; bool m_last_afr_state{false}; bool m_submitted_left_eye{false}; }; diff --git a/src/mods/vr/FFakeStereoRenderingHook.cpp b/src/mods/vr/FFakeStereoRenderingHook.cpp index 3cd57610..5bc5447f 100644 --- a/src/mods/vr/FFakeStereoRenderingHook.cpp +++ b/src/mods/vr/FFakeStereoRenderingHook.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "Framework.hpp" #include "Mods.hpp" @@ -3945,6 +3946,8 @@ __forceinline void FFakeStereoRenderingHook::calculate_stereo_view_offset( // if we have stereo emulation mode enabled // it is only for debugging purposes if (!vr->is_stereo_emulation_enabled()) { + const auto is_2d_screen = vr->is_using_2d_screen(); + const auto rotation_offset = vr->get_rotation_offset(); const auto current_hmd_rotation = glm::normalize(rotation_offset * glm::quat{vr->get_rotation(0)}); const auto current_eye_rotation_offset = glm::normalize(glm::quat{vr->get_eye_transform(true_index)}); @@ -3954,27 +3957,35 @@ __forceinline void FFakeStereoRenderingHook::calculate_stereo_view_offset( const auto pos = glm::vec3{rotation_offset * ((vr->get_position(0) - vr->get_standing_origin()))}; - const auto offset1 = quat_converter * (vqi_norm * (pos * world_scale)); - const auto offset2 = quat_converter * (glm::normalize(new_rotation) * (eye_offset * world_scale)); + const auto head_offset = quat_converter * (vqi_norm * (pos * world_scale)); + const auto eye_separation = quat_converter * (glm::normalize(new_rotation) * (eye_offset * world_scale)); if (!has_double_precision) { - *view_location -= offset1; - *view_location -= offset2; + if (!is_2d_screen) { + *view_location -= head_offset; + } + + *view_location -= eye_separation; } else { - *view_d -= offset1; - *view_d -= offset2; + if (!is_2d_screen) { + *view_location -= head_offset; + } + + *view_d -= eye_separation; } - const auto euler = glm::degrees(utility::math::euler_angles_from_steamvr(new_rotation)); + if (!is_2d_screen) { + const auto euler = glm::degrees(utility::math::euler_angles_from_steamvr(new_rotation)); - if (!has_double_precision) { - view_rotation->pitch = euler.x; - view_rotation->yaw = euler.y; - view_rotation->roll = euler.z; - } else { - rot_d->pitch = euler.x; - rot_d->yaw = euler.y; - rot_d->roll = euler.z; + if (!has_double_precision) { + view_rotation->pitch = euler.x; + view_rotation->yaw = euler.y; + view_rotation->roll = euler.z; + } else { + rot_d->pitch = euler.x; + rot_d->yaw = euler.y; + rot_d->roll = euler.z; + } } } @@ -4061,6 +4072,41 @@ __forceinline Matrix4x4f* FFakeStereoRenderingHook::calculate_stereo_projection_ // Can happen if we hooked this differently. if (g_hook->m_calculate_stereo_projection_matrix_hook) { g_hook->m_calculate_stereo_projection_matrix_hook.call(stereo, out, view_index); + } else { + if (g_hook->m_has_double_precision) { + (*out)[3][2] = sdk::globals::get_near_clipping_plane(); + } else { + (*(Matrix4x4d*)out)[3][2] = (double)sdk::globals::get_near_clipping_plane(); + } + } + + if (VR::get()->is_using_2d_screen()) { + float fov = 90.0f; // todo, get from FMinimalViewInfo + + const float width = VR::get()->get_hmd_width(); + const float height = VR::get()->get_hmd_height(); + const float half_fov = glm::radians(fov) / 2.0f; + const float xs = 1.0f / glm::tan(half_fov); + const float ys = width / glm::tan(half_fov) / height; + const float near_z = sdk::globals::get_near_clipping_plane(); + + if (g_hook->m_has_double_precision) { + (*(Matrix4x4d*)out) = Matrix4x4d { + xs, 0.0, 0.0, 0.0, + 0.0, ys, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0, + 0.0, 0.0, near_z, 0.0 + }; + } else { + *out = Matrix4x4f { + xs, 0.0f, 0.0f, 0.0f, + 0.0f, ys, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + 0.0f, 0.0f, near_z, 0.0f + }; + } + + return out; } // SPDLOG_INFO("NearZ: {}", old_znear); diff --git a/src/mods/vr/OverlayComponent.cpp b/src/mods/vr/OverlayComponent.cpp index c275fab9..6e3f0093 100644 --- a/src/mods/vr/OverlayComponent.cpp +++ b/src/mods/vr/OverlayComponent.cpp @@ -606,24 +606,33 @@ void OverlayComponent::update_overlay_openvr() { } } -std::optional> OverlayComponent::OpenXR::generate_slate_quad() { +std::optional> OverlayComponent::OpenXR::generate_slate_quad( + runtimes::OpenXR::SwapchainIndex swapchain, + XrEyeVisibility eye) +{ auto& vr = VR::get(); if (!vr->is_gui_enabled()) { return std::nullopt; } - auto& layer = this->m_slate_layer; + if (!vr->m_openxr->swapchains.contains((uint32_t)swapchain)) { + return std::nullopt; + } + + const auto is_left_eye = eye == XR_EYE_VISIBILITY_BOTH || eye == XR_EYE_VISIBILITY_LEFT; + + auto& layer = is_left_eye ? this->m_slate_layer : this->m_slate_layer_right; layer.type = XR_TYPE_COMPOSITION_LAYER_QUAD; - const auto& ui_swapchain = vr->m_openxr->swapchains[(uint32_t)runtimes::OpenXR::SwapchainIndex::UI]; + const auto& ui_swapchain = vr->m_openxr->swapchains[(uint32_t)swapchain]; layer.subImage.swapchain = ui_swapchain.handle; + layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; layer.subImage.imageRect.offset.x = 0; layer.subImage.imageRect.offset.y = 0; layer.subImage.imageRect.extent.width = ui_swapchain.width; layer.subImage.imageRect.extent.height = ui_swapchain.height; - layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; - layer.eyeVisibility = XrEyeVisibility::XR_EYE_VISIBILITY_BOTH; + layer.eyeVisibility = eye; auto glm_matrix = glm::identity(); diff --git a/src/mods/vr/OverlayComponent.hpp b/src/mods/vr/OverlayComponent.hpp index 5d1026eb..8f2c81f3 100644 --- a/src/mods/vr/OverlayComponent.hpp +++ b/src/mods/vr/OverlayComponent.hpp @@ -101,11 +101,15 @@ class OverlayComponent : public ModComponent { } - std::optional> generate_slate_quad(); + std::optional> generate_slate_quad( + runtimes::OpenXR::SwapchainIndex swapchain = runtimes::OpenXR::SwapchainIndex::UI, + XrEyeVisibility eye = XR_EYE_VISIBILITY_BOTH + ); std::optional> generate_framework_ui_quad(); private: XrCompositionLayerQuad m_slate_layer{}; + XrCompositionLayerQuad m_slate_layer_right{}; XrCompositionLayerQuad m_framework_ui_layer{}; OverlayComponent* m_parent{ nullptr }; diff --git a/src/mods/vr/d3d12/CommandContext.cpp b/src/mods/vr/d3d12/CommandContext.cpp index db53a1a9..3d8e2923 100644 --- a/src/mods/vr/d3d12/CommandContext.cpp +++ b/src/mods/vr/d3d12/CommandContext.cpp @@ -3,6 +3,7 @@ #include "Framework.hpp" +#include "TextureContext.hpp" #include "CommandContext.hpp" namespace d3d12 { @@ -79,6 +80,11 @@ void CommandContext::wait(uint32_t ms) { void CommandContext::copy(ID3D12Resource* src, ID3D12Resource* dst, D3D12_RESOURCE_STATES src_state, D3D12_RESOURCE_STATES dst_state) { std::scoped_lock _{this->mtx}; + if (src == nullptr || dst == nullptr) { + spdlog::error("[VR] nullptr passed to copy"); + return; + } + // Switch src into copy source. D3D12_RESOURCE_BARRIER src_barrier{}; @@ -123,6 +129,11 @@ void CommandContext::copy(ID3D12Resource* src, ID3D12Resource* dst, D3D12_RESOUR void CommandContext::copy_region(ID3D12Resource* src, ID3D12Resource* dst, D3D12_BOX* src_box, D3D12_RESOURCE_STATES src_state, D3D12_RESOURCE_STATES dst_state) { std::scoped_lock _{this->mtx}; + if (src == nullptr || dst == nullptr) { + spdlog::error("[VR] nullptr passed to copy_region"); + return; + } + // Switch src into copy source. D3D12_RESOURCE_BARRIER src_barrier{}; @@ -177,6 +188,11 @@ void CommandContext::copy_region(ID3D12Resource* src, ID3D12Resource* dst, D3D12 void CommandContext::clear_rtv(ID3D12Resource* dst, D3D12_CPU_DESCRIPTOR_HANDLE rtv, const float* color, D3D12_RESOURCE_STATES dst_state) { std::scoped_lock _{this->mtx}; + if (dst == nullptr) { + spdlog::error("[VR] nullptr passed to clear_rtv"); + return; + } + // Switch dst into copy destination. D3D12_RESOURCE_BARRIER dst_barrier{}; dst_barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; @@ -207,6 +223,14 @@ void CommandContext::clear_rtv(ID3D12Resource* dst, D3D12_CPU_DESCRIPTOR_HANDLE this->has_commands = true; } +void CommandContext::clear_rtv(d3d12::TextureContext& tex, const float* color, D3D12_RESOURCE_STATES dst_state) { + if (tex.texture == nullptr || tex.rtv_heap == nullptr) { + return; + } + + this->clear_rtv(tex.texture.Get(), tex.get_rtv(), color, dst_state); +} + void CommandContext::execute() { std::scoped_lock _{this->mtx}; @@ -217,7 +241,8 @@ void CommandContext::execute() { } auto command_queue = g_framework->get_d3d12_hook()->get_command_queue(); - command_queue->ExecuteCommandLists(1, (ID3D12CommandList* const*)this->cmd_list.GetAddressOf()); + ID3D12CommandList* const cmd_lists[] = {this->cmd_list.Get()}; + command_queue->ExecuteCommandLists(1, cmd_lists); command_queue->Signal(this->fence.Get(), ++this->fence_value); this->fence->SetEventOnCompletion(this->fence_value, this->fence_event); this->waiting_for_fence = true; diff --git a/src/mods/vr/d3d12/CommandContext.hpp b/src/mods/vr/d3d12/CommandContext.hpp index ee44cc0b..6387587c 100644 --- a/src/mods/vr/d3d12/CommandContext.hpp +++ b/src/mods/vr/d3d12/CommandContext.hpp @@ -6,6 +6,8 @@ #include "ComPtr.hpp" namespace d3d12 { +struct TextureContext; + struct CommandContext { CommandContext() = default; virtual ~CommandContext() { this->reset(); } @@ -21,6 +23,7 @@ struct CommandContext { D3D12_RESOURCE_STATES dst_state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); void clear_rtv(ID3D12Resource* dst, D3D12_CPU_DESCRIPTOR_HANDLE rtv, const float* color, D3D12_RESOURCE_STATES dst_state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + void clear_rtv(TextureContext& tex, const float* color, D3D12_RESOURCE_STATES dst_state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); void execute(); ComPtr cmd_allocator{}; diff --git a/src/mods/vr/d3d12/DirectXTK.cpp b/src/mods/vr/d3d12/DirectXTK.cpp index edb0d2dc..92e2e685 100644 --- a/src/mods/vr/d3d12/DirectXTK.cpp +++ b/src/mods/vr/d3d12/DirectXTK.cpp @@ -9,6 +9,14 @@ void render_srv_to_rtv( D3D12_RESOURCE_STATES src_state, D3D12_RESOURCE_STATES dst_state) { + if (src.texture == nullptr || dst.texture == nullptr) { + return; + } + + if (src.srv_heap == nullptr || dst.rtv_heap == nullptr) { + return; + } + const auto dst_desc = dst.texture->GetDesc(); const auto src_desc = src.texture->GetDesc(); @@ -67,5 +75,90 @@ void render_srv_to_rtv( barrier.Transition.StateAfter = dst_state; command_list->ResourceBarrier(1, &barrier); } -} +} + +void render_srv_to_rtv( + DirectX::DX12::SpriteBatch* batch, + ID3D12GraphicsCommandList* command_list, + const d3d12::TextureContext& src, + const d3d12::TextureContext& dst, + std::optional src_rect, + D3D12_RESOURCE_STATES src_state, + D3D12_RESOURCE_STATES dst_state) +{ + if (src.texture == nullptr || dst.texture == nullptr) { + return; + } + + if (src.srv_heap == nullptr || dst.rtv_heap == nullptr) { + return; + } + + const auto dst_desc = dst.texture->GetDesc(); + const auto src_desc = src.texture->GetDesc(); + + D3D12_VIEWPORT viewport{}; + viewport.Width = (float)dst_desc.Width; + viewport.Height = (float)dst_desc.Height; + viewport.MinDepth = D3D12_MIN_DEPTH; + viewport.MaxDepth = D3D12_MAX_DEPTH; + + batch->SetViewport(viewport); + + D3D12_RECT scissor_rect{}; + scissor_rect.left = 0; + scissor_rect.top = 0; + scissor_rect.right = (LONG)dst_desc.Width; + scissor_rect.bottom = (LONG)dst_desc.Height; + + // Transition dst to D3D12_RESOURCE_STATE_RENDER_TARGET + D3D12_RESOURCE_BARRIER barrier{}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition.pResource = dst.texture.Get(); + + if (dst_state != D3D12_RESOURCE_STATE_RENDER_TARGET) { + barrier.Transition.StateBefore = src_state; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + command_list->ResourceBarrier(1, &barrier); + } + + // Set RTV to backbuffer + D3D12_CPU_DESCRIPTOR_HANDLE rtv_heaps[] = { dst.get_rtv() }; + command_list->OMSetRenderTargets(1, rtv_heaps, FALSE, nullptr); + + // Setup viewport and scissor rects + command_list->RSSetViewports(1, &viewport); + command_list->RSSetScissorRects(1, &scissor_rect); + + batch->Begin(command_list, DirectX::DX12::SpriteSortMode::SpriteSortMode_Immediate); + + RECT dest_rect{ 0, 0, (LONG)dst_desc.Width, (LONG)dst_desc.Height }; + + // Set descriptor heaps + ID3D12DescriptorHeap* game_heaps[] = { src.srv_heap->Heap() }; + command_list->SetDescriptorHeaps(1, game_heaps); + + if (src_rect) { + batch->Draw(src.get_srv_gpu(), + DirectX::XMUINT2{ (uint32_t)src_desc.Width, (uint32_t)src_desc.Height }, + dest_rect, + &*src_rect, + DirectX::Colors::White); + } else { + batch->Draw(src.get_srv_gpu(), + DirectX::XMUINT2{ (uint32_t)src_desc.Width, (uint32_t)src_desc.Height }, + dest_rect, + DirectX::Colors::White); + } + + batch->End(); + + // Transition dst to dst_state + if (dst_state != D3D12_RESOURCE_STATE_RENDER_TARGET) { + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier.Transition.StateAfter = dst_state; + command_list->ResourceBarrier(1, &barrier); + } +} } \ No newline at end of file diff --git a/src/mods/vr/d3d12/DirectXTK.hpp b/src/mods/vr/d3d12/DirectXTK.hpp index af9ab2b5..601df045 100644 --- a/src/mods/vr/d3d12/DirectXTK.hpp +++ b/src/mods/vr/d3d12/DirectXTK.hpp @@ -12,4 +12,13 @@ void render_srv_to_rtv( const d3d12::TextureContext& dst, D3D12_RESOURCE_STATES src_state, D3D12_RESOURCE_STATES dst_state); + +void render_srv_to_rtv( + DirectX::DX12::SpriteBatch* sprite_batch, + ID3D12GraphicsCommandList* command_list, + const d3d12::TextureContext& src, + const d3d12::TextureContext& dst, + std::optional src_rect, + D3D12_RESOURCE_STATES src_state, + D3D12_RESOURCE_STATES dst_state); } \ No newline at end of file diff --git a/src/mods/vr/runtimes/OpenXR.hpp b/src/mods/vr/runtimes/OpenXR.hpp index 217def17..796e992a 100644 --- a/src/mods/vr/runtimes/OpenXR.hpp +++ b/src/mods/vr/runtimes/OpenXR.hpp @@ -311,6 +311,7 @@ struct OpenXR final : public VRRuntime { EXTRA_START, UI = EXTRA_START, + UI_RIGHT, // For 2D view with stereoscopic FRAMEWORK_UI, EXTRA_END,