diff --git a/include/reshade_api.hpp b/include/reshade_api.hpp index d4b0600777..8d37059209 100644 --- a/include/reshade_api.hpp +++ b/include/reshade_api.hpp @@ -178,6 +178,20 @@ namespace reshade { namespace api }; }; + /// + /// Describes a rectangular region in a resource. + /// + struct region + { + uint32_t left; + uint32_t top; + uint32_t right; + uint32_t bottom; + + constexpr uint32_t width() const { return right - left; } + constexpr uint32_t height() const { return bottom - top; } + }; + /// /// An opaque handle to a resource object (buffer, texture, ...). /// Resources created by the application are only guaranteed to be valid during event callbacks. diff --git a/source/addon/generic_depth.cpp b/source/addon/generic_depth.cpp index c29d738a5f..54333c7a1a 100644 --- a/source/addon/generic_depth.cpp +++ b/source/addon/generic_depth.cpp @@ -10,6 +10,7 @@ #include "dll_log.hpp" #include "dll_config.hpp" #include "reshade.hpp" +#include "openvr/reshade_vr.hpp" #include "addon_manager.hpp" #include "dxgi/format_utils.hpp" #include @@ -37,6 +38,7 @@ struct depth_stencil_info draw_stats current_stats; // Stats since last clear operation std::vector clears; bool copied_during_frame = false; + std::chrono::high_resolution_clock::time_point last_drawcall; }; struct state_tracking @@ -91,69 +93,44 @@ struct state_tracking } }; -struct state_tracking_context +struct depth_stencil_selection { - static constexpr uint8_t GUID[16] = { 0x7c, 0x63, 0x63, 0xc7, 0xf9, 0x4e, 0x43, 0x7a, 0x91, 0x60, 0x14, 0x17, 0x82, 0xc4, 0x4a, 0x98 }; - - // Enable or disable the creation of backup copies at clear operations on the selected depth-stencil - bool preserve_depth_buffers = false; - // Enable or disable the aspect ratio check from 'check_aspect_ratio' in the detection heuristic - bool use_aspect_ratio_heuristics = true; - - // Set to zero for automatic detection, otherwise will use the clear operation at the specific index within a frame - size_t force_clear_index = 0; - // Stats of the previous frame for the selected depth-stencil draw_stats previous_stats = {}; // A resource used as target for a backup copy for the selected depth-stencil - resource_handle backup_texture = { 0 }; + resource_handle backup= { 0 }; // The depth-stencil that is currently selected as being the main depth target // Any clear operations on it are subject for special handling (backup copy or replacement) - resource_handle selected_depth_stencil = { 0 }; + resource_handle resource = { 0 }; // Resource used to override automatic depth-stencil selection - resource_handle override_depth_stencil = { 0 }; + resource_handle override= { 0 }; // The current shader resource view bound to shaders // This can be created from either the original depth-stencil of the application (if it supports shader access), or from the backup resource, or from one of the replacement resources - resource_view_handle selected_shader_resource = { 0 }; + resource_view_handle view = { 0 }; -#if RESHADE_GUI - // List of all encountered depth-stencils of the last frame - std::vector> current_depth_stencil_list; - std::unordered_map display_count_per_depth_stencil; -#endif - - // Checks whether the aspect ratio of the two sets of dimensions is similar or not - bool check_aspect_ratio(float width_to_check, float height_to_check, uint32_t width, uint32_t height) const - { - if (width_to_check == 0.0f || height_to_check == 0.0f) - return true; - - const float w = static_cast(width); - const float w_ratio = w / width_to_check; - const float h = static_cast(height); - const float h_ratio = h / height_to_check; - const float aspect_ratio = (w / h) - (static_cast(width_to_check) / height_to_check); + // Enable or disable the creation of backup copies at clear operations on the selected depth-stencil + bool preserve_depth_buffers = false; - return std::fabs(aspect_ratio) <= 0.1f && w_ratio <= 1.85f && h_ratio <= 1.85f && w_ratio >= 0.5f && h_ratio >= 0.5f; - } + // Set to zero for automatic detection, otherwise will use the clear operation at the specific index within a frame + size_t force_clear_index = 0; // Update the backup texture to match the requested dimensions void update_backup_texture(device *device, resource_desc desc) { - if (backup_texture != 0) + if (backup != 0) { - const resource_desc existing_desc = device->get_resource_desc(backup_texture); + const resource_desc existing_desc = device->get_resource_desc(backup); if (desc.width == existing_desc.width && desc.height == existing_desc.height && desc.format == existing_desc.format) return; // Texture already matches dimensions, so can re-use device->wait_idle(); // Texture may still be in use on device, so wait for all operations to finish before destroying it - device->destroy_resource(backup_texture); - backup_texture = { 0 }; + device->destroy_resource(backup); + backup= { 0 }; } desc.usage = resource_usage::shader_resource | resource_usage::copy_dest; @@ -163,14 +140,57 @@ struct state_tracking_context if (device->get_api() >= render_api::d3d10 && device->get_api() <= render_api::d3d12) desc.format = static_cast(make_dxgi_format_typeless(static_cast(desc.format))); - if (!device->create_resource(resource_type::texture_2d, desc, memory_usage::gpu_only, resource_usage::copy_dest, &backup_texture)) + if (!device->create_resource(resource_type::texture_2d, desc, memory_usage::gpu_only, resource_usage::copy_dest, &backup)) LOG(ERROR) << "Failed to create backup depth-stencil texture!"; } }; -static void clear_depth_impl(command_list *cmd_list, state_tracking &state, const state_tracking_context &device_state, resource_handle depth_stencil, bool fullscreen_draw_call) +struct state_tracking_context +{ + static constexpr uint8_t GUID[16] = { 0x7c, 0x63, 0x63, 0xc7, 0xf9, 0x4e, 0x43, 0x7a, 0x91, 0x60, 0x14, 0x17, 0x82, 0xc4, 0x4a, 0x98 }; + + // Enable or disable the aspect ratio check from 'check_aspect_ratio' in the detection heuristic + bool use_aspect_ratio_heuristics = true; + + depth_stencil_selection selected_depth_stencil; + depth_stencil_selection vr_depth_stencil[2]; + + // By default, the left eye is expected to be the first eye rendered. Enable this to swap eyes if necessary. + bool vr_swap_eyes = false; + +#if RESHADE_GUI + // List of all encountered depth-stencils of the last frame + std::vector> current_depth_stencil_list; + std::unordered_map display_count_per_depth_stencil; +#endif + + // Checks whether the aspect ratio of the two sets of dimensions is similar or not + bool check_aspect_ratio(float width_to_check, float height_to_check, uint32_t width, uint32_t height) const + { + if (width_to_check == 0.0f || height_to_check == 0.0f) + return true; + + const float w = static_cast(width); + const float w_ratio = w / width_to_check; + const float h = static_cast(height); + const float h_ratio = h / height_to_check; + const float aspect_ratio = (w / h) - (static_cast(width_to_check) / height_to_check); + + return std::fabs(aspect_ratio) <= 0.1f && w_ratio <= 1.85f && h_ratio <= 1.85f && w_ratio >= 0.5f && h_ratio >= 0.5f; + } +}; + +struct match_info +{ + resource_desc description; + resource_handle resource; + depth_stencil_info snapshot; +}; + + +static void clear_depth_impl(command_list *cmd_list, state_tracking &state, const state_tracking_context &device_state, const depth_stencil_selection &selected_depth_stencil, resource_handle depth_stencil, bool fullscreen_draw_call) { - if (depth_stencil == 0 || device_state.backup_texture == 0 || depth_stencil != device_state.selected_depth_stencil) + if (depth_stencil == 0 || selected_depth_stencil.backup == 0 || depth_stencil != selected_depth_stencil.resource) return; depth_stencil_info &counters = state.counters_per_used_depth_stencil[depth_stencil.handle]; @@ -178,7 +198,7 @@ static void clear_depth_impl(command_list *cmd_list, state_tracking &state, cons // Update stats with data from previous frame if (!fullscreen_draw_call && counters.current_stats.drawcalls == 0 && state.first_empty_stats) { - counters.current_stats = device_state.previous_stats; + counters.current_stats = selected_depth_stencil.previous_stats; state.first_empty_stats = false; } @@ -197,11 +217,11 @@ static void clear_depth_impl(command_list *cmd_list, state_tracking &state, cons counters.clears.push_back({ counters.current_stats, fullscreen_draw_call }); // Make a backup copy of the depth texture before it is cleared - if (device_state.force_clear_index == 0 ? + if (selected_depth_stencil.force_clear_index == 0 ? // If clear index override is set to zero, always copy any suitable buffers fullscreen_draw_call || counters.current_stats.vertices > state.best_copy_stats.vertices : // This is not really correct, since clears may accumulate over multiple command lists, but it's unlikely that the same depth-stencil is used in more than one - counters.clears.size() == device_state.force_clear_index) + counters.clears.size() == selected_depth_stencil.force_clear_index) { // Since clears from fullscreen draw calls are selected based on their order (last one wins), their stats are ignored for the regular clear heuristic if (!fullscreen_draw_call) @@ -209,7 +229,7 @@ static void clear_depth_impl(command_list *cmd_list, state_tracking &state, cons // A resource has to be in this state for a clear operation, so can assume it here cmd_list->transition_state(depth_stencil, resource_usage::depth_stencil_write, resource_usage::copy_source); - cmd_list->copy_resource(depth_stencil, device_state.backup_texture); + cmd_list->copy_resource(depth_stencil, selected_depth_stencil.backup); cmd_list->transition_state(depth_stencil, resource_usage::copy_source, resource_usage::depth_stencil_write); counters.copied_during_frame = true; @@ -225,21 +245,39 @@ static void on_init_device(device *device) const reshade::ini_file &config = reshade::global_config(); config.get("DEPTH", "DisableINTZ", s_disable_intz); - config.get("DEPTH", "DepthCopyBeforeClears", device_state.preserve_depth_buffers); - config.get("DEPTH", "DepthCopyAtClearIndex", device_state.force_clear_index); + config.get("DEPTH", "DepthCopyBeforeClears", device_state.selected_depth_stencil.preserve_depth_buffers); + config.get("DEPTH", "DepthCopyAtClearIndex", device_state.selected_depth_stencil.force_clear_index); config.get("DEPTH", "UseAspectRatioHeuristics", device_state.use_aspect_ratio_heuristics); + config.get("DEPTH", "VRFirstEyeCopyAtClearIndex", device_state.vr_depth_stencil[0].force_clear_index); + config.get("DEPTH", "VRSwapEyes", device_state.vr_swap_eyes); + + if (device_state.selected_depth_stencil.force_clear_index == std::numeric_limits::max()) + device_state.selected_depth_stencil.force_clear_index = 0; + if (device_state.vr_depth_stencil[0].force_clear_index == std::numeric_limits::max()) + device_state.vr_depth_stencil[0].force_clear_index = 0; - if (device_state.force_clear_index == std::numeric_limits::max()) - device_state.force_clear_index = 0; + for (int i = 0; i < 2; ++i) + { + device_state.vr_depth_stencil[i].preserve_depth_buffers = device_state.selected_depth_stencil.preserve_depth_buffers; + if (device_state.vr_depth_stencil[i].force_clear_index == 0) + device_state.vr_depth_stencil[i].force_clear_index = device_state.selected_depth_stencil.force_clear_index; + } } static void on_destroy_device(device *device) { state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); - if (device_state.backup_texture != 0) - device->destroy_resource(device_state.backup_texture); - if (device_state.selected_shader_resource != 0) - device->destroy_resource_view(device_state.selected_shader_resource); + if (device_state.selected_depth_stencil.backup != 0) + device->destroy_resource(device_state.selected_depth_stencil.backup); + if (device_state.selected_depth_stencil.view != 0) + device->destroy_resource_view(device_state.selected_depth_stencil.view); + for (int i = 0; i < 2; ++i) + { + if (device_state.vr_depth_stencil[i].backup != 0) + device->destroy_resource(device_state.vr_depth_stencil[i].backup); + if (device_state.vr_depth_stencil[i].view != 0) + device->destroy_resource_view(device_state.vr_depth_stencil[i].view); + } device->destroy_data(state_tracking_context::GUID); } @@ -329,6 +367,7 @@ static void on_draw(command_list *cmd_list, uint32_t vertices, uint32_t instance counters.current_stats.vertices += vertices * instances; counters.current_stats.drawcalls += 1; std::memcpy(counters.current_stats.last_viewport, state.current_viewport, 6 * sizeof(float)); + counters.last_drawcall = std::chrono::high_resolution_clock::now(); } static void on_draw_indexed(command_list *cmd_list, uint32_t indices, uint32_t instances, uint32_t, int32_t, uint32_t) { @@ -366,7 +405,10 @@ static void on_set_depth_stencil(command_list *cmd_list, uint32_t, const resourc // Make a backup of the depth texture before it is used differently, since in D3D12 or Vulkan the underlying memory may be aliased to a different resource, so cannot just access it at the end of the frame if (depth_stencil != state.current_depth_stencil && state.current_depth_stencil != 0 && (device->get_api() == render_api::d3d12 || device->get_api() == render_api::vulkan)) { - clear_depth_impl(cmd_list, state, device->get_data(state_tracking_context::GUID), state.current_depth_stencil, true); + state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); + clear_depth_impl(cmd_list, state, device_state, device_state.selected_depth_stencil, state.current_depth_stencil, true); + clear_depth_impl(cmd_list, state, device_state, device_state.vr_depth_stencil[0], state.current_depth_stencil, true); + clear_depth_impl(cmd_list, state, device_state, device_state.vr_depth_stencil[1], state.current_depth_stencil, true); } state.current_depth_stencil = depth_stencil; @@ -376,7 +418,8 @@ static void on_clear_depth_stencil(command_list *cmd_list, resource_view_handle device *const device = cmd_list->get_device(); const state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); - if ((clear_flags & 0x1) == 0 || !device_state.preserve_depth_buffers) + // FIXME: may need adjustment for VR? + if ((clear_flags & 0x1) == 0 || (!device_state.selected_depth_stencil.preserve_depth_buffers && !device_state.vr_depth_stencil[0].preserve_depth_buffers)) { // Ignore clears that do not affect the depth buffer (stencil clears) return; @@ -385,7 +428,10 @@ static void on_clear_depth_stencil(command_list *cmd_list, resource_view_handle resource_handle depth_stencil = { 0 }; device->get_resource_from_view(dsv, &depth_stencil); - clear_depth_impl(cmd_list, cmd_list->get_data(state_tracking::GUID), device_state, depth_stencil, false); + state_tracking state = cmd_list->get_data(state_tracking::GUID); + clear_depth_impl(cmd_list, state, device_state, device_state.selected_depth_stencil, depth_stencil, false); + clear_depth_impl(cmd_list, state, device_state, device_state.vr_depth_stencil[0], depth_stencil, false); + clear_depth_impl(cmd_list, state, device_state, device_state.vr_depth_stencil[1], depth_stencil, false); } static void on_reset(command_list *cmd_list) @@ -400,24 +446,17 @@ static void on_execute(api_object *queue_or_cmd_list, command_list *cmd_list) target_state.merge(source_state); } -static void on_present(command_queue *, effect_runtime *runtime) +static std::vector find_matching_depth_textures(effect_runtime *runtime) { - device *const device = runtime->get_device(); - command_queue *const queue = runtime->get_command_queue(); - state_tracking &queue_state = queue->get_data(state_tracking::GUID); - state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); - -#if RESHADE_GUI - device_state.current_depth_stencil_list.clear(); - device_state.current_depth_stencil_list.reserve(queue_state.counters_per_used_depth_stencil.size()); -#endif + const device *const device = runtime->get_device(); + const command_queue *const queue = runtime->get_command_queue(); + const state_tracking &queue_state = queue->get_data(state_tracking::GUID); + const state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); uint32_t frame_width, frame_height; runtime->get_frame_width_and_height(&frame_width, &frame_height); - resource_desc best_desc = {}; - resource_handle best_match = { 0 }; - depth_stencil_info best_snapshot; + std::vector matches; for (const auto &[depth_stencil_handle, snapshot] : queue_state.counters_per_used_depth_stencil) { @@ -425,11 +464,6 @@ static void on_present(command_queue *, effect_runtime *runtime) if (!device->check_resource_handle_valid(resource)) continue; // Skip resources that were destroyed by the application -#if RESHADE_GUI - // Save to current list of depth-stencils on the device, so that it can be displayed in the GUI - device_state.current_depth_stencil_list.emplace_back(resource, snapshot); -#endif - if (snapshot.total_stats.drawcalls == 0) continue; // Skip unused @@ -440,44 +474,58 @@ static void on_present(command_queue *, effect_runtime *runtime) if (device_state.use_aspect_ratio_heuristics && !device_state.check_aspect_ratio(static_cast(desc.width), static_cast(desc.height), frame_width, frame_height)) continue; // Not a good fit - if (!queue_state.has_indirect_drawcalls ? + matches.push_back({ desc, resource, snapshot }); + } + std::sort(matches.begin(), matches.end(), [&](const match_info &a, const match_info &b) { + if (!queue_state.has_indirect_drawcalls) + { // Choose snapshot with the most vertices, since that is likely to contain the main scene - snapshot.total_stats.vertices > best_snapshot.total_stats.vertices : - // Or check draw calls, since vertices may not be accurate if application is using indirect draw calls - snapshot.total_stats.drawcalls > best_snapshot.total_stats.drawcalls) + return a.snapshot.total_stats.vertices > b.snapshot.total_stats.vertices; + } + else { - best_desc = desc; - best_match = resource; - best_snapshot = snapshot; + // Or check draw calls, since vertices may not be accurate if application is using indirect draw calls + return a.snapshot.total_stats.drawcalls > b.snapshot.total_stats.drawcalls; } - } + }); + + // have at least one empty result in the list + matches.push_back({}); + return matches; +} - if (device_state.override_depth_stencil != 0 && - device->check_resource_handle_valid(device_state.override_depth_stencil)) +static void update_selected_depth_stencil(effect_runtime *runtime, depth_stencil_selection &selected_depth_stencil, match_info match) { + device *const device = runtime->get_device(); + command_queue *const queue = runtime->get_command_queue(); + state_tracking &queue_state = queue->get_data(state_tracking::GUID); + state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); + + if (selected_depth_stencil.override != 0 && + device->check_resource_handle_valid(selected_depth_stencil.override)) { - best_desc = device->get_resource_desc(device_state.override_depth_stencil); - best_match = device_state.override_depth_stencil; - best_snapshot = queue_state.counters_per_used_depth_stencil[best_match.handle]; + match.description = device->get_resource_desc(selected_depth_stencil.override); + match.resource = selected_depth_stencil.override; + match.snapshot = queue_state.counters_per_used_depth_stencil[match.resource.handle]; } - if (best_match != 0) + if (match.resource != 0) { - if (best_match != device_state.selected_depth_stencil) + if (match.resource != selected_depth_stencil.resource) { // Destroy previous resource view, since the underlying resource has changed - if (device_state.selected_shader_resource != 0) + if (selected_depth_stencil.view != 0) { device->wait_idle(); // Ensure resource view is no longer in-use before destroying it - device->destroy_resource_view(device_state.selected_shader_resource); + device->destroy_resource_view(selected_depth_stencil.view); } - device_state.selected_depth_stencil = best_match; - device_state.selected_shader_resource = { 0 }; + selected_depth_stencil.resource = match.resource; + selected_depth_stencil.view = { 0 }; // Create two-dimensional resource view to the first level and layer of the depth-stencil resource resource_view_desc srv_desc = {}; srv_desc.type = resource_view_type::texture_2d; - srv_desc.format = best_desc.format; + srv_desc.format = match.description.format; srv_desc.levels = 1; srv_desc.layers = 1; @@ -486,75 +534,138 @@ static void on_present(command_queue *, effect_runtime *runtime) // Need to create backup texture only if doing backup copies or original resource does not support shader access (which is necessary for binding it to effects) // Also always create a backup texture in D3D12 or Vulkan to circument problems in case application makes use of resource aliasing - if (device_state.preserve_depth_buffers || (best_desc.usage & resource_usage::shader_resource) == 0 || (device->get_api() == render_api::d3d12 || device->get_api() == render_api::vulkan)) + if (selected_depth_stencil.preserve_depth_buffers || (match.description.usage & resource_usage::shader_resource) == 0 || (device->get_api() == render_api::d3d12 || device->get_api() == render_api::vulkan)) { - device_state.update_backup_texture(device, best_desc); + selected_depth_stencil.update_backup_texture(device, match.description); - if (device_state.backup_texture != 0) + if (selected_depth_stencil.backup != 0) { if (device->get_api() == render_api::d3d9) srv_desc.format = 114; // Same format as backup texture, as set in 'update_backup_texture' - device->create_resource_view(device_state.backup_texture, resource_usage::shader_resource, srv_desc, &device_state.selected_shader_resource); + device->create_resource_view(selected_depth_stencil.backup, resource_usage::shader_resource, srv_desc, &selected_depth_stencil.view); } } else { - device->create_resource_view(best_match, resource_usage::shader_resource, srv_desc, &device_state.selected_shader_resource); + device->create_resource_view(match.resource, resource_usage::shader_resource, srv_desc, &selected_depth_stencil.view); - if (device_state.backup_texture != 0) + if (selected_depth_stencil.backup != 0) { - device->destroy_resource(device_state.backup_texture); - device_state.backup_texture = { 0 }; + device->destroy_resource(selected_depth_stencil.backup); + selected_depth_stencil.backup = { 0 }; } } - - runtime->update_texture_bindings("DEPTH", device_state.selected_shader_resource); - - const bool bufready_depth_value = true; - runtime->update_uniform_variables("bufready_depth", &bufready_depth_value, 1); } - if (device_state.preserve_depth_buffers) + if (selected_depth_stencil.preserve_depth_buffers) { - device_state.previous_stats = best_snapshot.current_stats; + selected_depth_stencil.previous_stats = match.snapshot.current_stats; } else { // Copy to backup texture unless already copied during the current frame - if (device_state.backup_texture != 0 && !best_snapshot.copied_during_frame && (best_desc.usage & resource_usage::copy_source) != 0) + if (selected_depth_stencil.backup != 0 && !match.snapshot.copied_during_frame && (match.description.usage & resource_usage::copy_source) != 0) { command_list *const cmd_list = queue->get_immediate_command_list(); - cmd_list->transition_state(best_match, resource_usage::depth_stencil | resource_usage::shader_resource, resource_usage::copy_source); - cmd_list->copy_resource(best_match, device_state.backup_texture); - cmd_list->transition_state(best_match, resource_usage::copy_source, resource_usage::depth_stencil | resource_usage::shader_resource); + cmd_list->transition_state(match.resource, resource_usage::depth_stencil | resource_usage::shader_resource, resource_usage::copy_source); + cmd_list->copy_resource(match.resource, selected_depth_stencil.backup); + cmd_list->transition_state(match.resource, resource_usage::copy_source, resource_usage::depth_stencil | resource_usage::shader_resource); } } - best_snapshot.copied_during_frame = false; + match.snapshot.copied_during_frame = false; } else { // Unset any existing depth-stencil selected in previous frames - if (device_state.selected_depth_stencil != 0) + if (selected_depth_stencil.resource != 0) { - if (device_state.selected_shader_resource != 0) + if (selected_depth_stencil.view != 0) { device->wait_idle(); // Ensure resource view is no longer in-use before destroying it - device->destroy_resource_view(device_state.selected_shader_resource); + device->destroy_resource_view(selected_depth_stencil.view); } - device_state.selected_depth_stencil = { 0 }; - device_state.selected_shader_resource = { 0 }; + selected_depth_stencil = { 0 }; + selected_depth_stencil.view = { 0 }; + } + } + + runtime->update_texture_bindings("DEPTH", selected_depth_stencil.view); + + const bool bufready_depth_value = match.resource != 0; + runtime->update_uniform_variables("bufready_depth", &bufready_depth_value, 1); +} + +static void on_present_vr(effect_runtime *runtime, reshade::vr::submit_info submit_info) +{ + device *const device = runtime->get_device(); + state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); - runtime->update_texture_bindings("DEPTH", device_state.selected_shader_resource); + std::vector matches = find_matching_depth_textures(runtime); + matches.resize(2); - const bool bufready_depth_value = false; - runtime->update_uniform_variables("bufready_depth", &bufready_depth_value, 1); + if (matches[0].resource != 0) + { + if (matches[0].description.width >= submit_info.region.width() * 2 - 10) { + // case 1: single large depth texture containing both eyes + matches[1] = matches[0]; + } + else if (matches[1].resource == 0 || matches[1].description.width != matches[0].description.width || matches[1].description.height != matches[0].description.height) { + // case 2: single depth texture used for both eyes consecutively + // in this case, we'll have to make a copy during clear for the first eye + matches[1] = matches[0]; + device_state.vr_depth_stencil[0].preserve_depth_buffers = true; } + else if (matches[1].resource != 0 && matches[1].snapshot.last_drawcall < matches[0].snapshot.last_drawcall) + { + // case 3: two separate textures, but we need to bring them in the right order depending + // on the last drawcall timestamp + std::swap(matches[0], matches[1]); + } + } + + uint8_t eye = device_state.vr_swap_eyes ? 1 - submit_info.eye : submit_info.eye; + depth_stencil_selection &selected_depth_stencil = device_state.vr_depth_stencil[eye]; + selected_depth_stencil.override = submit_info.depth; + update_selected_depth_stencil(runtime, selected_depth_stencil, matches[eye]); +} + +static void on_present(command_queue *, effect_runtime *runtime) +{ + reshade::vr::submit_info *submit_info = nullptr; + if (runtime->get_data(reshade::vr::submit_info::GUID, reinterpret_cast(&submit_info))) + { + on_present_vr(runtime, *submit_info); + return; } + device *const device = runtime->get_device(); + command_queue *const queue = runtime->get_command_queue(); + state_tracking &queue_state = queue->get_data(state_tracking::GUID); + state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); + + +#if RESHADE_GUI + device_state.current_depth_stencil_list.clear(); + device_state.current_depth_stencil_list.reserve(queue_state.counters_per_used_depth_stencil.size()); + + for (const auto &[depth_stencil_handle, snapshot] : queue_state.counters_per_used_depth_stencil) + { + resource_handle const resource = { depth_stencil_handle }; + if (!device->check_resource_handle_valid(resource)) + continue; // Skip resources that were destroyed by the application + + // Save to current list of depth-stencils on the device, so that it can be displayed in the GUI + device_state.current_depth_stencil_list.emplace_back(resource, snapshot); + } +#endif + + match_info match = find_matching_depth_textures(runtime).front(); + update_selected_depth_stencil(runtime, device_state.selected_depth_stencil, match); + queue_state.reset_on_present(); } @@ -564,22 +675,19 @@ static void on_init_effect_runtime(effect_runtime *runtime) const state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); // Need to set texture binding again after a runtime was reset - runtime->update_texture_bindings("DEPTH", device_state.selected_shader_resource); + runtime->update_texture_bindings("DEPTH", device_state.selected_depth_stencil.view); - const bool bufready_depth_value = device_state.selected_shader_resource != 0; + const bool bufready_depth_value = device_state.selected_depth_stencil.view != 0; runtime->update_uniform_variables("bufready_depth", &bufready_depth_value, 1); } -static void on_before_render_effects(effect_runtime *runtime, command_list *cmd_list) +static void transition_state_before_effects(device *const device, command_list *cmd_list, const depth_stencil_selection &selected_depth_stencil) { - device *const device = runtime->get_device(); - const state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); - - if (device_state.selected_shader_resource != 0) + if (selected_depth_stencil.view != 0) { resource_handle resource = { 0 }; - device->get_resource_from_view(device_state.selected_shader_resource, &resource); + device->get_resource_from_view(selected_depth_stencil.view, &resource); - if (resource == device_state.backup_texture) + if (resource == selected_depth_stencil.backup) { cmd_list->transition_state(resource, resource_usage::copy_dest, resource_usage::shader_resource); } @@ -589,17 +697,23 @@ static void on_before_render_effects(effect_runtime *runtime, command_list *cmd_ } } } -static void on_after_render_effects(effect_runtime *runtime, command_list *cmd_list) +static void on_before_render_effects(effect_runtime *runtime, command_list *cmd_list) { device *const device = runtime->get_device(); const state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); - if (device_state.selected_shader_resource != 0) + transition_state_before_effects(device, cmd_list, device_state.selected_depth_stencil); + transition_state_before_effects(device, cmd_list, device_state.vr_depth_stencil[0]); + transition_state_before_effects(device, cmd_list, device_state.vr_depth_stencil[1]); +} +static void transition_state_after_effects(device *const device, command_list *cmd_list, const depth_stencil_selection &selected_depth_stencil) +{ + if (selected_depth_stencil.view != 0) { resource_handle resource = { 0 }; - device->get_resource_from_view(device_state.selected_shader_resource, &resource); + device->get_resource_from_view(selected_depth_stencil.view, &resource); - if (resource == device_state.backup_texture) + if (resource == selected_depth_stencil.backup) { cmd_list->transition_state(resource, resource_usage::shader_resource, resource_usage::copy_dest); } @@ -609,6 +723,15 @@ static void on_after_render_effects(effect_runtime *runtime, command_list *cmd_l } } } +static void on_after_render_effects(effect_runtime *runtime, command_list *cmd_list) +{ + device *const device = runtime->get_device(); + const state_tracking_context &device_state = device->get_data(state_tracking_context::GUID); + + transition_state_after_effects(device, cmd_list, device_state.selected_depth_stencil); + transition_state_after_effects(device, cmd_list, device_state.vr_depth_stencil[0]); + transition_state_after_effects(device, cmd_list, device_state.vr_depth_stencil[1]); +} #if RESHADE_GUI static void draw_debug_menu(effect_runtime *runtime, void *) @@ -621,7 +744,7 @@ static void draw_debug_menu(effect_runtime *runtime, void *) modified |= ImGui::Checkbox("Disable replacement with INTZ format (requires restart)", &s_disable_intz); modified |= ImGui::Checkbox("Use aspect ratio heuristics", &device_state.use_aspect_ratio_heuristics); - modified |= ImGui::Checkbox("Copy depth buffer before clear operations", &device_state.preserve_depth_buffers); + modified |= ImGui::Checkbox("Copy depth buffer before clear operations", &device_state.selected_depth_stencil.preserve_depth_buffers); ImGui::Spacing(); ImGui::Separator(); @@ -673,7 +796,7 @@ static void draw_debug_menu(effect_runtime *runtime, void *) device_state.display_count_per_depth_stencil[item.resource.handle] = item.display_count; char label[512] = ""; - sprintf_s(label, "%s0x%016llx", (item.resource == device_state.selected_depth_stencil ? "> " : " "), item.resource.handle); + sprintf_s(label, "%s0x%016llx", (item.resource == device_state.selected_depth_stencil.resource ? "> " : " "), item.resource.handle); if (item.desc.samples > 1) // Disable widget for MSAA textures { @@ -681,10 +804,10 @@ static void draw_debug_menu(effect_runtime *runtime, void *) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); } - if (bool value = (item.resource == device_state.override_depth_stencil); + if (bool value = (item.resource == device_state.selected_depth_stencil.override); ImGui::Checkbox(label, &value)) { - device_state.override_depth_stencil = value ? item.resource : resource_handle { 0 }; + device_state.selected_depth_stencil.override = value ? item.resource : resource_handle { 0 }; modified = true; } @@ -692,16 +815,16 @@ static void draw_debug_menu(effect_runtime *runtime, void *) ImGui::Text("| %4ux%-4u | %5u draw calls ==> %8u vertices |%s", item.desc.width, item.desc.height, item.snapshot.total_stats.drawcalls, item.snapshot.total_stats.vertices, (item.desc.samples > 1 ? " MSAA" : "")); - if (device_state.preserve_depth_buffers && item.resource == device_state.selected_depth_stencil) + if (device_state.selected_depth_stencil.preserve_depth_buffers && item.resource == device_state.selected_depth_stencil.resource) { for (size_t clear_index = 1; clear_index <= item.snapshot.clears.size(); ++clear_index) { - sprintf_s(label, "%s CLEAR %2zu", (clear_index == device_state.force_clear_index ? "> " : " "), clear_index); + sprintf_s(label, "%s CLEAR %2zu", (clear_index == device_state.selected_depth_stencil.force_clear_index ? "> " : " "), clear_index); - if (bool value = (device_state.force_clear_index == clear_index); + if (bool value = (device_state.selected_depth_stencil.force_clear_index == clear_index); ImGui::Checkbox(label, &value)) { - device_state.force_clear_index = value ? clear_index : 0; + device_state.selected_depth_stencil.force_clear_index = value ? clear_index : 0; modified = true; } @@ -722,22 +845,24 @@ static void draw_debug_menu(effect_runtime *runtime, void *) if (modified) { // Reset selected depth-stencil to force re-creation of resources next frame (like the backup texture) - if (device_state.selected_shader_resource != 0) + if (device_state.selected_depth_stencil.view != 0) { device->wait_idle(); // Ensure resource view is no longer in-use before destroying it - device->destroy_resource_view(device_state.selected_shader_resource); + device->destroy_resource_view(device_state.selected_depth_stencil.view); } device_state.selected_depth_stencil = { 0 }; - device_state.selected_shader_resource = { 0 }; + device_state.selected_depth_stencil.view = { 0 }; on_init_effect_runtime(runtime); reshade::ini_file &config = reshade::global_config(); config.set("DEPTH", "DisableINTZ", s_disable_intz); - config.set("DEPTH", "DepthCopyBeforeClears", device_state.preserve_depth_buffers); - config.set("DEPTH", "DepthCopyAtClearIndex", device_state.force_clear_index); + config.set("DEPTH", "DepthCopyBeforeClears", device_state.selected_depth_stencil.preserve_depth_buffers); + config.set("DEPTH", "DepthCopyAtClearIndex", device_state.selected_depth_stencil.force_clear_index); config.set("DEPTH", "UseAspectRatioHeuristics", device_state.use_aspect_ratio_heuristics); + config.set("DEPTH", "VRFirstEyeCopyAtClearIndex", device_state.vr_depth_stencil[0].force_clear_index); + config.set("DEPTH", "VRSwapEyes", device_state.vr_swap_eyes); } } #endif diff --git a/source/openvr/openvr.cpp b/source/openvr/openvr.cpp index af3a0ac3cf..cebb5e9ca5 100644 --- a/source/openvr/openvr.cpp +++ b/source/openvr/openvr.cpp @@ -14,9 +14,12 @@ #include "opengl/runtime_gl.hpp" #include "vulkan/vulkan_hooks.hpp" #include "vulkan/runtime_vk.hpp" +#include "reshade_vr.hpp" #include static std::pair s_vr_runtime = { nullptr, vr::TextureType_Invalid }; +static void *last_submitted_texture = nullptr; +static vr::VRTextureBounds_t last_submitted_bounds = { 0, 0, 0, 0 }; extern lockfree_table g_vulkan_devices; @@ -41,14 +44,8 @@ static bool on_submit_d3d11(vr::EVREye, ID3D11Texture2D *texture, const vr::VRTe texture->GetDesc(&tex_desc); D3D11_BOX region = { 0, 0, 0, tex_desc.Width, tex_desc.Height, 1 }; - if (bounds != nullptr) - { - region.left = static_cast(region.right * std::min(bounds->uMin, bounds->uMax)); - region.top = static_cast(region.bottom * std::min(bounds->vMin, bounds->vMax)); - region.right = static_cast(region.right * std::max(bounds->uMin, bounds->uMax)); - region.bottom = static_cast(region.bottom * std::max(bounds->vMin, bounds->vMax)); - } + RESHADE_ADDON_EVENT(present, s_vr_runtime.first->get_command_queue(), s_vr_runtime.first); return static_cast(s_vr_runtime.first)->on_present(texture, region, nullptr); } static bool on_submit_d3d12(vr::EVREye, const vr::D3D12TextureData_t *texture, const vr::VRTextureBounds_t *bounds) @@ -68,14 +65,8 @@ static bool on_submit_d3d12(vr::EVREye, const vr::D3D12TextureData_t *texture, c const D3D12_RESOURCE_DESC tex_desc = texture->m_pResource->GetDesc(); D3D12_BOX region = { 0, 0, 0, static_cast(tex_desc.Width), tex_desc.Height, 1 }; - if (bounds != nullptr) - { - region.left = static_cast(region.right * std::min(bounds->uMin, bounds->uMax)); - region.top = static_cast(region.bottom * std::min(bounds->vMin, bounds->vMax)); - region.right = static_cast(region.right * std::max(bounds->uMin, bounds->uMax)); - region.bottom = static_cast(region.bottom * std::max(bounds->vMin, bounds->vMax)); - } + RESHADE_ADDON_EVENT(present, s_vr_runtime.first->get_command_queue(), s_vr_runtime.first); // Resource should be in D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE state at this point return static_cast(s_vr_runtime.first)->on_present(texture->m_pResource, region, nullptr); } @@ -96,14 +87,8 @@ static bool on_submit_opengl(vr::EVREye, GLuint object, bool is_rbo, bool is_arr reshade::opengl::make_resource_handle(is_rbo ? GL_RENDERBUFFER : GL_TEXTURE, object), nullptr, nullptr); GLint region[4] = { 0, 0, static_cast(object_desc.width), static_cast(object_desc.height) }; - if (bounds != nullptr) - { - region[0] = static_cast(object_desc.width * std::min(bounds->uMin, bounds->uMax)); - region[1] = static_cast(object_desc.height * std::min(bounds->vMin, bounds->vMax)); - region[2] = static_cast(object_desc.width * std::max(bounds->uMin, bounds->uMax)); - region[3] = static_cast(object_desc.height * std::max(bounds->vMin, bounds->vMax)); - } + RESHADE_ADDON_EVENT(present, s_vr_runtime.first->get_command_queue(), s_vr_runtime.first); return static_cast(s_vr_runtime.first)->on_present(object, is_rbo, is_array, object_desc.width, object_desc.height, region); } static bool on_submit_vulkan(vr::EVREye, const vr::VRVulkanTextureData_t *texture, bool with_array_data, const vr::VRTextureBounds_t *bounds) @@ -130,16 +115,10 @@ static bool on_submit_vulkan(vr::EVREye, const vr::VRVulkanTextureData_t *textur VkOffset2D { 0, 0 }, VkExtent2D { texture->m_nWidth, texture->m_nHeight } }; - if (bounds != nullptr) - { - region.offset.x = static_cast(texture->m_nWidth * std::min(bounds->uMin, bounds->uMax)); - region.extent.width = static_cast(texture->m_nWidth * std::max(bounds->uMin, bounds->uMax) - region.offset.x); - region.offset.y = static_cast(texture->m_nHeight * std::min(bounds->vMin, bounds->vMax)); - region.extent.height = static_cast(texture->m_nHeight * std::max(bounds->vMin, bounds->vMax) - region.offset.y); - } const uint32_t layer_index = with_array_data ? static_cast(texture)->m_unArrayIndex : 0; + RESHADE_ADDON_EVENT(present, s_vr_runtime.first->get_command_queue(), s_vr_runtime.first); // Image should be in VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL layout at this point std::vector wait_semaphores; return static_cast(s_vr_runtime.first)->on_present( @@ -153,6 +132,59 @@ static bool on_submit_vulkan(vr::EVREye, const vr::VRVulkanTextureData_t *textur wait_semaphores); } +static void update_submit_info(vr::EVREye eye, const vr::Texture_t *texture, const vr::VRTextureBounds_t *bounds, vr::EVRSubmitFlags submitFlags) +{ + if (s_vr_runtime.first == nullptr) + return; + + reshade::vr::submit_info *submit_info = nullptr; + if (!s_vr_runtime.first->get_data(reshade::vr::submit_info::GUID, reinterpret_cast(&submit_info))) + submit_info = &s_vr_runtime.first->create_data(reshade::vr::submit_info::GUID); + + auto *device = s_vr_runtime.first->get_device(); + + submit_info->eye = eye; + + if (texture == nullptr || texture->handle == nullptr) + return; + + submit_info->color = reshade::api::resource_handle {reinterpret_cast(texture->handle)}; + auto desc = device->get_resource_desc(submit_info->color); + reshade::api::region region { 0, 0, desc.width, desc.height }; + if (bounds != nullptr) + { + region.left = static_cast(desc.width * std::min(bounds->uMin, bounds->uMax)); + region.top = static_cast(desc.height * std::min(bounds->vMin, bounds->vMax)); + region.right = static_cast(desc.width * std::max(bounds->uMin, bounds->uMax)); + region.bottom = static_cast(desc.height * std::max(bounds->vMin, bounds->vMax)); + } + submit_info->region = region; + + submit_info->depth = {0}; + if (submitFlags & vr::Submit_TextureWithDepth) + { + vr::VRTextureDepthInfo_t depth_info = submitFlags & vr::Submit_TextureWithPose + ? static_cast(texture)->depth + : static_cast(texture)->depth; + submit_info->depth = {reinterpret_cast(depth_info.handle)}; + } +} + +bool should_apply_effects(void *texture_handle, const vr::VRTextureBounds_t *bounds) +{ + if (texture_handle != last_submitted_texture || bounds == nullptr || (bounds->uMin == last_submitted_bounds.uMin && bounds->uMax == last_submitted_bounds.uMax && bounds->vMin == last_submitted_bounds.vMin && bounds->vMax == last_submitted_bounds.vMax)) + { + last_submitted_texture = texture_handle; + last_submitted_bounds = *bounds; + return true; + } + else + { + last_submitted_texture = nullptr; + return false; + } +} + #ifdef WIN64 #define IVRCompositor_Submit_Impl(vtable_offset, interface_version, impl) \ static vr::EVRCompositorError IVRCompositor_Submit_##interface_version(vr::IVRCompositor *pCompositor, IVRCompositor_Submit_##interface_version##_ArgTypes) \ @@ -195,50 +227,80 @@ static bool on_submit_vulkan(vr::EVREye, const vr::VRVulkanTextureData_t *textur #define IVRCompositor_Submit_012_ArgNames eEye, pTexture, pBounds, nSubmitFlags IVRCompositor_Submit_Impl(6, 007, { - switch (eTextureType) + if (!should_apply_effects(pTexture, pBounds)) + status = true; + else { - case 0: // API_DirectX - status = on_submit_d3d11(eEye, static_cast(pTexture), pBounds); - break; - case 1: // API_OpenGL - status = on_submit_opengl(eEye, static_cast(reinterpret_cast(pTexture)), false, false, pBounds); - break; + update_submit_info(eEye, nullptr, nullptr, vr::Submit_Default); + switch (eTextureType) + { + case 0: // API_DirectX + status = on_submit_d3d11(eEye, static_cast(pTexture), pBounds); + break; + case 1: // API_OpenGL + status = on_submit_opengl(eEye, static_cast(reinterpret_cast(pTexture)), false, false, pBounds); + break; + } } }) IVRCompositor_Submit_Impl(6, 008, { - switch (eTextureType) + if (!should_apply_effects(pTexture, pBounds)) + status = true; + else { - case 0: // API_DirectX - status = on_submit_d3d11(eEye, static_cast(pTexture), pBounds); - break; - case 1: // API_OpenGL - status = on_submit_opengl(eEye, static_cast(reinterpret_cast(pTexture)), (nSubmitFlags & vr::Submit_GlRenderBuffer) != 0, false, pBounds); - break; + update_submit_info(eEye, nullptr, nullptr, vr::Submit_Default); + switch (eTextureType) + { + case 0: // API_DirectX + status = on_submit_d3d11(eEye, static_cast(pTexture), pBounds); + break; + case 1: // API_OpenGL + status = on_submit_opengl(eEye, static_cast(reinterpret_cast(pTexture)), (nSubmitFlags & vr::Submit_GlRenderBuffer) != 0, false, pBounds); + break; + } } }) IVRCompositor_Submit_Impl(4, 009, { - switch (pTexture->eType) + if (pTexture->handle == nullptr) + return vr::VRCompositorError_InvalidTexture; + update_submit_info(eEye, pTexture, pBounds, nSubmitFlags); + + if (!should_apply_effects(pTexture->handle, pBounds)) + status = true; + else { - case vr::TextureType_DirectX: - status = on_submit_d3d11(eEye, static_cast(pTexture->handle), pBounds); - break; - case vr::TextureType_OpenGL: - status = on_submit_opengl(eEye, static_cast(reinterpret_cast(pTexture->handle)), (nSubmitFlags & vr::Submit_GlRenderBuffer) != 0, false, pBounds); - break; + switch (pTexture->eType) + { + case vr::TextureType_DirectX: + status = on_submit_d3d11(eEye, static_cast(pTexture->handle), pBounds); + break; + case vr::TextureType_OpenGL: + status = on_submit_opengl(eEye, static_cast(reinterpret_cast(pTexture->handle)), (nSubmitFlags & vr::Submit_GlRenderBuffer) != 0, false, pBounds); + break; + } } }) IVRCompositor_Submit_Impl(5, 012, { - switch (pTexture->eType) + if (pTexture->handle == nullptr) + return vr::VRCompositorError_InvalidTexture; + update_submit_info(eEye, pTexture, pBounds, nSubmitFlags); + + if (!should_apply_effects(pTexture->handle, pBounds)) + status = true; + else { - case vr::TextureType_DirectX: - status = on_submit_d3d11(eEye, static_cast(pTexture->handle), pBounds); - break; - case vr::TextureType_DirectX12: - status = on_submit_d3d12(eEye, static_cast(pTexture->handle), pBounds); - break; - case vr::TextureType_OpenGL: - status = on_submit_opengl(eEye, static_cast(reinterpret_cast(pTexture->handle)), (nSubmitFlags & vr::Submit_GlRenderBuffer) != 0, (nSubmitFlags & vr::Submit_GlArrayTexture) != 0, pBounds); - break; - case vr::TextureType_Vulkan: - status = on_submit_vulkan(eEye, static_cast(pTexture->handle), (nSubmitFlags & vr::Submit_VulkanTextureWithArrayData) != 0, pBounds); - break; + switch (pTexture->eType) + { + case vr::TextureType_DirectX: + status = on_submit_d3d11(eEye, static_cast(pTexture->handle), pBounds); + break; + case vr::TextureType_DirectX12: + status = on_submit_d3d12(eEye, static_cast(pTexture->handle), pBounds); + break; + case vr::TextureType_OpenGL: + status = on_submit_opengl(eEye, static_cast(reinterpret_cast(pTexture->handle)), (nSubmitFlags & vr::Submit_GlRenderBuffer) != 0, (nSubmitFlags & vr::Submit_GlArrayTexture) != 0, pBounds); + break; + case vr::TextureType_Vulkan: + status = on_submit_vulkan(eEye, static_cast(pTexture->handle), (nSubmitFlags & vr::Submit_VulkanTextureWithArrayData) != 0, pBounds); + break; + } } }) HOOK_EXPORT uint32_t VR_CALLTYPE VR_InitInternal2(vr::EVRInitError *peError, vr::EVRApplicationType eApplicationType, const char *pStartupInfo) diff --git a/source/openvr/reshade_vr.hpp b/source/openvr/reshade_vr.hpp new file mode 100644 index 0000000000..82c12cb342 --- /dev/null +++ b/source/openvr/reshade_vr.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "reshade_api.hpp" + +namespace reshade::vr +{ + struct submit_info + { + static constexpr uint8_t GUID[16] = { 0x20, 0x98, 0x62, 0x08, 0xA2, 0x5E, 0x47, 0x43, 0xBC, 0x2B, 0x42, 0xF2, 0xA6, 0x23, 0xBB, 0xB6 }; + + uint8_t eye = 0; + api::resource_handle color = {0}; + api::resource_handle depth = {0}; + api::region region; + }; +}