diff --git a/shared/sdk/Renderer.cpp b/shared/sdk/Renderer.cpp index 5a653f6f0..3a0bf4e2a 100644 --- a/shared/sdk/Renderer.cpp +++ b/shared/sdk/Renderer.cpp @@ -753,6 +753,10 @@ void RenderContext::clear_rtv(sdk::renderer::RenderTargetView* rtv, float color[ - 0xD InterleaveNormalDepthHalfWithoutGBuffer */ void RenderContext::copy_texture(Texture* dest, Texture* src, Fence& fence) { + copy_texture(dest, src, &fence); +} + +void RenderContext::copy_texture(Texture* dest, Texture* src, Fence* fence) { // Okay it was actually this simple in older games but it isn't anymore in DD2+ // There's some extra garbage going on that I don't want to deal with right now // so will just call the function directly @@ -787,7 +791,7 @@ void RenderContext::copy_texture(Texture* dest, Texture* src, Fence& fence) { // so in universal builds we dispatch at runtime but both branches must compile. #if defined(REFRAMEWORK_UNIVERSAL) || TDB_VER < 82 auto copy_legacy = [&]() { - using CopyTexFn = void (*)(RenderContext*, Texture*, Texture*, Fence&); + using CopyTexFn = void (*)(RenderContext*, Texture*, Texture*, Fence*); static auto func = []() -> CopyTexFn { spdlog::info("Searching for RenderContext::copy_texture"); @@ -855,7 +859,7 @@ void RenderContext::copy_texture(Texture* dest, Texture* src, Fence& fence) { #if defined(REFRAMEWORK_UNIVERSAL) || TDB_VER >= 82 auto copy_modern = [&]() { - using CopyTexFn = void (*)(RenderContext*, Texture*, int32_t, Texture*, int32_t, Fence&); + using CopyTexFn = void (*)(RenderContext*, Texture*, int32_t, Texture*, int32_t, Fence*); static auto func = []() -> CopyTexFn { spdlog::info("Searching for RenderContext::copy_texture (>= TDB82)"); @@ -2016,4 +2020,4 @@ ID3D12Resource* layer::Scene::get_depth_stencil_d3d12() { return internal_resource->get_native_resource(); } } -} \ No newline at end of file +} diff --git a/shared/sdk/Renderer.hpp b/shared/sdk/Renderer.hpp index e06ed1056..b29da9cfe 100644 --- a/shared/sdk/Renderer.hpp +++ b/shared/sdk/Renderer.hpp @@ -573,7 +573,7 @@ static_assert(sizeof(command::Fence) == 0x30); struct CopyBase : public Base { sdk::renderer::RenderResource* src{}; sdk::renderer::RenderResource* dst{}; - ::sdk::renderer::Fence fence{}; + ::sdk::renderer::Fence* fence{}; }; struct CopyTexture : public CopyBase { @@ -581,8 +581,8 @@ struct CopyTexture : public CopyBase { int32_t dst_subresource{-1}; }; -static_assert(sizeof(CopyBase) == 0x30); -static_assert(sizeof(CopyTexture) == 0x38); +static_assert(sizeof(CopyBase) == 0x28); +static_assert(sizeof(CopyTexture) == 0x30); } class RenderContext { @@ -603,9 +603,9 @@ class RenderContext { } void copy_texture(Texture* dest, Texture* src, Fence& fence); + void copy_texture(Texture* dest, Texture* src, Fence* fence); void copy_texture(Texture* dest, Texture* src) { - Fence fence{}; - copy_texture(dest, src, fence); + copy_texture(dest, src, (Fence*)nullptr); } public: diff --git a/src/mods/vr/D3D12Component.cpp b/src/mods/vr/D3D12Component.cpp index 4197d02a8..16d7acdf7 100644 --- a/src/mods/vr/D3D12Component.cpp +++ b/src/mods/vr/D3D12Component.cpp @@ -74,6 +74,10 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { runtime->fix_frame(); } + if (runtime->is_openvr()) { + m_openvr.prune_retired_textures(); + } + const auto frame_count = vr->m_render_frame_count; const auto is_multipass = vr->is_using_multipass(); @@ -94,6 +98,45 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { } } + if (is_multipass && TemporalUpscaler::get()->ready() && + vr->m_multipass.eye_textures[0].Get() != nullptr && vr->m_multipass.eye_textures[1].Get() != nullptr) + { + auto& ctx0 = vr->m_multipass.eye_contexts[0]; + auto& ctx1 = vr->m_multipass.eye_contexts[1]; + + if (ctx0.texture.Get() != vr->m_multipass.eye_textures[0].Get()) { + ctx0.reset(); + const auto desc = vr->m_multipass.eye_textures[0]->GetDesc(); + ctx0.setup(device, vr->m_multipass.eye_textures[0].Get(), desc.Format, desc.Format); + } + + if (ctx1.texture.Get() != vr->m_multipass.eye_textures[1].Get()) { + ctx1.reset(); + const auto desc = vr->m_multipass.eye_textures[1]->GetDesc(); + ctx1.setup(device, vr->m_multipass.eye_textures[1].Get(), desc.Format, desc.Format); + } + } + + auto render_or_copy_to_texture = [&](d3d12::CommandContext& ctx, const d3d12::TextureContext& src, d3d12::TextureContext& dst, D3D12_RESOURCE_STATES src_state, D3D12_RESOURCE_STATES dst_state) { + const auto src_desc = src.texture->GetDesc(); + const auto dst_desc = dst.texture->GetDesc(); + const auto can_copy = + src_desc.Width == dst_desc.Width && + src_desc.Height == dst_desc.Height && + src_desc.DepthOrArraySize == dst_desc.DepthOrArraySize && + src_desc.MipLevels == dst_desc.MipLevels && + src_desc.Format == dst_desc.Format; + + if (can_copy) { + ctx.copy(src.texture.Get(), dst.texture.Get(), src_state, dst_state); + return; + } + + const float clear_color[4]{0.0f, 0.0f, 0.0f, 0.0f}; + ctx.clear_rtv(dst, clear_color, dst_state); + render_srv_to_rtv(ctx.cmd_list.Get(), src, dst, src_state, dst_state); + }; + // If m_frame_count is even, we're rendering the left eye. if (frame_count % 2 == vr->m_left_eye_interval && !is_multipass) { // OpenXR texture @@ -151,34 +194,25 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { ctx1.setup(device, vr->m_multipass.eye_textures[1].Get(), desc.Format, desc.Format); } - if (m_backbuffer_is_8bit) { - if (!TemporalUpscaler::get()->ready()) { + auto copy0fn = [&](d3d12::CommandContext& ctx, d3d12::TextureContext& dst, D3D12_RESOURCE_STATES src_state, D3D12_RESOURCE_STATES dst_state) { + render_or_copy_to_texture(ctx, ctx0, dst, src_state, dst_state); + }; + + auto copy1fn = [&](d3d12::CommandContext& ctx, d3d12::TextureContext& dst, D3D12_RESOURCE_STATES src_state, D3D12_RESOURCE_STATES dst_state) { + render_or_copy_to_texture(ctx, ctx1, dst, src_state, dst_state); + }; + + if (!TemporalUpscaler::get()->ready()) { + if (m_backbuffer_is_8bit) { m_openxr.copy(0, vr->m_multipass.eye_textures[0].Get(), nullptr, D3D12_RESOURCE_STATE_COPY_DEST); m_openxr.copy(1, vr->m_multipass.eye_textures[1].Get(), nullptr, D3D12_RESOURCE_STATE_COPY_DEST); } else { - m_openxr.copy(0, vr->m_multipass.eye_textures[0].Get(), nullptr, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - m_openxr.copy(1, vr->m_multipass.eye_textures[1].Get(), nullptr, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - } - } else { - auto copy0fn = [&](d3d12::CommandContext& ctx, d3d12::TextureContext& dst, D3D12_RESOURCE_STATES src_state, D3D12_RESOURCE_STATES dst_state) { - const float clear_color[4]{0.0f, 0.0f, 0.0f, 0.0f}; - ctx.clear_rtv(dst, clear_color, dst_state); - render_srv_to_rtv(ctx.cmd_list.Get(), vr->m_multipass.eye_contexts[0], dst, src_state, dst_state); - }; - - auto copy1fn = [&](d3d12::CommandContext& ctx, d3d12::TextureContext& dst, D3D12_RESOURCE_STATES src_state, D3D12_RESOURCE_STATES dst_state) { - const float clear_color[4]{0.0f, 0.0f, 0.0f, 0.0f}; - ctx.clear_rtv(dst, clear_color, dst_state); - render_srv_to_rtv(ctx.cmd_list.Get(), vr->m_multipass.eye_contexts[1], dst, src_state, dst_state); - }; - - if (!TemporalUpscaler::get()->ready()) { m_openxr.copy(0, ctx0.texture.Get(), nullptr, D3D12_RESOURCE_STATE_COPY_DEST, copy0fn); m_openxr.copy(1, ctx1.texture.Get(), nullptr, D3D12_RESOURCE_STATE_COPY_DEST, copy1fn); - } else { - m_openxr.copy(0, ctx0.texture.Get(), nullptr, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, copy0fn); - m_openxr.copy(1, ctx1.texture.Get(), nullptr, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, copy1fn); } + } else { + m_openxr.copy(0, ctx0.texture.Get(), nullptr, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, copy0fn); + m_openxr.copy(1, ctx1.texture.Get(), nullptr, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, copy1fn); } } else { // just copy the backbuffer to both eyes as a fallback @@ -202,8 +236,14 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { m_openvr.copy_left(vr->m_multipass.eye_textures[0].Get(), D3D12_RESOURCE_STATE_COPY_DEST); m_openvr.copy_right(vr->m_multipass.eye_textures[1].Get(), D3D12_RESOURCE_STATE_COPY_DEST); } else { - m_openvr.copy_left(vr->m_multipass.eye_textures[0].Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS); - m_openvr.copy_right(vr->m_multipass.eye_textures[1].Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + auto& left = m_openvr.acquire_left(); + auto& right = m_openvr.acquire_right(); + + render_or_copy_to_texture(left.commands, vr->m_multipass.eye_contexts[0], left, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + render_or_copy_to_texture(right.commands, vr->m_multipass.eye_contexts[1], right, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + + left.commands.execute(); + right.commands.execute(); } } else { // just copy the backbuffer to both eyes as a fallback @@ -252,6 +292,7 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { } ++m_openvr.texture_counter; + ++m_openvr.submission_serial; } } @@ -304,7 +345,28 @@ vr::EVRCompositorError D3D12Component::on_frame(VR* vr) { // Allows the desktop window to be recorded. if (vr->m_desktop_fix->value()) { - if (runtime->ready() && m_prev_backbuffer != backbuffer && m_prev_backbuffer != nullptr) { + bool mirrored_current_frame = false; + + if (is_multipass && TemporalUpscaler::get()->ready() && vr->m_multipass.eye_textures[0].Get() != nullptr) { + const auto src_desc = vr->m_multipass.eye_textures[0]->GetDesc(); + const auto dst_desc = backbuffer->GetDesc(); + const auto can_copy = + src_desc.Width == dst_desc.Width && + src_desc.Height == dst_desc.Height && + src_desc.DepthOrArraySize == dst_desc.DepthOrArraySize && + src_desc.MipLevels == dst_desc.MipLevels && + src_desc.Format == dst_desc.Format; + + if (can_copy) { + auto& copier = m_generic_copiers[frame_count % m_generic_copiers.size()]; + copier.wait(INFINITE); + copier.copy(vr->m_multipass.eye_textures[0].Get(), backbuffer.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_PRESENT); + copier.execute(); + mirrored_current_frame = true; + } + } + + if (!mirrored_current_frame && runtime->ready() && m_prev_backbuffer != backbuffer && m_prev_backbuffer != nullptr) { auto& copier = m_generic_copiers[frame_count % m_generic_copiers.size()]; copier.wait(INFINITE); copier.copy(m_prev_backbuffer.Get(), backbuffer.Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_PRESENT); @@ -326,6 +388,10 @@ void D3D12Component::on_reset(VR* vr) { auto runtime = vr->get_runtime(); + if (runtime->is_openvr()) { + m_openvr.retire_textures(); + } + for (auto& ctx : m_openvr.left_eye_tex) { ctx.reset(); } @@ -541,8 +607,8 @@ void D3D12Component::setup() { setup_sprite_batch_pso(rt_desc.Format); - m_backbuffer_size[0] = real_backbuffer_desc.Width; - m_backbuffer_size[1] = real_backbuffer_desc.Height; + m_backbuffer_size[0] = backbuffer_desc.Width; + m_backbuffer_size[1] = backbuffer_desc.Height; spdlog::info("[VR] d3d12 textures have been setup"); m_force_reset = false; diff --git a/src/mods/vr/D3D12Component.hpp b/src/mods/vr/D3D12Component.hpp index 77c1c263a..494aa9320 100644 --- a/src/mods/vr/D3D12Component.hpp +++ b/src/mods/vr/D3D12Component.hpp @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include #include #include @@ -62,6 +64,11 @@ class D3D12Component { // Mimicking what OpenXR does. struct OpenVR { + struct RetiredTextureSet { + uint64_t release_after_submission{}; + std::vector> textures{}; + }; + d3d12::TextureContext& get_left() { auto& ctx = this->left_eye_tex[this->texture_counter % left_eye_tex.size()]; @@ -100,9 +107,44 @@ class D3D12Component { ctx.commands.execute(); } + void retire_textures() { + RetiredTextureSet retired{}; + retired.release_after_submission = this->submission_serial + 6; + + for (auto& ctx : this->left_eye_tex) { + if (ctx.texture != nullptr) { + retired.textures.emplace_back(ctx.texture); + } + } + + for (auto& ctx : this->right_eye_tex) { + if (ctx.texture != nullptr) { + retired.textures.emplace_back(ctx.texture); + } + } + + if (retired.textures.empty()) { + return; + } + + this->retired_textures.emplace_back(std::move(retired)); + + while (this->retired_textures.size() > 2) { + this->retired_textures.pop_front(); + } + } + + void prune_retired_textures() { + while (!this->retired_textures.empty() && this->submission_serial >= this->retired_textures.front().release_after_submission) { + this->retired_textures.pop_front(); + } + } + std::array left_eye_tex{}; std::array right_eye_tex{}; + std::deque retired_textures{}; uint32_t texture_counter{0}; + uint64_t submission_serial{}; DXGI_FORMAT last_format{}; } m_openvr; diff --git a/src/mods/vr/d3d12/DirectXTK.cpp b/src/mods/vr/d3d12/DirectXTK.cpp index edb0d2dc1..c019933fb 100644 --- a/src/mods/vr/d3d12/DirectXTK.cpp +++ b/src/mods/vr/d3d12/DirectXTK.cpp @@ -26,16 +26,29 @@ void render_srv_to_rtv( 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(); + D3D12_RESOURCE_BARRIER barriers[2]{}; + uint32_t barrier_count = 0; - if (dst_state != D3D12_RESOURCE_STATE_RENDER_TARGET) { + if (src_state != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) { + auto& barrier = barriers[barrier_count++]; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition.pResource = src.texture.Get(); barrier.Transition.StateBefore = src_state; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + } + + if (dst_state != D3D12_RESOURCE_STATE_RENDER_TARGET) { + auto& barrier = barriers[barrier_count++]; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition.pResource = dst.texture.Get(); + barrier.Transition.StateBefore = dst_state; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - command_list->ResourceBarrier(1, &barrier); + } + + if (barrier_count > 0) { + command_list->ResourceBarrier(barrier_count, barriers); } // Set RTV to backbuffer @@ -61,11 +74,28 @@ void render_srv_to_rtv( batch->End(); - // Transition dst to dst_state + barrier_count = 0; + if (dst_state != D3D12_RESOURCE_STATE_RENDER_TARGET) { + auto& barrier = barriers[barrier_count++]; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition.pResource = dst.texture.Get(); barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; barrier.Transition.StateAfter = dst_state; - command_list->ResourceBarrier(1, &barrier); + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + } + + if (src_state != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) { + auto& barrier = barriers[barrier_count++]; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Transition.pResource = src.texture.Get(); + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + barrier.Transition.StateAfter = src_state; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + } + + if (barrier_count > 0) { + command_list->ResourceBarrier(barrier_count, barriers); } } -} \ No newline at end of file +}