diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 0fc2b6cc60..4d50c7ca49 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -1,10 +1,8 @@ #pragma once -#include "../rsx_cache.h" -#include "../rsx_utils.h" #include "texture_cache_predictor.h" #include "texture_cache_utils.h" -#include "TextureUtils.h" +#include "texture_cache_helpers.h" #include @@ -31,6 +29,8 @@ namespace rsx using ranged_storage = rsx::ranged_storage; using ranged_storage_block = typename ranged_storage::block_type; + using copy_region_descriptor = copy_region_descriptor_base; + private: static_assert(std::is_base_of, section_storage_type>::value, "section_storage_type must derive from rsx::cached_texture_section"); @@ -133,77 +133,24 @@ namespace rsx bool has_flushables = false; }; - enum surface_transform : u32 - { - identity = 0, - argb_to_bgra = 1, - coordinate_transform = 2 - }; - - struct copy_region_descriptor - { - image_resource_type src; - flags32_t xform; - u16 src_x; - u16 src_y; - u16 dst_x; - u16 dst_y; - u16 dst_z; - u16 src_w; - u16 src_h; - u16 dst_w; - u16 dst_h; - }; - - enum deferred_request_command : u32 - { - nop = 0, - copy_image_static, - copy_image_dynamic, - cubemap_gather, - cubemap_unwrap, - atlas_gather, - _3d_gather, - _3d_unwrap - }; - - using texture_channel_remap_t = std::pair, std::array>; - struct deferred_subresource + struct deferred_subresource : image_section_attributes_t { image_resource_type external_handle = 0; std::vector sections_to_copy; texture_channel_remap_t remap; deferred_request_command op = deferred_request_command::nop; - u32 base_address = 0; - u32 gcm_format = 0; u16 x = 0; u16 y = 0; - u16 width = 0; - u16 height = 0; - u16 depth = 1; - u16 slice_h = 1; bool do_not_cache = false; deferred_subresource() = default; - deferred_subresource(image_resource_type _res, deferred_request_command _op, u32 _addr, u32 _fmt, u16 _x, u16 _y, u16 _w, u16 _h, u16 _d, u16 _s, texture_channel_remap_t _remap) : - external_handle(_res), op(_op), base_address(_addr), gcm_format(_fmt), x(_x), y(_y), width(_w), height(_h), depth(_d), slice_h(_s), remap(std::move(_remap)) - {} - }; - - struct blit_op_result - { - bool succeeded = false; - bool is_depth = false; - u32 real_dst_address = 0; - u32 real_dst_size = 0; - - blit_op_result(bool success) : succeeded(success) - {} - - inline address_range to_address_range() const + deferred_subresource(image_resource_type _res, deferred_request_command _op, + const image_section_attributes_t& attr, position2u offset, + texture_channel_remap_t _remap) + : external_handle(_res), op(_op), x(offset.x), y(offset.y), remap(std::move(_remap)) { - return address_range::start_length(real_dst_address, real_dst_size); + static_cast(*this) = attr; } }; @@ -216,29 +163,29 @@ namespace rsx sampled_image_descriptor() = default; sampled_image_descriptor(image_view_type handle, texture_upload_context ctx, format_type ftype, - f32 x_scale, f32 y_scale, rsx::texture_dimension_extended type, bool cyclic_reference = false) + size2f scale, rsx::texture_dimension_extended type, bool cyclic_reference = false) { image_handle = handle; upload_context = ctx; format_class = ftype; is_cyclic_reference = cyclic_reference; - scale_x = x_scale; - scale_y = y_scale; + scale_x = scale.width; + scale_y = scale.height; image_type = type; } - sampled_image_descriptor(image_resource_type external_handle, deferred_request_command reason, u32 base_address, u32 gcm_format, - u16 x_offset, u16 y_offset, u16 width, u16 height, u16 depth, u16 slice_h, - texture_upload_context ctx, format_type ftype, f32 x_scale, f32 y_scale, + sampled_image_descriptor(image_resource_type external_handle, deferred_request_command reason, + const image_section_attributes_t& attr, position2u src_offset, + texture_upload_context ctx, format_type ftype, size2f scale, rsx::texture_dimension_extended type, const texture_channel_remap_t& remap) { - external_subresource_desc = { external_handle, reason, base_address, gcm_format, x_offset, y_offset, width, height, depth, slice_h, remap }; + external_subresource_desc = { external_handle, reason, attr, src_offset, remap }; image_handle = 0; upload_context = ctx; format_class = ftype; - scale_x = x_scale; - scale_y = y_scale; + scale_x = scale.width; + scale_y = scale.height; image_type = type; } @@ -957,77 +904,6 @@ namespace rsx return {}; } - protected: - inline bool is_gcm_depth_format(u32 format) const - { - switch (format) - { - case CELL_GCM_TEXTURE_DEPTH16: - case CELL_GCM_TEXTURE_DEPTH16_FLOAT: - case CELL_GCM_TEXTURE_DEPTH24_D8: - case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: - return true; - default: - return false; - } - } - - inline u32 get_compatible_depth_format(u32 gcm_format) const - { - switch (gcm_format) - { - case CELL_GCM_TEXTURE_DEPTH24_D8: - case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: - case CELL_GCM_TEXTURE_A8R8G8B8: - return CELL_GCM_TEXTURE_DEPTH24_D8; - case CELL_GCM_TEXTURE_DEPTH16: - case CELL_GCM_TEXTURE_DEPTH16_FLOAT: - case CELL_GCM_TEXTURE_X16: - //case CELL_GCM_TEXTURE_A4R4G4B4: - //case CELL_GCM_TEXTURE_G8B8: - //case CELL_GCM_TEXTURE_A1R5G5B5: - //case CELL_GCM_TEXTURE_R5G5B5A1: - //case CELL_GCM_TEXTURE_R5G6B5: - //case CELL_GCM_TEXTURE_R6G5B5: - return CELL_GCM_TEXTURE_DEPTH16; - } - - LOG_ERROR(RSX, "Unsupported depth conversion (0x%X)", gcm_format); - return gcm_format; - } - - inline bool is_compressed_gcm_format(u32 format) - { - switch (format) - { - default: - return false; - case CELL_GCM_TEXTURE_COMPRESSED_DXT1: - case CELL_GCM_TEXTURE_COMPRESSED_DXT23: - case CELL_GCM_TEXTURE_COMPRESSED_DXT45: - case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: - case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8: - case CELL_GCM_TEXTURE_COMPRESSED_HILO8: - case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8: - return true; - } - } - - inline format_type get_format_class(u32 gcm_format) - { - switch (gcm_format) - { - default: - return format_type::color; - case CELL_GCM_TEXTURE_DEPTH16: - case CELL_GCM_TEXTURE_DEPTH24_D8: - return format_type::depth_uint; - case CELL_GCM_TEXTURE_DEPTH16_FLOAT: - case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: - return format_type::depth_float; - } - } - public: texture_cache() : m_storage(this), m_predictor(this) {} @@ -1444,7 +1320,7 @@ namespace rsx { if (!desc.do_not_cache) { - const auto found = m_temporary_subresource_cache.equal_range(desc.base_address); + const auto found = m_temporary_subresource_cache.equal_range(desc.address); for (auto It = found.first; It != found.second; ++It) { const auto& found_desc = It->second.first; @@ -1533,7 +1409,7 @@ namespace rsx if (result) { - m_temporary_subresource_cache.insert({ desc.base_address,{ desc, result } }); + m_temporary_subresource_cache.insert({ desc.address,{ desc, result } }); } return result; @@ -1554,569 +1430,72 @@ namespace rsx } } - template - std::vector gather_texture_slices(commandbuffer_type& cmd, - const surface_store_list_type& fbos, const std::vector& local, - u32 texaddr, u16 slice_w, u16 slice_h, u16 src_padding, u16 pitch, u16 count, u8 bpp, bool is_depth) - { - // Need to preserve sorting order - struct sort_helper - { - u64 tag; // Timestamp - u32 list; // List source, 0 = fbo, 1 = local - u32 index; // Index in list - }; - - std::vector surfaces; - std::vector sort_list; - const u16 src_slice_h = slice_h + src_padding; - - if (!fbos.empty() && !local.empty()) - { - // Generate sorting tree if both resources are available and overlapping - sort_list.reserve(fbos.size() + local.size()); - - for (u32 index = 0; index < fbos.size(); ++index) - { - sort_list.push_back({ fbos[index].surface->last_use_tag, 0, index }); - } - - for (u32 index = 0; index < local.size(); ++index) - { - if (local[index]->get_context() != rsx::texture_upload_context::blit_engine_dst) - continue; - - sort_list.push_back({ local[index]->last_write_tag, 1, index }); - } - - std::sort(sort_list.begin(), sort_list.end(), [](const auto &a, const auto &b) - { - return (a.tag < b.tag); - }); - } - - auto add_rtt_resource = [&](auto& section, u16 slice) - { - if (section.is_depth != is_depth) - { - // TODO - return; - } - - const u32 slice_begin = (slice * src_slice_h); - const u32 slice_end = (slice_begin + slice_h); - - const u32 section_end = section.dst_area.y + section.dst_area.height; - if (section.dst_area.y >= slice_end || section_end <= slice_begin) - { - // Belongs to a different slice - return; - } - - // How much of this slice to read? - int rebased = int(section.dst_area.y) - slice_begin; - const auto src_x = section.src_area.x; - const auto dst_x = section.dst_area.x; - auto src_y = section.src_area.y; - auto dst_y = section.dst_area.y; - - if (rebased < 0) - { - const u16 delta = u16(-rebased); - src_y += delta; - dst_y += delta; - } - - verify(HERE), dst_y >= slice_begin; - dst_y = (dst_y - slice_begin); - - const auto h = std::min(section_end, slice_end) - section.dst_area.y; - const auto src_width = rsx::apply_resolution_scale(section.src_area.width, true); - const auto src_height = rsx::apply_resolution_scale(h, true); - const auto dst_width = rsx::apply_resolution_scale(section.dst_area.width, true); - const auto dst_height = src_height; - - surfaces.push_back - ({ - section.surface->get_surface(rsx::surface_access::read), - surface_transform::identity, - rsx::apply_resolution_scale(src_x, true), - rsx::apply_resolution_scale(src_y, true), - rsx::apply_resolution_scale(dst_x, true), - rsx::apply_resolution_scale(dst_y, true), - slice, - src_width, src_height, - dst_width, dst_height - }); - }; - - auto add_local_resource = [&](auto& section, u32 address, u16 slice, bool scaling = true) - { - if (section->is_depth_texture() != is_depth) - { - // TODO - return; - } - - // Intersect this resource with the original one - const auto section_bpp = get_format_block_size_in_bytes(section->get_gcm_format()); - const auto normalized_width = (section->get_width() * section_bpp) / bpp; - - const auto clipped = rsx::intersect_region( - section->get_section_base(), normalized_width, section->get_height(), section_bpp, /* parent region (extractee) */ - address, slice_w, slice_h, bpp, /* child region (extracted) */ - pitch); - - // Rect intersection test - // TODO: Make the intersection code cleaner with proper 2D regions - if (std::get<0>(clipped).x >= section->get_width()) - { - // Overlap lies outside the image area! - return; - } - - const auto slice_begin = u32(slice * src_slice_h); - const auto slice_end = u32(slice_begin + slice_h); - - const auto dst_y = std::get<1>(clipped).y; - const auto dst_h = std::get<2>(clipped).height; - - const auto section_end = dst_y + dst_h; - if (dst_y >= slice_end || section_end <= slice_begin) - { - // Belongs to a different slice - return; - } - - const u16 dst_w = (u16)std::get<2>(clipped).width; - const u16 src_w = u16(dst_w * bpp) / section_bpp; - const u16 height = (u16)std::get<2>(clipped).height; - - if (scaling) - { - // Since output is upscaled, also upscale on dst - surfaces.push_back - ({ - section->get_raw_texture(), - surface_transform::identity, - (u16)std::get<0>(clipped).x, - (u16)std::get<0>(clipped).y, - rsx::apply_resolution_scale((u16)std::get<1>(clipped).x, true), - rsx::apply_resolution_scale((u16)std::get<1>(clipped).y, true), - slice, - src_w, - height, - rsx::apply_resolution_scale(dst_w, true), - rsx::apply_resolution_scale(height, true), - }); - } - else - { - surfaces.push_back - ({ - section->get_raw_texture(), - surface_transform::identity, - (u16)std::get<0>(clipped).x, - (u16)std::get<0>(clipped).y, - (u16)std::get<1>(clipped).x, - (u16)std::get<1>(clipped).y, - 0, - src_w, - height, - dst_w, - height, - }); - } - }; - - u32 current_address = texaddr; - u16 current_src_offset = 0; - u16 current_dst_offset = 0; - u32 slice_size = (pitch * src_slice_h); - - surfaces.reserve(count); - u16 found_slices = 0; - - for (u16 slice = 0; slice < count; ++slice) - { - auto num_surface = surfaces.size(); - - if (LIKELY(local.empty())) - { - for (auto §ion : fbos) - { - add_rtt_resource(section, slice); - } - } - else if (fbos.empty()) - { - for (auto §ion : local) - { - add_local_resource(section, current_address, slice, false); - } - } - else - { - for (const auto &e : sort_list) - { - if (e.list == 0) - { - add_rtt_resource(fbos[e.index], slice); - } - else - { - add_local_resource(local[e.index], current_address, slice); - } - } - } - - current_address += slice_size; - if (surfaces.size() != num_surface) - { - found_slices++; - } - } - - if (found_slices < count) - { - if (found_slices > 0) - { - //TODO: Gather remaining sides from the texture cache or upload from cpu (too slow?) - LOG_ERROR(RSX, "Could not gather all required slices for cubemap/3d generation"); - } - else - { - LOG_WARNING(RSX, "Could not gather textures into an atlas; using CPU fallback..."); - } - } - - return surfaces; - } - - template - bool check_framebuffer_resource(commandbuffer_type& cmd, render_target_type texptr, - u16 tex_width, u16 tex_height, u16 tex_depth, u16 tex_pitch, u16 slice_h, - rsx::texture_dimension_extended extended_dimension) - { - if (!rsx::pitch_compatible(texptr, tex_pitch, tex_height)) - { - return false; - } - - const auto surface_width = texptr->get_surface_width(rsx::surface_metrics::samples); - const auto surface_height = texptr->get_surface_height(rsx::surface_metrics::samples); - - switch (extended_dimension) - { - case rsx::texture_dimension_extended::texture_dimension_1d: - return (surface_width >= tex_width); - case rsx::texture_dimension_extended::texture_dimension_2d: - return (surface_width >= tex_width && surface_height >= tex_height); - case rsx::texture_dimension_extended::texture_dimension_3d: - return (surface_width >= tex_width && surface_height >= (slice_h * tex_depth)); - case rsx::texture_dimension_extended::texture_dimension_cubemap: - return (surface_width == tex_height && surface_width >= tex_width && surface_height >= (slice_h * 6)); - } - - return false; - } - - template - sampled_image_descriptor process_framebuffer_resource_fast(commandbuffer_type& cmd, render_target_type texptr, - u32 texaddr, u32 format, - u16 tex_width, u16 tex_height, u16 tex_depth, u16 slice_h, - f32 scale_x, f32 scale_y, + template + sampled_image_descriptor fast_texture_search( + commandbuffer_type& cmd, + const image_section_attributes_t& attr, + const size2f& scale, + u32 encoded_remap, + const texture_channel_remap_t& remap, + bool is_compressed_format, + const utils::address_range& memory_range, rsx::texture_dimension_extended extended_dimension, - u32 encoded_remap, const texture_channel_remap_t& decoded_remap, - bool surface_is_rop_target) + surface_store_type& m_rtts, Args&& ... extras) { - texptr->read_barrier(cmd); - - const auto surface_width = texptr->get_surface_width(rsx::surface_metrics::samples); - const auto surface_height = texptr->get_surface_height(rsx::surface_metrics::samples); - - bool is_depth = texptr->is_depth_surface(); - const bool force_convert = !render_target_format_is_compatible(texptr, format); - - if (const bool gcm_format_is_depth = is_gcm_depth_format(format); - gcm_format_is_depth != is_depth) - { - if (force_convert) - { - is_depth = gcm_format_is_depth; - } - else - { - format = get_compatible_depth_format(format); - } - - // Always make sure the conflict is resolved! - verify(HERE), is_gcm_depth_format(format) == is_depth; - } - - if (LIKELY(extended_dimension == rsx::texture_dimension_extended::texture_dimension_2d || - extended_dimension == rsx::texture_dimension_extended::texture_dimension_1d)) - { - if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_1d) - { - tex_height = 1; - } - - if ((surface_is_rop_target && g_cfg.video.strict_rendering_mode) || - tex_width < surface_width || - tex_height < surface_height || - force_convert) - { - const auto scaled_w = rsx::apply_resolution_scale(tex_width, true); - const auto scaled_h = rsx::apply_resolution_scale(tex_height, true); - - const auto format_class = (force_convert) ? get_format_class(format): texptr->get_format_type(); - const auto command = surface_is_rop_target ? deferred_request_command::copy_image_dynamic : deferred_request_command::copy_image_static; - return { texptr->get_surface(rsx::surface_access::read), command, texaddr, format, 0, 0, scaled_w, scaled_h, 1, 0, - texture_upload_context::framebuffer_storage, format_class, scale_x, scale_y, - extended_dimension, decoded_remap }; - } - - if (surface_is_rop_target) - { - insert_texture_barrier(cmd, texptr); - } - - return{ texptr->get_view(encoded_remap, decoded_remap), texture_upload_context::framebuffer_storage, - texptr->get_format_type(), scale_x, scale_y, rsx::texture_dimension_extended::texture_dimension_2d, surface_is_rop_target }; - } - - const auto scaled_w = rsx::apply_resolution_scale(tex_width, true); - const auto scaled_h = rsx::apply_resolution_scale(tex_height, true); - - if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_3d) - { - return{ texptr->get_surface(rsx::surface_access::read), deferred_request_command::_3d_unwrap, texaddr, format, 0, 0, - scaled_w, scaled_h, tex_depth, slice_h, - texture_upload_context::framebuffer_storage, texptr->get_format_type(), 1.f, 1.f, - rsx::texture_dimension_extended::texture_dimension_3d, decoded_remap }; - } - - verify(HERE), extended_dimension == rsx::texture_dimension_extended::texture_dimension_cubemap; - return{ texptr->get_surface(rsx::surface_access::read), deferred_request_command::cubemap_unwrap, texaddr, format, 0, 0, - scaled_w, scaled_h, 1, slice_h, - texture_upload_context::framebuffer_storage, texptr->get_format_type(), 1.f, 1.f, - rsx::texture_dimension_extended::texture_dimension_cubemap, decoded_remap }; - } - - template - sampled_image_descriptor merge_cache_resources(commandbuffer_type& cmd, const surface_store_list_type& fbos, const std::vector& local, - u32 texaddr, u32 format, - u16 tex_width, u16 tex_height, u16 tex_depth, u16 tex_pitch, u16 slice_h, - f32 scale_x, f32 scale_y, - rsx::texture_dimension_extended extended_dimension, - u32 encoded_remap, const texture_channel_remap_t& decoded_remap, - int select_hint = -1) - { - verify(HERE), (select_hint & 0x1) == select_hint; - - bool is_depth; - if (is_depth = (select_hint == 0) ? fbos.back().is_depth : local.back()->is_depth_texture(); - is_depth) - { - if (const auto suggested_format = get_compatible_depth_format(format); - !is_gcm_depth_format(suggested_format)) - { - // Failed! - is_depth = false; - } - else - { - format = suggested_format; - } - } - - // If this method was called, there is no easy solution, likely means atlas gather is needed - auto scaled_w = rsx::apply_resolution_scale(tex_width, true); - auto scaled_h = rsx::apply_resolution_scale(tex_height, true); - - const auto bpp = get_format_block_size_in_bytes(format); - const auto format_class = get_format_class(format); - - if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_cubemap) - { - sampled_image_descriptor desc = { nullptr, deferred_request_command::cubemap_gather, texaddr, format, 0, 0, - scaled_w, scaled_w, 1, slice_h, - texture_upload_context::framebuffer_storage, format_class, 1.f, 1.f, - rsx::texture_dimension_extended::texture_dimension_cubemap, decoded_remap }; - - u16 padding = u16(slice_h - tex_width); - desc.external_subresource_desc.sections_to_copy = std::move(gather_texture_slices(cmd, fbos, local, texaddr, tex_width, tex_height, padding, tex_pitch, 6, bpp, is_depth)); - return desc; - } - else if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_3d && tex_depth > 1) - { - sampled_image_descriptor desc = { nullptr, deferred_request_command::_3d_gather, texaddr, format, 0, 0, - scaled_w, scaled_h, tex_depth, slice_h, - texture_upload_context::framebuffer_storage, format_class, 1.f, 1.f, - rsx::texture_dimension_extended::texture_dimension_3d, decoded_remap }; - - u16 padding = u16(slice_h - tex_height); - desc.external_subresource_desc.sections_to_copy = std::move(gather_texture_slices(cmd, fbos, local, texaddr, tex_width, tex_height, padding, tex_pitch, tex_depth, bpp, is_depth)); - return desc; - } - - if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_1d) - { - verify(HERE), tex_height == 1; - } - - const auto w = fbos.empty()? tex_width : rsx::apply_resolution_scale(tex_width, true); - const auto h = fbos.empty()? tex_height : rsx::apply_resolution_scale(tex_height, true); - - sampled_image_descriptor result = { nullptr, deferred_request_command::atlas_gather, - texaddr, format, 0, 0, w, h, 1, slice_h, texture_upload_context::framebuffer_storage, format_class, - scale_x, scale_y, rsx::texture_dimension_extended::texture_dimension_2d, decoded_remap }; - - result.external_subresource_desc.sections_to_copy = gather_texture_slices(cmd, fbos, local, texaddr, tex_width, tex_height, 0, tex_pitch, 1, bpp, is_depth); - result.simplify(); - return result; - } - - template - sampled_image_descriptor upload_texture(commandbuffer_type& cmd, RsxTextureType& tex, surface_store_type& m_rtts, Args&&... extras) - { - const u32 texaddr = rsx::get_address(tex.offset(), tex.location()); - const u32 tex_size = (u32)get_texture_size(tex); - const address_range tex_range = address_range::start_length(texaddr, tex_size); - const u32 format = tex.format() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); - const bool is_compressed_format = is_compressed_gcm_format(format); - const bool unnormalized = (tex.format() & CELL_GCM_TEXTURE_UN) != 0; - const bool linear = !!(tex.format() & CELL_GCM_TEXTURE_LN); - - const auto extended_dimension = tex.get_extended_texture_dimension(); - u16 tex_width = tex.width(); - u16 tex_height = tex.height(); - u16 tex_pitch = linear? (u16)tex.pitch() : get_format_packed_pitch(format, tex_width, !tex.border_type(), !linear); - - u16 depth; - u32 required_surface_height, slice_h; - switch (extended_dimension) - { - case rsx::texture_dimension_extended::texture_dimension_1d: - depth = 1; - slice_h = required_surface_height = 1; - break; - case rsx::texture_dimension_extended::texture_dimension_2d: - depth = 1; - slice_h = required_surface_height = tex_height; - break; - case rsx::texture_dimension_extended::texture_dimension_cubemap: - depth = 6; - required_surface_height = tex_range.length() / tex_pitch; - slice_h = required_surface_height / depth; - break; - case rsx::texture_dimension_extended::texture_dimension_3d: - depth = tex.depth(); - required_surface_height = tex_range.length() / tex_pitch; - slice_h = required_surface_height / depth; - break; - } - - f32 scale_x = (unnormalized) ? (1.f / tex_width) : 1.f; - f32 scale_y = (unnormalized) ? (1.f / tex_height) : 1.f; - - if (!tex_pitch) - { - // Linear scanning with pitch of 0, read only texel (0,0) - tex_pitch = get_format_packed_pitch(format, tex_width, !tex.border_type(), !linear); - scale_x = 0.f; - scale_y = 0.f; - } - else if (required_surface_height == 1) - { - // Pitch doesn't matter when height=1 and some games abuse this fact - tex_pitch = std::max(tex_pitch, get_format_packed_pitch(format, tex_width, !tex.border_type(), !linear)); - scale_y = 0.f; - } - - // Sanity check - if (UNLIKELY(unnormalized && extended_dimension > rsx::texture_dimension_extended::texture_dimension_2d)) - { - LOG_ERROR(RSX, "Unimplemented unnormalized sampling for texture type %d", (u32)extended_dimension); - } - - if (UNLIKELY(m_rtts.address_is_bound(texaddr))) - { - if (auto texptr = m_rtts.get_surface_at(texaddr); - check_framebuffer_resource(cmd, texptr, tex_width, tex_height, depth, tex_pitch, slice_h, extended_dimension)) - { - return process_framebuffer_resource_fast(cmd, texptr, texaddr, format, tex_width, tex_height, depth, slice_h, - scale_x, scale_y, extended_dimension, tex.remap(), tex.decoded_remap(), true); - } - } - - reader_lock lock(m_cache_mutex); - if (LIKELY(is_compressed_format)) { // Most mesh textures are stored as compressed to make the most of the limited memory - if (auto cached_texture = find_texture_from_dimensions(texaddr, format, tex_width, tex_height, depth)) + if (auto cached_texture = find_texture_from_dimensions(attr.address, attr.gcm_format, attr.width, attr.height, attr.depth)) { - return{ cached_texture->get_view(tex.remap(), tex.decoded_remap()), cached_texture->get_context(), cached_texture->get_format_type(), scale_x, scale_y, cached_texture->get_image_type() }; + return{ cached_texture->get_view(encoded_remap, remap), cached_texture->get_context(), cached_texture->get_format_type(), scale, cached_texture->get_image_type() }; } } else { - // Check shader_read storage. In a given scene, reads from local memory far outnumber reads from the surface cache - const u32 lookup_mask = rsx::texture_upload_context::shader_read | rsx::texture_upload_context::blit_engine_dst | rsx::texture_upload_context::blit_engine_src; - - auto lookup_range = tex_range; - if (LIKELY(extended_dimension <= rsx::texture_dimension_extended::texture_dimension_2d)) + // Fast lookup for cyclic reference + if (UNLIKELY(m_rtts.address_is_bound(attr.address))) { - // Optimize the range a bit by only searching for mip0, layer0 to avoid false positives - const auto texel_rows_per_line = get_format_texel_rows_per_line(format); - const auto num_rows = (tex_height + texel_rows_per_line - 1) / texel_rows_per_line; - if (const auto length = u32(num_rows * tex_pitch); length < tex_range.length()) + if (auto texptr = m_rtts.get_surface_at(attr.address); + texture_cache_helpers::check_framebuffer_resource(texptr, attr, extended_dimension)) { - lookup_range = utils::address_range::start_length(texaddr, length); + const bool force_convert = !render_target_format_is_compatible(texptr, attr.gcm_format); + + auto result = texture_cache_helpers::process_framebuffer_resource_fast( + cmd, texptr, attr, scale, extended_dimension, encoded_remap, remap, true, force_convert); + + insert_texture_barrier(cmd, texptr); + return result; } } - auto overlapping_locals = find_texture_from_range(lookup_range, tex_height > 1? tex_pitch : 0, lookup_mask); + // Check shader_read storage. In a given scene, reads from local memory far outnumber reads from the surface cache + const u32 lookup_mask = rsx::texture_upload_context::shader_read | rsx::texture_upload_context::blit_engine_dst | rsx::texture_upload_context::blit_engine_src; + auto overlapping_locals = find_texture_from_range(memory_range, attr.height > 1 ? attr.pitch : 0, lookup_mask); // Search for exact match if possible for (auto& cached_texture : overlapping_locals) { - if (cached_texture->matches(texaddr, format, tex_width, tex_height, depth, 0)) + if (cached_texture->matches(attr.address, attr.gcm_format, attr.width, attr.height, attr.depth, 0)) { - return{ cached_texture->get_view(tex.remap(), tex.decoded_remap()), cached_texture->get_context(), cached_texture->get_format_type(), scale_x, scale_y, cached_texture->get_image_type() }; + return{ cached_texture->get_view(encoded_remap, remap), cached_texture->get_context(), cached_texture->get_format_type(), scale, cached_texture->get_image_type() }; } } - // Next, attempt to merge blit engine and surface store - // Blit sources contain info from any shader-read stuff in range - // NOTE: Compressed formats require a reupload, facilitated by blit synchronization and/or WCB and are not handled here - if (!overlapping_locals.empty()) { - if (tex.get_exact_mipmap_count() > 1 && !linear) - { - // Investigate if this is possible; it can work for disjoint sections, but the gather code cannot handle this at the moment - LOG_TODO(RSX, "Mipmap gather of swizzled texture requested but not implemented"); - overlapping_locals.clear(); - } - else - { - // Remove everything that is not a transfer target - overlapping_locals.erase - ( - std::remove_if(overlapping_locals.begin(), overlapping_locals.end(), [](const auto& e) - { - return (e->get_context() != rsx::texture_upload_context::blit_engine_dst); - }), - overlapping_locals.end() - ); - } + // Remove everything that is not a transfer target + overlapping_locals.erase + ( + std::remove_if(overlapping_locals.begin(), overlapping_locals.end(), [](const auto& e) + { + return (e->get_context() != rsx::texture_upload_context::blit_engine_dst); + }), + overlapping_locals.end() + ); } - const auto bpp = get_format_block_size_in_bytes(format); - const auto overlapping_fbos = m_rtts.get_merged_texture_memory_region(cmd, texaddr, tex_width, required_surface_height, tex_pitch, bpp, rsx::surface_access::read); + // TODO: Should scan for block_height aka required_surface_height not simply attr.height + const auto overlapping_fbos = m_rtts.get_merged_texture_memory_region(cmd, attr.address, attr.width, attr.height, attr.pitch, attr.bpp, rsx::surface_access::read); if (!overlapping_fbos.empty() || !overlapping_locals.empty()) { @@ -2138,27 +1517,26 @@ namespace rsx { // Surface cache data is newer, check if this thing fits our search parameters const auto& last = overlapping_fbos.back(); - if (last.src_area.x == 0 && last.src_area.y == 0) + if (last.src_area.x == 0 && last.src_area.y == 0 && !last.is_clipped) { - if (last.dst_area.width >= tex_width && last.dst_area.height >= required_surface_height) - { - return process_framebuffer_resource_fast(cmd, last.surface, texaddr, format, tex_width, tex_height, depth, slice_h, - scale_x, scale_y, extended_dimension, tex.remap(), tex.decoded_remap(), false); - } + const bool force_convert = !render_target_format_is_compatible(last.surface, attr.gcm_format); + + return texture_cache_helpers::process_framebuffer_resource_fast( + cmd, last.surface, attr, scale, extended_dimension, encoded_remap, remap, false, force_convert); } } else if (extended_dimension <= rsx::texture_dimension_extended::texture_dimension_2d) { const auto last = overlapping_locals.back(); - const auto normalized_width = u16(last->get_width() * get_format_block_size_in_bytes(last->get_gcm_format())) / bpp; + const auto normalized_width = u16(last->get_width() * get_format_block_size_in_bytes(last->get_gcm_format())) / attr.bpp; - if (last->get_section_base() == texaddr && - normalized_width >= tex_width && last->get_height() >= tex_height) + if (last->get_section_base() == attr.address && + normalized_width >= attr.width && last->get_height() >= attr.height) { bool is_depth = last->is_depth_texture(); - u32 gcm_format = format; + u32 gcm_format = attr.gcm_format; - if (const auto gcm_format_is_depth = is_gcm_depth_format(format); + if (const auto gcm_format_is_depth = texture_cache_helpers::is_gcm_depth_format(attr.gcm_format); is_depth != gcm_format_is_depth) { // Conflict, resolve @@ -2171,7 +1549,7 @@ namespace rsx const auto actual_format = last->get_gcm_format(); bool resolved = false; - switch (format) + switch (attr.gcm_format) { case CELL_GCM_TEXTURE_A8R8G8B8: case CELL_GCM_TEXTURE_D8R8G8B8: @@ -2201,22 +1579,23 @@ namespace rsx if (!resolved) { LOG_ERROR(RSX, "Reading texture with gcm format 0x%x as unexpected cast with format 0x%x", - actual_format, format); + actual_format, attr.gcm_format); is_depth = gcm_format_is_depth; } } } - return { last->get_raw_texture(), deferred_request_command::copy_image_static, texaddr, gcm_format, 0, 0, - tex_width, tex_height, 1, 0, last->get_context(), last->get_format_type(), - scale_x, scale_y, extended_dimension, tex.decoded_remap() }; + auto new_attr = attr; + new_attr.gcm_format = gcm_format; + + return { last->get_raw_texture(), deferred_request_command::copy_image_static, new_attr, {}, + last->get_context(), last->get_format_type(), scale, extended_dimension, remap }; } } - auto result = merge_cache_resources(cmd, overlapping_fbos, overlapping_locals, - texaddr, format, tex_width, tex_height, depth, tex_pitch, slice_h, - scale_x, scale_y, extended_dimension, tex.remap(), tex.decoded_remap(), _pool); + auto result = texture_cache_helpers::merge_cache_resources( + overlapping_fbos, overlapping_locals, attr, scale, extended_dimension, encoded_remap, remap, _pool); if (!result.external_subresource_desc.sections_to_copy.empty() && (_pool == 0 || result.atlas_covers_target_area())) @@ -2226,7 +1605,7 @@ namespace rsx // Its then sampled again here as 384x384 and this does not work! (obviously) // Optionally disallow caching if resource is being written to as it is being read from - for (const auto §ion : overlapping_fbos) + for (const auto& section : overlapping_fbos) { if (m_rtts.address_is_bound(section.base_address)) { @@ -2245,52 +1624,161 @@ namespace rsx return result; } -#ifdef TEXTURE_CACHE_DEBUG - else - { - LOG_ERROR(RSX, "Area merge failed! addr=0x%x, w=%d, h=%d, gcm_format=0x%x[sz=%d]", texaddr, tex_width, tex_height, format, !(tex.format() & CELL_GCM_TEXTURE_LN)); - for (const auto &s : overlapping_locals) - { - if (s->get_context() == rsx::texture_upload_context::blit_engine_dst) - { - LOG_ERROR(RSX, "Btw, you're about to lose a blit surface at 0x%x", s->get_section_base()); - } - } - //LOG_TRACE(RSX, "Partial memory recovered from cache; may require WCB/WDB to properly gather all the data"); - } -#endif // TEXTURE_CACHE_DEBUG } } - // Do direct upload from CPU as the last resort - const bool is_swizzled = !(tex.format() & CELL_GCM_TEXTURE_LN); - auto subresources_layout = get_subresources_layout(tex); + return {}; + } - rsx::format_type format_class; - switch (format) + template + sampled_image_descriptor upload_texture(commandbuffer_type& cmd, RsxTextureType& tex, surface_store_type& m_rtts, Args&&... extras) + { + image_section_attributes_t attributes{}; + attributes.address = rsx::get_address(tex.offset(), tex.location()); + attributes.gcm_format = tex.format() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); + attributes.bpp = get_format_block_size_in_bytes(attributes.gcm_format); + attributes.width = tex.width(); + attributes.height = tex.height(); + + const bool is_compressed_format = texture_cache_helpers::is_compressed_gcm_format(attributes.gcm_format); + const bool is_unnormalized = !!(tex.format() & CELL_GCM_TEXTURE_UN); + const bool is_swizzled = !(tex.format() & CELL_GCM_TEXTURE_LN); + const auto extended_dimension = tex.get_extended_texture_dimension(); + + u32 tex_size = 0, required_surface_height, subsurface_count; + size2f scale{ 1.f, 1.f }; + + if (LIKELY(!is_swizzled)) { - default: - format_class = rsx::format_type::color; + if (attributes.pitch = tex.pitch(); !attributes.pitch) + { + attributes.pitch = get_format_packed_pitch(attributes.gcm_format, attributes.width, !tex.border_type(), false); + scale = { 0.f, 0.f }; + } + } + else + { + attributes.pitch = get_format_packed_pitch(attributes.gcm_format, attributes.width, !tex.border_type(), true); + } + + switch (extended_dimension) + { + case rsx::texture_dimension_extended::texture_dimension_1d: + attributes.depth = 1; + attributes.slice_h = 1; + scale.height = 0.f; + subsurface_count = 1; + required_surface_height = 1; break; - case CELL_GCM_TEXTURE_DEPTH16: - case CELL_GCM_TEXTURE_DEPTH24_D8: - format_class = rsx::format_type::depth_uint; + case rsx::texture_dimension_extended::texture_dimension_2d: + attributes.depth = 1; + subsurface_count = is_compressed_format? 1 : tex.get_exact_mipmap_count(); + attributes.slice_h = required_surface_height = attributes.height; break; - case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: - case CELL_GCM_TEXTURE_DEPTH16_FLOAT: - format_class = rsx::format_type::depth_float; + case rsx::texture_dimension_extended::texture_dimension_cubemap: + attributes.depth = 6; + subsurface_count = 1; + tex_size = (u32)get_texture_size(tex); + required_surface_height = tex_size / attributes.pitch; + attributes.slice_h = required_surface_height / attributes.depth; break; + case rsx::texture_dimension_extended::texture_dimension_3d: + attributes.depth = tex.depth(); + subsurface_count = 1; + tex_size = (u32)get_texture_size(tex); + required_surface_height = tex_size / attributes.pitch; + attributes.slice_h = required_surface_height / attributes.depth; + break; + } + + if (is_unnormalized) + { + if (extended_dimension <= rsx::texture_dimension_extended::texture_dimension_2d) + { + scale.width /= attributes.width; + scale.height /= attributes.height; + } + else + { + LOG_ERROR(RSX, "Unimplemented unnormalized sampling for texture type %d", (u32)extended_dimension); + } + } + + const auto lookup_range = utils::address_range::start_length(attributes.address, attributes.pitch * required_surface_height); + reader_lock lock(m_cache_mutex); + + auto result = fast_texture_search(cmd, attributes, scale, tex.remap(), tex.decoded_remap(), + is_compressed_format, lookup_range, extended_dimension, m_rtts, + std::forward(extras)...); + + if (result.validate()) + { +#if 0 + if (subsurface_count <= 1 || + (result.image_handle && result.upload_context == rsx::texture_upload_context::shader_read)) + { + // Full result exists + return result; + } + + if (result.upload_context != rsx::texture_upload_context::blit_engine_dst && + result.upload_context != rsx::texture_upload_context::framebuffer_storage) + { + LOG_ERROR(RSX, "Unexpected surface context %d", (u32)result.upload_context); + return result; + } + + // Traverse mipmap tree + auto scan_address = texaddr + (tex_pitch * tex_height); + auto scan_pitch = swizzled? (tex_pitch / 2) : tex_pitch; + auto scan_width = (tex_width / 2); + auto scan_height = (tex_height / 2); + + std::vector sections; + sections.reserve(subsurface_count - 1); + + for (u32 subsurface = 1; subsurface < subsurface_count; ++subsurface) + { + const auto range = utils::address_range::start_length(scan_address, scan_pitch * scan_height); + + auto ret = fast_texture_search(cmd, scan_address, format, scan_width, scan_height, 1, scan_height, + scan_pitch, bpp, is_compressed_format, range, extended_dimension, m_rtts, + std::forward(extras)...); + + if (LIKELY(ret.validate())) + { + sections.push_back(ret); + } + else + { + break; + } + } +#endif + return result; + } + + // Do direct upload from CPU as the last resort + const auto subresources_layout = get_subresources_layout(tex); + const auto format_class = texture_cache_helpers::get_format_class(attributes.gcm_format); + + if (!tex_size) + { + tex_size = (u32)get_texture_size(tex); } lock.upgrade(); - //Invalidate + // Invalidate + const address_range tex_range = address_range::start_length(attributes.address, tex_size); invalidate_range_impl_base(cmd, tex_range, invalidation_cause::read, std::forward(extras)...); - //NOTE: SRGB correction is to be handled in the fragment shader; upload as linear RGB - return{ upload_image_from_cpu(cmd, tex_range, tex_width, tex_height, depth, tex.get_exact_mipmap_count(), tex_pitch, format, - texture_upload_context::shader_read, subresources_layout, extended_dimension, is_swizzled)->get_view(tex.remap(), tex.decoded_remap()), - texture_upload_context::shader_read, format_class, scale_x, scale_y, extended_dimension }; + // Upload from CPU. Note that sRGB conversion is handled in the FS + auto uploaded = upload_image_from_cpu(cmd, tex_range, attributes.width, attributes.height, attributes.depth, tex.get_exact_mipmap_count(), attributes.pitch, attributes.gcm_format, + texture_upload_context::shader_read, subresources_layout, extended_dimension, is_swizzled); + + return{ uploaded->get_view(tex.remap(), tex.decoded_remap()), + texture_upload_context::shader_read, format_class, scale, extended_dimension }; } template diff --git a/rpcs3/Emu/RSX/Common/texture_cache_helpers.h b/rpcs3/Emu/RSX/Common/texture_cache_helpers.h new file mode 100644 index 0000000000..b7b8c94b05 --- /dev/null +++ b/rpcs3/Emu/RSX/Common/texture_cache_helpers.h @@ -0,0 +1,585 @@ +#pragma once + +#include "../rsx_utils.h" +#include "TextureUtils.h" + +namespace rsx +{ + using texture_channel_remap_t = std::pair, std::array>; + + // Defines pixel operation to be performed on a surface before it is ready for use + enum surface_transform : u32 + { + identity = 0, // Nothing + argb_to_bgra = 1, // Swap ARGB to BGRA (endian swap) + coordinate_transform = 2 // Incoming source coordinates may generated based on the format of the secondary (dest) surface. Recalculate them before use. + }; + + template + struct copy_region_descriptor_base + { + image_resource_type src; + flags32_t xform; + u16 src_x; + u16 src_y; + u16 dst_x; + u16 dst_y; + u16 dst_z; + u16 src_w; + u16 src_h; + u16 dst_w; + u16 dst_h; + }; + + // Deferred texture processing commands + enum class deferred_request_command : u32 + { + nop = 0, // Nothing + copy_image_static, // Copy image and cache the results + copy_image_dynamic, // Copy image but do not cache the results + cubemap_gather, // Provided list of sections generates a cubemap + cubemap_unwrap, // One large texture provided to be partitioned into a cubemap + atlas_gather, // Provided list of sections generates a texture atlas + _3d_gather, // Provided list of sections generates a 3D array + _3d_unwrap, // One large texture provided to be partitioned into a 3D array + mipmap_gather // Provided list of sections to be reassembled as mipmap levels of the same texture + }; + + struct image_section_attributes_t + { + u32 address; + u32 gcm_format; + u16 width; + u16 height; + u16 depth; + u16 pitch; + u16 slice_h; + u8 bpp; + }; + + struct blit_op_result + { + bool succeeded = false; + bool is_depth = false; + u32 real_dst_address = 0; + u32 real_dst_size = 0; + + blit_op_result(bool success) : succeeded(success) + {} + + inline address_range to_address_range() const + { + return address_range::start_length(real_dst_address, real_dst_size); + } + }; + + namespace texture_cache_helpers + { + static inline bool is_gcm_depth_format(u32 format) + { + switch (format) + { + case CELL_GCM_TEXTURE_DEPTH16: + case CELL_GCM_TEXTURE_DEPTH16_FLOAT: + case CELL_GCM_TEXTURE_DEPTH24_D8: + case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: + return true; + default: + return false; + } + } + + static inline u32 get_compatible_depth_format(u32 gcm_format) + { + switch (gcm_format) + { + case CELL_GCM_TEXTURE_DEPTH24_D8: + case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: + case CELL_GCM_TEXTURE_A8R8G8B8: + return CELL_GCM_TEXTURE_DEPTH24_D8; + case CELL_GCM_TEXTURE_DEPTH16: + case CELL_GCM_TEXTURE_DEPTH16_FLOAT: + case CELL_GCM_TEXTURE_X16: + //case CELL_GCM_TEXTURE_A4R4G4B4: + //case CELL_GCM_TEXTURE_G8B8: + //case CELL_GCM_TEXTURE_A1R5G5B5: + //case CELL_GCM_TEXTURE_R5G5B5A1: + //case CELL_GCM_TEXTURE_R5G6B5: + //case CELL_GCM_TEXTURE_R6G5B5: + return CELL_GCM_TEXTURE_DEPTH16; + } + + LOG_ERROR(RSX, "Unsupported depth conversion (0x%X)", gcm_format); + return gcm_format; + } + + static inline bool is_compressed_gcm_format(u32 format) + { + switch (format) + { + default: + return false; + case CELL_GCM_TEXTURE_COMPRESSED_DXT1: + case CELL_GCM_TEXTURE_COMPRESSED_DXT23: + case CELL_GCM_TEXTURE_COMPRESSED_DXT45: + case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: + case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8: + case CELL_GCM_TEXTURE_COMPRESSED_HILO8: + case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8: + return true; + } + } + + static inline format_type get_format_class(u32 gcm_format) + { + switch (gcm_format) + { + default: + return format_type::color; + case CELL_GCM_TEXTURE_DEPTH16: + case CELL_GCM_TEXTURE_DEPTH24_D8: + return format_type::depth_uint; + case CELL_GCM_TEXTURE_DEPTH16_FLOAT: + case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: + return format_type::depth_float; + } + } + + template + void gather_texture_slices( + std::vector& out, + const surface_store_list_type& fbos, + const std::vector& local, + const image_section_attributes_t& attr, + u16 count, bool is_depth) + { + // Need to preserve sorting order + struct sort_helper + { + u64 tag; // Timestamp + u32 list; // List source, 0 = fbo, 1 = local + u32 index; // Index in list + }; + + std::vector sort_list; + const auto src_slice_h = attr.height; + + if (!fbos.empty() && !local.empty()) + { + // Generate sorting tree if both resources are available and overlapping + sort_list.reserve(fbos.size() + local.size()); + + for (u32 index = 0; index < fbos.size(); ++index) + { + sort_list.push_back({ fbos[index].surface->last_use_tag, 0, index }); + } + + for (u32 index = 0; index < local.size(); ++index) + { + if (local[index]->get_context() != rsx::texture_upload_context::blit_engine_dst) + continue; + + sort_list.push_back({ local[index]->last_write_tag, 1, index }); + } + + std::sort(sort_list.begin(), sort_list.end(), [](const auto& a, const auto& b) + { + return (a.tag < b.tag); + }); + } + + auto add_rtt_resource = [&](auto& section, u16 slice) + { + if (section.is_depth != is_depth) + { + // TODO + return; + } + + const u32 slice_begin = (slice * src_slice_h); + const u32 slice_end = (slice_begin + attr.slice_h); + + const u32 section_end = section.dst_area.y + section.dst_area.height; + if (section.dst_area.y >= slice_end || section_end <= slice_begin) + { + // Belongs to a different slice + return; + } + + // How much of this slice to read? + int rebased = int(section.dst_area.y) - slice_begin; + const auto src_x = section.src_area.x; + const auto dst_x = section.dst_area.x; + auto src_y = section.src_area.y; + auto dst_y = section.dst_area.y; + + if (rebased < 0) + { + const u16 delta = u16(-rebased); + src_y += delta; + dst_y += delta; + } + + verify(HERE), dst_y >= slice_begin; + dst_y = (dst_y - slice_begin); + + const auto h = std::min(section_end, slice_end) - section.dst_area.y; + const auto src_width = rsx::apply_resolution_scale(section.src_area.width, true); + const auto src_height = rsx::apply_resolution_scale(h, true); + const auto dst_width = rsx::apply_resolution_scale(section.dst_area.width, true); + const auto dst_height = src_height; + + out.push_back + ({ + section.surface->get_surface(rsx::surface_access::read), + surface_transform::identity, + rsx::apply_resolution_scale(src_x, true), + rsx::apply_resolution_scale(src_y, true), + rsx::apply_resolution_scale(dst_x, true), + rsx::apply_resolution_scale(dst_y, true), + slice, + src_width, src_height, + dst_width, dst_height + }); + }; + + auto add_local_resource = [&](auto& section, u32 address, u16 slice, bool scaling = true) + { + if (section->is_depth_texture() != is_depth) + { + // TODO + return; + } + + // Intersect this resource with the original one + const auto section_bpp = get_format_block_size_in_bytes(section->get_gcm_format()); + const auto normalized_width = (section->get_width() * section_bpp) / attr.bpp; + + const auto clipped = rsx::intersect_region( + section->get_section_base(), normalized_width, section->get_height(), section_bpp, /* parent region (extractee) */ + address, attr.width, attr.slice_h, attr.bpp, /* child region (extracted) */ + attr.pitch); + + // Rect intersection test + // TODO: Make the intersection code cleaner with proper 2D regions + if (std::get<0>(clipped).x >= section->get_width()) + { + // Overlap lies outside the image area! + return; + } + + const auto slice_begin = u32(slice * src_slice_h); + const auto slice_end = u32(slice_begin + attr.slice_h); + + const auto dst_y = std::get<1>(clipped).y; + const auto dst_h = std::get<2>(clipped).height; + + const auto section_end = dst_y + dst_h; + if (dst_y >= slice_end || section_end <= slice_begin) + { + // Belongs to a different slice + return; + } + + const u16 dst_w = (u16)std::get<2>(clipped).width; + const u16 src_w = u16(dst_w * attr.bpp) / section_bpp; + const u16 height = (u16)std::get<2>(clipped).height; + + if (scaling) + { + // Since output is upscaled, also upscale on dst + out.push_back + ({ + section->get_raw_texture(), + surface_transform::identity, + (u16)std::get<0>(clipped).x, + (u16)std::get<0>(clipped).y, + rsx::apply_resolution_scale((u16)std::get<1>(clipped).x, true), + rsx::apply_resolution_scale((u16)std::get<1>(clipped).y, true), + slice, + src_w, + height, + rsx::apply_resolution_scale(dst_w, true), + rsx::apply_resolution_scale(height, true), + }); + } + else + { + out.push_back + ({ + section->get_raw_texture(), + surface_transform::identity, + (u16)std::get<0>(clipped).x, + (u16)std::get<0>(clipped).y, + (u16)std::get<1>(clipped).x, + (u16)std::get<1>(clipped).y, + 0, + src_w, + height, + dst_w, + height, + }); + } + }; + + u32 current_address = attr.address; + u16 current_src_offset = 0; + u16 current_dst_offset = 0; + u32 slice_size = (attr.pitch * src_slice_h); + + out.reserve(count); + u16 found_slices = 0; + + for (u16 slice = 0; slice < count; ++slice) + { + auto num_surface = out.size(); + + if (LIKELY(local.empty())) + { + for (auto& section : fbos) + { + add_rtt_resource(section, slice); + } + } + else if (fbos.empty()) + { + for (auto& section : local) + { + add_local_resource(section, current_address, slice, false); + } + } + else + { + for (const auto& e : sort_list) + { + if (e.list == 0) + { + add_rtt_resource(fbos[e.index], slice); + } + else + { + add_local_resource(local[e.index], current_address, slice); + } + } + } + + current_address += slice_size; + if (out.size() != num_surface) + { + found_slices++; + } + } + + if (found_slices < count) + { + if (found_slices > 0) + { + //TODO: Gather remaining sides from the texture cache or upload from cpu (too slow?) + LOG_ERROR(RSX, "Could not gather all required slices for cubemap/3d generation"); + } + else + { + LOG_WARNING(RSX, "Could not gather textures into an atlas; using CPU fallback..."); + } + } + } + + template + bool check_framebuffer_resource( + render_target_type texptr, + const image_section_attributes_t& attr, + texture_dimension_extended extended_dimension) + { + if (!rsx::pitch_compatible(texptr, attr.pitch, attr.height)) + { + return false; + } + + const auto surface_width = texptr->get_surface_width(rsx::surface_metrics::samples); + const auto surface_height = texptr->get_surface_height(rsx::surface_metrics::samples); + + switch (extended_dimension) + { + case rsx::texture_dimension_extended::texture_dimension_1d: + return (surface_width >= attr.width); + case rsx::texture_dimension_extended::texture_dimension_2d: + return (surface_width >= attr.width && surface_height >= attr.height); + case rsx::texture_dimension_extended::texture_dimension_3d: + return (surface_width >= attr.width && surface_height >= (attr.slice_h * attr.depth)); + case rsx::texture_dimension_extended::texture_dimension_cubemap: + return (surface_width == attr.height && surface_width >= attr.width && surface_height >= (attr.slice_h * 6)); + } + + return false; + } + + template + sampled_image_descriptor process_framebuffer_resource_fast(commandbuffer_type& cmd, + render_target_type texptr, + const image_section_attributes_t& attr, + const size2f& scale, + texture_dimension_extended extended_dimension, + u32 encoded_remap, const texture_channel_remap_t& decoded_remap, + bool surface_is_rop_target, + bool force_convert) + { + texptr->read_barrier(cmd); + + const auto surface_width = texptr->get_surface_width(rsx::surface_metrics::samples); + const auto surface_height = texptr->get_surface_height(rsx::surface_metrics::samples); + + bool is_depth = texptr->is_depth_surface(); + auto attr2 = attr; + + if (const bool gcm_format_is_depth = is_gcm_depth_format(attr2.gcm_format); + gcm_format_is_depth != is_depth) + { + if (force_convert) + { + is_depth = gcm_format_is_depth; + } + else + { + attr2.gcm_format = get_compatible_depth_format(attr2.gcm_format); + } + + // Always make sure the conflict is resolved! + verify(HERE), is_gcm_depth_format(attr2.gcm_format) == is_depth; + } + + if (LIKELY(extended_dimension == rsx::texture_dimension_extended::texture_dimension_2d || + extended_dimension == rsx::texture_dimension_extended::texture_dimension_1d)) + { + if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_1d) + { + verify(HERE), attr.height == 1; + } + + if ((surface_is_rop_target && g_cfg.video.strict_rendering_mode) || + attr2.width < surface_width || + attr2.height < surface_height || + force_convert) + { + const auto scaled_w = rsx::apply_resolution_scale(attr2.width, true); + const auto scaled_h = rsx::apply_resolution_scale(attr2.height, true); + + const auto format_class = (force_convert) ? get_format_class(attr2.gcm_format) : texptr->get_format_type(); + const auto command = surface_is_rop_target ? deferred_request_command::copy_image_dynamic : deferred_request_command::copy_image_static; + + attr2.width = scaled_w; + attr2.height = scaled_h; + + return { texptr->get_surface(rsx::surface_access::read), command, attr2, {}, + texture_upload_context::framebuffer_storage, format_class, scale, + extended_dimension, decoded_remap }; + } + + return{ texptr->get_view(encoded_remap, decoded_remap), texture_upload_context::framebuffer_storage, + texptr->get_format_type(), scale, rsx::texture_dimension_extended::texture_dimension_2d, surface_is_rop_target }; + } + + const auto scaled_w = rsx::apply_resolution_scale(attr2.width, true); + const auto scaled_h = rsx::apply_resolution_scale(attr2.height, true); + + if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_3d) + { + return{ texptr->get_surface(rsx::surface_access::read), deferred_request_command::_3d_unwrap, + attr2, {}, + texture_upload_context::framebuffer_storage, texptr->get_format_type(), scale, + rsx::texture_dimension_extended::texture_dimension_3d, decoded_remap }; + } + + verify(HERE), extended_dimension == rsx::texture_dimension_extended::texture_dimension_cubemap; + + attr2.width = scaled_w; + attr2.height = scaled_h; + + return{ texptr->get_surface(rsx::surface_access::read), deferred_request_command::cubemap_unwrap, + attr2, {}, + texture_upload_context::framebuffer_storage, texptr->get_format_type(), scale, + rsx::texture_dimension_extended::texture_dimension_cubemap, decoded_remap }; + } + + template + sampled_image_descriptor merge_cache_resources( + const surface_store_list_type& fbos, const std::vector& local, + const image_section_attributes_t& attr, + const size2f& scale, + texture_dimension_extended extended_dimension, + u32 encoded_remap, const texture_channel_remap_t& decoded_remap, + int select_hint = -1) + { + verify(HERE), (select_hint & 0x1) == select_hint; + + bool is_depth; + auto attr2 = attr; + + if (is_depth = (select_hint == 0) ? fbos.back().is_depth : local.back()->is_depth_texture(); + is_depth) + { + if (const auto suggested_format = get_compatible_depth_format(attr.gcm_format); + !is_gcm_depth_format(suggested_format)) + { + // Failed! + is_depth = false; + } + else + { + attr2.gcm_format = suggested_format; + } + } + + // If this method was called, there is no easy solution, likely means atlas gather is needed + auto scaled_w = rsx::apply_resolution_scale(attr2.width, true); + auto scaled_h = rsx::apply_resolution_scale(attr2.height, true); + const auto format_class = get_format_class(attr2.gcm_format); + + if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_cubemap) + { + attr2.width = scaled_w; + attr2.height = scaled_h; + attr2.depth = 1; + + sampled_image_descriptor desc = { nullptr, deferred_request_command::cubemap_gather, + attr2, {}, + texture_upload_context::framebuffer_storage, format_class, scale, + rsx::texture_dimension_extended::texture_dimension_cubemap, decoded_remap }; + + gather_texture_slices(desc.external_subresource_desc.sections_to_copy, fbos, local, attr, 6, is_depth); + return desc; + } + else if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_3d && attr.depth > 1) + { + attr2.width = scaled_w; + attr2.height = scaled_h; + + sampled_image_descriptor desc = { nullptr, deferred_request_command::_3d_gather, + attr2, {}, + texture_upload_context::framebuffer_storage, format_class, scale, + rsx::texture_dimension_extended::texture_dimension_3d, decoded_remap }; + + gather_texture_slices(desc.external_subresource_desc.sections_to_copy, fbos, local, attr, attr.depth, is_depth); + return desc; + } + + if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_1d) + { + verify(HERE), attr.height == 1; + } + + if (!fbos.empty()) + { + attr2.width = scaled_w; + attr2.height = scaled_h; + } + + sampled_image_descriptor result = { nullptr, deferred_request_command::atlas_gather, + attr2, {}, texture_upload_context::framebuffer_storage, format_class, + scale, rsx::texture_dimension_extended::texture_dimension_2d, decoded_remap }; + + gather_texture_slices(result.external_subresource_desc.sections_to_copy, fbos, local, attr, 1, is_depth); + result.simplify(); + return result; + } + }; +} diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.h b/rpcs3/Emu/RSX/GL/GLTextureCache.h index 729beb4c5c..75eb0fa6a9 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.h +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.h @@ -487,7 +487,7 @@ namespace gl } gl::texture_view* create_temporary_subresource_impl(gl::command_context& cmd, gl::texture* src, GLenum sized_internal_fmt, GLenum dst_type, u32 gcm_format, - u16 x, u16 y, u16 width, u16 height, const texture_channel_remap_t& remap, bool copy) + u16 x, u16 y, u16 width, u16 height, const rsx::texture_channel_remap_t& remap, bool copy) { if (sized_internal_fmt == GL_NONE) { @@ -501,7 +501,7 @@ namespace gl std::vector region = {{ src, - surface_transform::coordinate_transform, + rsx::surface_transform::coordinate_transform, x, y, 0, 0, 0, width, height, width, height }}; @@ -584,7 +584,7 @@ namespace gl auto src_w = slice.src_w; auto src_h = slice.src_h; - if (slice.xform == surface_transform::coordinate_transform) + if (slice.xform == rsx::surface_transform::coordinate_transform) { // Dimensions were given in 'dst' space. Work out the real source coordinates const auto src_bpp = slice.src->pitch() / slice.src->width(); @@ -691,19 +691,19 @@ namespace gl protected: gl::texture_view* create_temporary_subresource_view(gl::command_context &cmd, gl::texture** src, u32 gcm_format, u16 x, u16 y, u16 w, u16 h, - const texture_channel_remap_t& remap_vector) override + const rsx::texture_channel_remap_t& remap_vector) override { return create_temporary_subresource_impl(cmd, *src, GL_NONE, GL_TEXTURE_2D, gcm_format, x, y, w, h, remap_vector, true); } gl::texture_view* create_temporary_subresource_view(gl::command_context &cmd, gl::texture* src, u32 gcm_format, u16 x, u16 y, u16 w, u16 h, - const texture_channel_remap_t& remap_vector) override + const rsx::texture_channel_remap_t& remap_vector) override { return create_temporary_subresource_impl(cmd, src, (GLenum)src->get_internal_format(), GL_TEXTURE_2D, gcm_format, x, y, w, h, remap_vector, true); } - gl::texture_view* generate_cubemap_from_images(gl::command_context& cmd, u32 gcm_format, u16 size, const std::vector& sources, const texture_channel_remap_t& /*remap_vector*/) override + gl::texture_view* generate_cubemap_from_images(gl::command_context& cmd, u32 gcm_format, u16 size, const std::vector& sources, const rsx::texture_channel_remap_t& /*remap_vector*/) override { const GLenum ifmt = gl::get_sized_internal_format(gcm_format); std::unique_ptr dst_image = std::make_unique(GL_TEXTURE_CUBE_MAP, size, size, 1, 1, ifmt); @@ -725,7 +725,7 @@ namespace gl return result; } - gl::texture_view* generate_3d_from_2d_images(gl::command_context& cmd, u32 gcm_format, u16 width, u16 height, u16 depth, const std::vector& sources, const texture_channel_remap_t& /*remap_vector*/) override + gl::texture_view* generate_3d_from_2d_images(gl::command_context& cmd, u32 gcm_format, u16 width, u16 height, u16 depth, const std::vector& sources, const rsx::texture_channel_remap_t& /*remap_vector*/) override { const GLenum ifmt = gl::get_sized_internal_format(gcm_format); std::unique_ptr dst_image = std::make_unique(GL_TEXTURE_3D, width, height, depth, 1, ifmt); @@ -748,7 +748,7 @@ namespace gl } gl::texture_view* generate_atlas_from_images(gl::command_context& cmd, u32 gcm_format, u16 width, u16 height, const std::vector& sections_to_copy, - const texture_channel_remap_t& remap_vector) override + const rsx::texture_channel_remap_t& remap_vector) override { auto _template = get_template_from_collection_impl(sections_to_copy); auto result = create_temporary_subresource_impl(cmd, _template, GL_NONE, GL_TEXTURE_2D, gcm_format, 0, 0, width, height, remap_vector, false); @@ -762,7 +762,7 @@ namespace gl std::vector region = {{ src, - surface_transform::identity, + rsx::surface_transform::identity, 0, 0, 0, 0, 0, width, height, width, height }}; diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.h b/rpcs3/Emu/RSX/VK/VKTextureCache.h index d44af8c4ed..ff481d6f46 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.h +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.h @@ -519,7 +519,7 @@ namespace vk m_temporary_memory_size = 0; } - VkComponentMapping apply_component_mapping_flags(u32 gcm_format, rsx::texture_create_flags flags, const texture_channel_remap_t& remap_vector) const + VkComponentMapping apply_component_mapping_flags(u32 gcm_format, rsx::texture_create_flags flags, const rsx::texture_channel_remap_t& remap_vector) const { switch (gcm_format) { @@ -580,14 +580,14 @@ namespace vk auto src_h = section.src_h; rsx::flags32_t transform = section.xform; - if (section.xform == surface_transform::coordinate_transform) + if (section.xform == rsx::surface_transform::coordinate_transform) { // Dimensions were given in 'dst' space. Work out the real source coordinates const auto src_bpp = vk::get_format_texel_width(section.src->format()); src_x = (src_x * dst_bpp) / src_bpp; src_w = (src_w * dst_bpp) / src_bpp; - transform &= ~(surface_transform::coordinate_transform); + transform &= ~(rsx::surface_transform::coordinate_transform); } if (auto surface = dynamic_cast(section.src)) @@ -618,7 +618,7 @@ namespace vk // Final aspect mask of the 'final' transfer source const auto new_src_aspect = src_image->aspect(); - if (LIKELY(src_w == section.dst_w && src_h == section.dst_h && transform == surface_transform::identity)) + if (LIKELY(src_w == section.dst_w && src_h == section.dst_h && transform == rsx::surface_transform::identity)) { VkImageCopy copy_rgn; copy_rgn.srcOffset = { src_x, src_y, 0 }; @@ -657,7 +657,7 @@ namespace vk _dst->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } - if (transform == surface_transform::identity) + if (transform == rsx::surface_transform::identity) { vk::copy_scaled_image(cmd, src_image->value, _dst->value, section.src->current_layout, _dst->current_layout, coordi{ { src_x, src_y }, { src_w, src_h } }, @@ -665,7 +665,7 @@ namespace vk 1, src_image->aspect(), src_image->info.format == _dst->info.format, VK_FILTER_NEAREST, src_image->info.format, _dst->info.format); } - else if (transform == surface_transform::argb_to_bgra) + else if (transform == rsx::surface_transform::argb_to_bgra) { VkBufferImageCopy copy{}; copy.imageExtent = { src_w, src_h, 1 }; @@ -805,7 +805,7 @@ namespace vk protected: vk::image_view* create_temporary_subresource_view_impl(vk::command_buffer& cmd, vk::image* source, VkImageType image_type, VkImageViewType view_type, - u32 gcm_format, u16 x, u16 y, u16 w, u16 h, const texture_channel_remap_t& remap_vector, bool copy) + u32 gcm_format, u16 x, u16 y, u16 w, u16 h, const rsx::texture_channel_remap_t& remap_vector, bool copy) { std::unique_ptr image; @@ -854,7 +854,7 @@ namespace vk std::vector region = {{ source, - surface_transform::coordinate_transform, + rsx::surface_transform::coordinate_transform, x, y, 0, 0, 0, w, h, w, h }}; @@ -873,20 +873,20 @@ namespace vk } vk::image_view* create_temporary_subresource_view(vk::command_buffer& cmd, vk::image* source, u32 gcm_format, - u16 x, u16 y, u16 w, u16 h, const texture_channel_remap_t& remap_vector) override + u16 x, u16 y, u16 w, u16 h, const rsx::texture_channel_remap_t& remap_vector) override { return create_temporary_subresource_view_impl(cmd, source, source->info.imageType, VK_IMAGE_VIEW_TYPE_2D, gcm_format, x, y, w, h, remap_vector, true); } vk::image_view* create_temporary_subresource_view(vk::command_buffer& cmd, vk::image** source, u32 gcm_format, - u16 x, u16 y, u16 w, u16 h, const texture_channel_remap_t& remap_vector) override + u16 x, u16 y, u16 w, u16 h, const rsx::texture_channel_remap_t& remap_vector) override { return create_temporary_subresource_view(cmd, *source, gcm_format, x, y, w, h, remap_vector); } vk::image_view* generate_cubemap_from_images(vk::command_buffer& cmd, u32 gcm_format, u16 size, - const std::vector& sections_to_copy, const texture_channel_remap_t& /*remap_vector*/) override + const std::vector& sections_to_copy, const rsx::texture_channel_remap_t& /*remap_vector*/) override { std::unique_ptr image; VkFormat dst_format = vk::get_compatible_sampler_format(m_formats_support, gcm_format); @@ -938,7 +938,7 @@ namespace vk } vk::image_view* generate_3d_from_2d_images(vk::command_buffer& cmd, u32 gcm_format, u16 width, u16 height, u16 depth, - const std::vector& sections_to_copy, const texture_channel_remap_t& /*remap_vector*/) override + const std::vector& sections_to_copy, const rsx::texture_channel_remap_t& /*remap_vector*/) override { std::unique_ptr image; VkFormat dst_format = vk::get_compatible_sampler_format(m_formats_support, gcm_format); @@ -990,7 +990,7 @@ namespace vk } vk::image_view* generate_atlas_from_images(vk::command_buffer& cmd, u32 gcm_format, u16 width, u16 height, - const std::vector& sections_to_copy, const texture_channel_remap_t& remap_vector) override + const std::vector& sections_to_copy, const rsx::texture_channel_remap_t& remap_vector) override { auto _template = get_template_from_collection_impl(sections_to_copy); auto result = create_temporary_subresource_view_impl(cmd, _template, VK_IMAGE_TYPE_2D, @@ -1021,9 +1021,9 @@ namespace vk void update_image_contents(vk::command_buffer& cmd, vk::image_view* dst_view, vk::image* src, u16 width, u16 height) override { std::vector region = - {{ + { { src, - surface_transform::identity, + rsx::surface_transform::identity, 0, 0, 0, 0, 0, width, height, width, height }}; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index d4282717c8..5293f2abc8 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -383,6 +383,7 @@ + @@ -641,4 +642,4 @@ - + \ No newline at end of file diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 230ece44d4..0431e61eff 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -824,6 +824,12 @@ Emu\Io + + Source Files + + + Source Files + @@ -1027,9 +1033,6 @@ Utilities - - Utilities - Emu\GPU\RSX\Common @@ -1561,5 +1564,11 @@ Emu\Io + + Header Files + + + Emu\GPU\RSX\Common + \ No newline at end of file