diff --git a/rpcs3/Emu/RSX/Common/surface_store.cpp b/rpcs3/Emu/RSX/Common/surface_store.cpp new file mode 100644 index 0000000000..5a9b313c23 --- /dev/null +++ b/rpcs3/Emu/RSX/Common/surface_store.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include "surface_store.h" + +namespace rsx +{ + namespace utility + { + std::vector get_rtt_indexes(Surface_target color_target) + { + switch (color_target) + { + case Surface_target::none: return{}; + case Surface_target::surface_a: return{ 0 }; + case Surface_target::surface_b: return{ 1 }; + case Surface_target::surfaces_a_b: return{ 0, 1 }; + case Surface_target::surfaces_a_b_c: return{ 0, 1, 2 }; + case Surface_target::surfaces_a_b_c_d: return{ 0, 1, 2, 3 }; + } + throw EXCEPTION("Wrong color_target"); + } + + size_t get_aligned_pitch(Surface_color_format format, u32 width) + { + switch (format) + { + case Surface_color_format::b8: return align(width, 256); + case Surface_color_format::g8b8: + case Surface_color_format::x1r5g5b5_o1r5g5b5: + case Surface_color_format::x1r5g5b5_z1r5g5b5: + case Surface_color_format::r5g6b5: return align(width * 2, 256); + case Surface_color_format::a8b8g8r8: + case Surface_color_format::x8b8g8r8_o8b8g8r8: + case Surface_color_format::x8b8g8r8_z8b8g8r8: + case Surface_color_format::x8r8g8b8_o8r8g8b8: + case Surface_color_format::x8r8g8b8_z8r8g8b8: + case Surface_color_format::x32: + case Surface_color_format::a8r8g8b8: return align(width * 4, 256); + case Surface_color_format::w16z16y16x16: return align(width * 8, 256); + case Surface_color_format::w32z32y32x32: return align(width * 16, 256); + } + throw EXCEPTION("Unknow color surface format"); + } + + size_t get_packed_pitch(Surface_color_format format, u32 width) + { + switch (format) + { + case Surface_color_format::b8: return width; + case Surface_color_format::g8b8: + case Surface_color_format::x1r5g5b5_o1r5g5b5: + case Surface_color_format::x1r5g5b5_z1r5g5b5: + case Surface_color_format::r5g6b5: return width * 2; + case Surface_color_format::a8b8g8r8: + case Surface_color_format::x8b8g8r8_o8b8g8r8: + case Surface_color_format::x8b8g8r8_z8b8g8r8: + case Surface_color_format::x8r8g8b8_o8r8g8b8: + case Surface_color_format::x8r8g8b8_z8r8g8b8: + case Surface_color_format::x32: + case Surface_color_format::a8r8g8b8: return width * 4; + case Surface_color_format::w16z16y16x16: return width * 8; + case Surface_color_format::w32z32y32x32: return width * 16; + } + throw EXCEPTION("Unknow color surface format"); + } + } +} \ No newline at end of file diff --git a/rpcs3/Emu/RSX/Common/surface_store.h b/rpcs3/Emu/RSX/Common/surface_store.h new file mode 100644 index 0000000000..8361162a0a --- /dev/null +++ b/rpcs3/Emu/RSX/Common/surface_store.h @@ -0,0 +1,348 @@ +#pragma once + +#include +#include "../GCM.h" + +namespace rsx +{ + namespace utility + { + std::vector get_rtt_indexes(Surface_target color_target); + size_t get_aligned_pitch(Surface_color_format format, u32 width); + size_t get_packed_pitch(Surface_color_format format, u32 width); + } + + /** + * Helper for surface (ie color and depth stencil render target) management. + * It handles surface creation and storage. Backend should only retrieve pointer to surface. + * It provides 2 methods get_texture_from_*_if_applicable that should be used when an app + * wants to sample a previous surface. + * Please note that the backend is still responsible for creating framebuffer/descriptors + * and need to inform surface_store everytime surface format/size/addresses change. + * + * Since it's a template it requires a trait with the followings: + * - type surface_storage_type which is a structure containing texture. + * - type surface_type which is a pointer to storage_type or a reference. + * - type command_list_type that can be void for backend without command list + * - type download_buffer_object used by issue_download_command and map_downloaded_buffer functions to handle sync + * + * - a member function static surface_type(const surface_storage_type&) that returns underlying surface pointer from a storage type. + * - 2 member functions static surface_storage_type create_new_surface(u32 address, Surface_color_format/Surface_depth_format format, size_t width, size_t height,...) + * used to create a new surface_storage_type holding surface from passed parameters. + * - a member function static prepare_rtt_for_drawing(command_list, surface_type) that makes a sampleable surface a color render target one. + * - a member function static prepare_rtt_for_drawing(command_list, surface_type) that makes a render target surface a sampleable one. + * - a member function static prepare_ds_for_drawing that does the same for depth stencil surface. + * - a member function static prepare_ds_for_sampling that does the same for depth stencil surface. + * - a member function static bool rtt_has_format_width_height(const surface_storage_type&, Surface_color_format surface_color_format, size_t width, size_t height) + * that checks if the given surface has the given format and size + * - a member function static bool ds_has_format_width_height that does the same for ds + * - a member function static download_buffer_object issue_download_command(surface_type, Surface_color_format color_format, size_t width, size_t height,...) + * that generates command to download the given surface to some mappable buffer. + * - a member function static issue_depth_download_command that does the same for depth surface + * - a member function static issue_stencil_download_command that does the same for stencil surface + * - a member function gsl::span map_downloaded_buffer(download_buffer_object, ...) that maps a download_buffer_object + * - a member function static unmap_downloaded_buffer that unmaps it. + */ + template + struct surface_store + { + template + void copy_pitched_src_to_dst(gsl::span dest, gsl::span src, size_t src_pitch_in_bytes, size_t width, size_t height) + { + for (int row = 0; row < height; row++) + { + for (unsigned col = 0; col < width; col++) + dest[col] = src[col]; + src = src.subspan(src_pitch_in_bytes / sizeof(U)); + dest = dest.subspan(width); + } + } + + private: + using surface_storage_type = typename Traits::surface_storage_type; + using surface_type = typename Traits::surface_type; + using command_list_type = typename Traits::command_list_type; + using download_buffer_object = typename Traits::download_buffer_object; + + std::unordered_map m_render_targets_storage = {}; + std::unordered_map m_depth_stencil_storage = {}; + + public: + std::array, 4> m_bound_render_targets = {}; + std::tuple m_bound_depth_stencil = {}; + + std::list invalidated_resources; + + surface_store() = default; + ~surface_store() = default; + surface_store(const surface_store&) = delete; + private: + /** + * If render target already exists at address, issue state change operation on cmdList. + * Otherwise create one with width, height, clearColor info. + * returns the corresponding render target resource. + */ + template + gsl::not_null bind_address_as_render_targets( + command_list_type command_list, + u32 address, + Surface_color_format surface_color_format, size_t width, size_t height, + Args&&... extra_params) + { + auto It = m_render_targets_storage.find(address); + // TODO: Fix corner cases + // This doesn't take overlapping surface(s) into account. + // Invalidated surface(s) should also copy their content to the new resources. + if (It != m_render_targets_storage.end()) + { + surface_storage_type &rtt = It->second; + if (Traits::rtt_has_format_width_height(rtt, surface_color_format, width, height)) + { + Traits::prepare_rtt_for_drawing(command_list, Traits::get(rtt)); + return Traits::get(rtt); + } + invalidated_resources.push_back(std::move(rtt)); + m_render_targets_storage.erase(address); + } + + m_render_targets_storage[address] = Traits::create_new_surface(address, surface_color_format, width, height, std::forward(extra_params)...); + return Traits::get(m_render_targets_storage[address]); + } + + template + gsl::not_null bind_address_as_depth_stencil( + command_list_type command_list, + u32 address, + Surface_depth_format surface_depth_format, size_t width, size_t height, + Args&&... extra_params) + { + auto It = m_depth_stencil_storage.find(address); + if (It != m_depth_stencil_storage.end()) + { + surface_storage_type &ds = It->second; + if (Traits::ds_has_format_width_height(ds, surface_depth_format, width, height)) + { + Traits::prepare_ds_for_drawing(command_list, Traits::get(ds)); + return Traits::get(ds); + } + invalidated_resources.push_back(std::move(ds)); + m_depth_stencil_storage.erase(address); + } + + m_depth_stencil_storage[address] = Traits::create_new_surface(address, surface_depth_format, width, height, std::forward(extra_params)...); + return Traits::get(m_depth_stencil_storage[address]); + } + public: + /** + * Update bound color and depth surface. + * Must be called everytime surface format, clip, or addresses changes. + */ + template + void prepare_render_target( + command_list_type command_list, + u32 set_surface_format_reg, + u32 clip_horizontal_reg, u32 clip_vertical_reg, + Surface_target set_surface_target, + const std::array &surface_addresses, u32 address_z, + Args&&... extra_params) + { + u32 clip_width = clip_horizontal_reg >> 16; + u32 clip_height = clip_vertical_reg >> 16; + u32 clip_x = clip_horizontal_reg; + u32 clip_y = clip_vertical_reg; + + Surface_color_format color_format = to_surface_color_format(set_surface_format_reg & 0x1f); + Surface_depth_format depth_format = to_surface_depth_format((set_surface_format_reg >> 5) & 0x7); + + // Make previous RTTs sampleable + for (std::tuple &rtt : m_bound_render_targets) + { + if (std::get<1>(rtt) != nullptr) + Traits::prepare_rtt_for_sampling(command_list, std::get<1>(rtt)); + rtt = std::make_tuple(0, nullptr); + } + + // Create/Reuse requested rtts + for (u8 surface_index : utility::get_rtt_indexes(set_surface_target)) + { + if (surface_addresses[surface_index] == 0) + continue; + + m_bound_render_targets[surface_index] = std::make_tuple(surface_addresses[surface_index], + bind_address_as_render_targets(command_list, surface_addresses[surface_index], color_format, clip_width, clip_height, std::forward(extra_params)...)); + } + + // Same for depth buffer + if (std::get<1>(m_bound_depth_stencil) != nullptr) + Traits::prepare_ds_for_sampling(command_list, std::get<1>(m_bound_depth_stencil)); + m_bound_depth_stencil = std::make_tuple(0, nullptr); + if (!address_z) + return; + m_bound_depth_stencil = std::make_tuple(address_z, + bind_address_as_depth_stencil(command_list, address_z, depth_format, clip_width, clip_height, std::forward(extra_params)...)); + } + + /** + * Search for given address in stored color surface and returns it if size/format match. + * Return an empty surface_type otherwise. + */ + surface_type get_texture_from_render_target_if_applicable(u32 address) + { + // TODO: Handle texture that overlaps one (or several) surface. + // Handle texture conversion + // FIXME: Disgaea 3 loading screen seems to use a subset of a surface. It's not properly handled here. + // Note: not const because conversions/resolve/... can happen + auto It = m_render_targets_storage.find(address); + if (It != m_render_targets_storage.end()) + return Traits::get(It->second); + return surface_type(); + } + + /** + * Search for given address in stored depth stencil surface and returns it if size/format match. + * Return an empty surface_type otherwise. + */ + surface_type get_texture_from_depth_stencil_if_applicable(u32 address) + { + // TODO: Same as above although there wasn't any game using corner case for DS yet. + auto It = m_depth_stencil_storage.find(address); + if (It != m_depth_stencil_storage.end()) + return Traits::get(It->second); + return surface_type(); + } + + /** + * Get bound color surface raw data. + */ + template + std::array, 4> get_render_targets_data( + Surface_color_format surface_color_format, size_t width, size_t height, + Args&& ...args + ) + { + std::array download_data = {}; + + // Issue download commands + for (int i = 0; i < 4; i++) + { + if (std::get<0>(m_bound_render_targets[i]) == 0) + continue; + + surface_type surface_resource = std::get<1>(m_bound_render_targets[i]); + download_data[i] = std::move( + Traits::issue_download_command(surface_resource, surface_color_format, width, height, std::forward(args)...) + ); + } + + std::array, 4> result = {}; + + // Sync and copy data + for (int i = 0; i < 4; i++) + { + if (std::get<0>(m_bound_render_targets[i]) == 0) + continue; + + gsl::span raw_src = Traits::map_downloaded_buffer(download_data[i], std::forward(args)...); + + size_t src_pitch = utility::get_aligned_pitch(surface_color_format, gsl::narrow(width)); + size_t dst_pitch = utility::get_packed_pitch(surface_color_format, gsl::narrow(width)); + + result[i].resize(dst_pitch * height); + + // Note: MSVC + GSL doesn't support span -> span for non const span atm + // thus manual conversion + switch (surface_color_format) + { + case Surface_color_format::a8b8g8r8: + case Surface_color_format::x8b8g8r8_o8b8g8r8: + case Surface_color_format::x8b8g8r8_z8b8g8r8: + case Surface_color_format::a8r8g8b8: + case Surface_color_format::x8r8g8b8_o8r8g8b8: + case Surface_color_format::x8r8g8b8_z8r8g8b8: + case Surface_color_format::x32: + { + gsl::span> dst_span{ (be_t*)result[i].data(), gsl::narrow(dst_pitch * width / sizeof(be_t)) }; + copy_pitched_src_to_dst(dst_span, gsl::as_span(raw_src), src_pitch, width, height); + break; + } + case Surface_color_format::b8: + { + gsl::span dst_span{ (u8*)result[i].data(), gsl::narrow(dst_pitch * width / sizeof(u8)) }; + copy_pitched_src_to_dst(dst_span, gsl::as_span(raw_src), src_pitch, width, height); + break; + } + case Surface_color_format::g8b8: + case Surface_color_format::r5g6b5: + case Surface_color_format::x1r5g5b5_o1r5g5b5: + case Surface_color_format::x1r5g5b5_z1r5g5b5: + { + gsl::span> dst_span{ (be_t*)result[i].data(), gsl::narrow(dst_pitch * width / sizeof(be_t)) }; + copy_pitched_src_to_dst(dst_span, gsl::as_span(raw_src), src_pitch, width, height); + break; + } + // Note : may require some big endian swap + case Surface_color_format::w32z32y32x32: + { + gsl::span dst_span{ (u128*)result[i].data(), gsl::narrow(dst_pitch * width / sizeof(u128)) }; + copy_pitched_src_to_dst(dst_span, gsl::as_span(raw_src), src_pitch, width, height); + break; + } + case Surface_color_format::w16z16y16x16: + { + gsl::span dst_span{ (u64*)result[i].data(), gsl::narrow(dst_pitch * width / sizeof(u64)) }; + copy_pitched_src_to_dst(dst_span, gsl::as_span(raw_src), src_pitch, width, height); + break; + } + + } + Traits::unmap_downloaded_buffer(download_data[i], std::forward(args)...); + } + return result; + } + + /** + * Get bound color surface raw data. + */ + template + std::array, 2> get_depth_stencil_data( + Surface_depth_format surface_depth_format, size_t width, size_t height, + Args&& ...args + ) + { + std::array, 2> result = {}; + if (std::get<0>(m_bound_depth_stencil) == 0) + return result; + size_t row_pitch = align(width * 4, 256); + + download_buffer_object stencil_data = {}; + download_buffer_object depth_data = Traits::issue_depth_download_command(std::get<1>(m_bound_depth_stencil), surface_depth_format, width, height, std::forward(args)...); + if (surface_depth_format == Surface_depth_format::z24s8) + stencil_data = std::move(Traits::issue_stencil_download_command(std::get<1>(m_bound_depth_stencil), width, height, std::forward(args)...)); + + gsl::span depth_buffer_raw_src = Traits::map_downloaded_buffer(depth_data, std::forward(args)...); + if (surface_depth_format == Surface_depth_format::z16) + { + result[0].resize(width * height * 2); + gsl::span dest{ (u16*)result[0].data(), gsl::narrow(width * height) }; + copy_pitched_src_to_dst(dest, gsl::as_span(depth_buffer_raw_src), row_pitch, width, height); + } + if (surface_depth_format == Surface_depth_format::z24s8) + { + result[0].resize(width * height * 4); + gsl::span dest{ (u32*)result[0].data(), gsl::narrow(width * height) }; + copy_pitched_src_to_dst(dest, gsl::as_span(depth_buffer_raw_src), row_pitch, width, height); + } + Traits::unmap_downloaded_buffer(depth_data, std::forward(args)...); + + if (surface_depth_format == Surface_depth_format::z16) + return result; + + gsl::span stencil_buffer_raw_src = Traits::map_downloaded_buffer(stencil_data, std::forward(args)...); + result[1].resize(width * height); + gsl::span dest{ (u8*)result[1].data(), gsl::narrow(width * height) }; + copy_pitched_src_to_dst(dest, gsl::as_span(stencil_buffer_raw_src), align(width, 256), width, height); + Traits::unmap_downloaded_buffer(stencil_data, std::forward(args)...); + return result; + } + }; +} \ No newline at end of file diff --git a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h index f9618600c4..a7e34fcffe 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h +++ b/rpcs3/Emu/RSX/D3D12/D3D12GSRender.h @@ -116,7 +116,7 @@ private: data_heap m_buffer_data; data_heap m_readback_resources; - render_targets m_rtts; + rsx::render_targets m_rtts; std::vector m_IASet; @@ -127,13 +127,6 @@ private: // Used to fill unused texture slot ID3D12Resource *m_dummy_texture; - - // Store previous fbo addresses to detect RTT config changes. - std::array m_previous_color_address = {}; - u32 m_previous_address_z = 0; - u32 m_previous_target = 0; - u32 m_previous_clip_horizontal = 0; - u32 m_previous_clip_vertical = 0; public: D3D12GSRender(); virtual ~D3D12GSRender(); diff --git a/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.cpp b/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.cpp index 62e8ee8987..3edf99d548 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.cpp +++ b/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.cpp @@ -171,58 +171,23 @@ void D3D12GSRender::prepare_render_targets(ID3D12GraphicsCommandList *copycmdlis { // check if something has changed u32 surface_format = rsx::method_registers[NV4097_SET_SURFACE_FORMAT]; - u32 context_dma_color[] = - { - rsx::method_registers[NV4097_SET_CONTEXT_DMA_COLOR_A], - rsx::method_registers[NV4097_SET_CONTEXT_DMA_COLOR_B], - rsx::method_registers[NV4097_SET_CONTEXT_DMA_COLOR_C], - rsx::method_registers[NV4097_SET_CONTEXT_DMA_COLOR_D] - }; - u32 m_context_dma_z = rsx::method_registers[NV4097_SET_CONTEXT_DMA_ZETA]; - - u32 offset_color[] = - { - rsx::method_registers[NV4097_SET_SURFACE_COLOR_AOFFSET], - rsx::method_registers[NV4097_SET_SURFACE_COLOR_BOFFSET], - rsx::method_registers[NV4097_SET_SURFACE_COLOR_COFFSET], - rsx::method_registers[NV4097_SET_SURFACE_COLOR_DOFFSET] - }; - u32 offset_zeta = rsx::method_registers[NV4097_SET_SURFACE_ZETA_OFFSET]; - - // FBO location has changed, previous data might be copied - std::array address_color = - { - rsx::get_address(offset_color[0], context_dma_color[0]), - rsx::get_address(offset_color[1], context_dma_color[1]), - rsx::get_address(offset_color[2], context_dma_color[2]), - rsx::get_address(offset_color[3], context_dma_color[3]), - }; - u32 address_z = rsx::get_address(offset_zeta, m_context_dma_z); - - u32 clip_h_reg = rsx::method_registers[NV4097_SET_SURFACE_CLIP_HORIZONTAL]; - u32 clip_v_reg = rsx::method_registers[NV4097_SET_SURFACE_CLIP_VERTICAL]; - u32 target_reg = rsx::method_registers[NV4097_SET_SURFACE_COLOR_TARGET]; // Exit early if there is no rtt changes - if (m_previous_color_address == address_color && - m_previous_address_z == address_z && - m_surface.format == surface_format && - m_previous_clip_horizontal == clip_h_reg && - m_previous_clip_vertical == clip_v_reg && - m_previous_target == target_reg) + if (!m_rtts_dirty) return; + m_rtts_dirty = false; - m_previous_color_address = address_color; - m_previous_address_z = address_z; - m_previous_target = target_reg; - m_previous_clip_horizontal = clip_h_reg; - m_previous_clip_vertical = clip_v_reg; if (m_surface.format != surface_format) m_surface.unpack(surface_format); std::array clear_color = get_clear_color(rsx::method_registers[NV4097_SET_COLOR_CLEAR_VALUE]); - m_rtts.prepare_render_target(copycmdlist, surface_format, clip_h_reg, clip_v_reg, to_surface_target(target_reg), address_color, address_z, m_device.Get(), clear_color, 1.f, 0); + m_rtts.prepare_render_target(copycmdlist, + rsx::method_registers[NV4097_SET_SURFACE_FORMAT], + rsx::method_registers[NV4097_SET_SURFACE_CLIP_HORIZONTAL], rsx::method_registers[NV4097_SET_SURFACE_CLIP_VERTICAL], + to_surface_target(rsx::method_registers[NV4097_SET_SURFACE_COLOR_TARGET]), + get_color_surface_addresses(), get_zeta_surface_address(), + m_device.Get(), clear_color, 1.f, 0); // write descriptors DXGI_FORMAT dxgi_format = get_color_surface_format(m_surface.color_format); @@ -261,7 +226,7 @@ void D3D12GSRender::set_rtt_and_ds(ID3D12GraphicsCommandList *command_list) command_list->OMSetRenderTargets((UINT)num_rtt, &m_rtts.current_rtts_handle, true, ds_handle); } -void render_targets::init(ID3D12Device *device) +void rsx::render_targets::init(ID3D12Device *device) { g_descriptor_stride_rtv = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); } diff --git a/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.h b/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.h index 9eeb332c96..df76dc8a9e 100644 --- a/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.h +++ b/rpcs3/Emu/RSX/D3D12/D3D12RenderTargetSets.h @@ -6,356 +6,10 @@ #include "D3D12Formats.h" #include "D3D12MemoryHelpers.h" -#include +#include "../Common/surface_store.h" namespace rsx { - namespace - { - std::vector get_rtt_indexes(Surface_target color_target) - { - switch (color_target) - { - case Surface_target::none: return{}; - case Surface_target::surface_a: return{ 0 }; - case Surface_target::surface_b: return{ 1 }; - case Surface_target::surfaces_a_b: return{ 0, 1 }; - case Surface_target::surfaces_a_b_c: return{ 0, 1, 2 }; - case Surface_target::surfaces_a_b_c_d: return{ 0, 1, 2, 3 }; - } - throw EXCEPTION("Wrong color_target"); - } - - template - void copy_pitched_src_to_dst(gsl::span dest, gsl::span src, size_t src_pitch_in_bytes, size_t width, size_t height) - { - for (int row = 0; row < height; row++) - { - for (unsigned col = 0; col < width; col++) - dest[col] = src[col]; - src = src.subspan(src_pitch_in_bytes / sizeof(U)); - dest = dest.subspan(width); - } - } - - size_t get_aligned_pitch(Surface_color_format format, u32 width) - { - switch (format) - { - case Surface_color_format::b8: return align(width, 256); - case Surface_color_format::g8b8: - case Surface_color_format::x1r5g5b5_o1r5g5b5: - case Surface_color_format::x1r5g5b5_z1r5g5b5: - case Surface_color_format::r5g6b5: return align(width * 2, 256); - case Surface_color_format::a8b8g8r8: - case Surface_color_format::x8b8g8r8_o8b8g8r8: - case Surface_color_format::x8b8g8r8_z8b8g8r8: - case Surface_color_format::x8r8g8b8_o8r8g8b8: - case Surface_color_format::x8r8g8b8_z8r8g8b8: - case Surface_color_format::x32: - case Surface_color_format::a8r8g8b8: return align(width * 4, 256); - case Surface_color_format::w16z16y16x16: return align(width * 8, 256); - case Surface_color_format::w32z32y32x32: return align(width * 16, 256); - } - throw EXCEPTION("Unknow color surface format"); - } - - size_t get_packed_pitch(Surface_color_format format, u32 width) - { - switch (format) - { - case Surface_color_format::b8: return width; - case Surface_color_format::g8b8: - case Surface_color_format::x1r5g5b5_o1r5g5b5: - case Surface_color_format::x1r5g5b5_z1r5g5b5: - case Surface_color_format::r5g6b5: return width * 2; - case Surface_color_format::a8b8g8r8: - case Surface_color_format::x8b8g8r8_o8b8g8r8: - case Surface_color_format::x8b8g8r8_z8b8g8r8: - case Surface_color_format::x8r8g8b8_o8r8g8b8: - case Surface_color_format::x8r8g8b8_z8r8g8b8: - case Surface_color_format::x32: - case Surface_color_format::a8r8g8b8: return width * 4; - case Surface_color_format::w16z16y16x16: return width * 8; - case Surface_color_format::w32z32y32x32: return width * 16; - } - throw EXCEPTION("Unknow color surface format"); - } - } - - template - struct surface_store - { - private: - using surface_storage_type = typename Traits::surface_storage_type; - using surface_type = typename Traits::surface_type; - using command_list_type = typename Traits::command_list_type; - using download_buffer_object = typename Traits::download_buffer_object; - - std::unordered_map m_render_targets_storage = {}; - std::unordered_map m_depth_stencil_storage = {}; - - public: - std::array, 4> m_bound_render_targets = {}; - std::tuple m_bound_depth_stencil = {}; - - std::list invalidated_resources; - - surface_store() = default; - ~surface_store() = default; - surface_store(const surface_store&) = delete; - private: - /** - * If render target already exists at address, issue state change operation on cmdList. - * Otherwise create one with width, height, clearColor info. - * returns the corresponding render target resource. - */ - template - gsl::not_null bind_address_as_render_targets( - command_list_type command_list, - u32 address, - Surface_color_format surface_color_format, size_t width, size_t height, - Args&&... extra_params) - { - auto It = m_render_targets_storage.find(address); - // TODO: Fix corner cases - // This doesn't take overlapping surface(s) into account. - // Invalidated surface(s) should also copy their content to the new resources. - if (It != m_render_targets_storage.end()) - { - surface_storage_type &rtt = It->second; - if (Traits::rtt_has_format_width_height(rtt, surface_color_format, width, height)) - { - Traits::prepare_rtt_for_drawing(command_list, rtt.Get()); - return rtt.Get(); - } - invalidated_resources.push_back(std::move(rtt)); - m_render_targets_storage.erase(address); - } - - m_render_targets_storage[address] = Traits::create_new_surface(address, surface_color_format, width, height, std::forward(extra_params)...); - return m_render_targets_storage[address].Get(); - } - - template - gsl::not_null bind_address_as_depth_stencil( - command_list_type command_list, - u32 address, - Surface_depth_format surface_depth_format, size_t width, size_t height, - Args&&... extra_params) - { - auto It = m_depth_stencil_storage.find(address); - if (It != m_depth_stencil_storage.end()) - { - surface_storage_type &ds = It->second; - if (Traits::ds_has_format_width_height(ds, surface_depth_format, width, height)) - { - Traits::prepare_ds_for_drawing(command_list, ds.Get()); - return ds.Get(); - } - invalidated_resources.push_back(std::move(ds)); - m_depth_stencil_storage.erase(address); - } - - m_depth_stencil_storage[address] = Traits::create_new_surface(address, surface_depth_format, width, height, std::forward(extra_params)...); - return m_depth_stencil_storage[address].Get(); - } - public: - template - void prepare_render_target( - command_list_type command_list, - u32 set_surface_format_reg, - u32 clip_horizontal_reg, u32 clip_vertical_reg, - Surface_target set_surface_target, - const std::array &surface_addresses, u32 address_z, - Args&&... extra_params) - { - u32 clip_width = clip_horizontal_reg >> 16; - u32 clip_height = clip_vertical_reg >> 16; - u32 clip_x = clip_horizontal_reg; - u32 clip_y = clip_vertical_reg; - - rsx::surface_info surface = {}; - surface.unpack(set_surface_format_reg); - - // Make previous RTTs sampleable - for (std::tuple &rtt : m_bound_render_targets) - { - if (std::get<1>(rtt) != nullptr) - Traits::prepare_rtt_for_sampling(command_list, std::get<1>(rtt)); - rtt = std::make_tuple(0, nullptr); - } - - // Create/Reuse requested rtts - for (u8 surface_index : get_rtt_indexes(set_surface_target)) - { - if (surface_addresses[surface_index] == 0) - continue; - - m_bound_render_targets[surface_index] = std::make_tuple(surface_addresses[surface_index], - bind_address_as_render_targets(command_list, surface_addresses[surface_index], surface.color_format, clip_width, clip_height, std::forward(extra_params)...)); - } - - // Same for depth buffer - if (std::get<1>(m_bound_depth_stencil) != nullptr) - Traits::prepare_ds_for_sampling(command_list, std::get<1>(m_bound_depth_stencil)); - m_bound_depth_stencil = std::make_tuple(0, nullptr); - if (!address_z) - return; - m_bound_depth_stencil = std::make_tuple(address_z, - bind_address_as_depth_stencil(command_list, address_z, surface.depth_format, clip_width, clip_height, std::forward(extra_params)...)); - } - - surface_type get_texture_from_render_target_if_applicable(u32 address) - { - // TODO: Handle texture that overlaps one (or several) surface. - // Handle texture conversion - // FIXME: Disgaea 3 loading screen seems to use a subset of a surface. It's not properly handled here. - // Note: not const because conversions/resolve/... can happen - auto It = m_render_targets_storage.find(address); - if (It != m_render_targets_storage.end()) - return It->second.Get(); - return surface_type(); - } - - surface_type get_texture_from_depth_stencil_if_applicable(u32 address) - { - // TODO: Same as above although there wasn't any game using corner case for DS yet. - auto It = m_depth_stencil_storage.find(address); - if (It != m_depth_stencil_storage.end()) - return It->second.Get(); - return surface_type(); - } - - template - std::array, 4> get_render_targets_data( - Surface_color_format surface_color_format, size_t width, size_t height, - Args&& ...args - ) - { - std::array download_data = {}; - - // Issue download commands - for (int i = 0; i < 4; i++) - { - if (std::get<0>(m_bound_render_targets[i]) == 0) - continue; - - surface_type surface_resource = std::get<1>(m_bound_render_targets[i]); - download_data[i] = std::move( - Traits::issue_download_command(surface_resource, surface_color_format, width, height, std::forward(args)...) - ); - } - - std::array, 4> result = {}; - - // Sync and copy data - for (int i = 0; i < 4; i++) - { - if (std::get<0>(m_bound_render_targets[i]) == 0) - continue; - - gsl::span raw_src = Traits::map_downloaded_buffer(download_data[i], std::forward(args)...); - - size_t src_pitch = get_aligned_pitch(surface_color_format, gsl::narrow(width)); - size_t dst_pitch = get_packed_pitch(surface_color_format, gsl::narrow(width)); - - result[i].resize(dst_pitch * height); - - // Note: MSVC + GSL doesn't support span -> span for non const span atm - // thus manual conversion - switch (surface_color_format) - { - case Surface_color_format::a8b8g8r8: - case Surface_color_format::x8b8g8r8_o8b8g8r8: - case Surface_color_format::x8b8g8r8_z8b8g8r8: - case Surface_color_format::a8r8g8b8: - case Surface_color_format::x8r8g8b8_o8r8g8b8: - case Surface_color_format::x8r8g8b8_z8r8g8b8: - case Surface_color_format::x32: - { - gsl::span> dst_span{ (be_t*)result[i].data(), gsl::narrow(dst_pitch * width / sizeof(be_t)) }; - copy_pitched_src_to_dst(dst_span, gsl::as_span(raw_src), src_pitch, width, height); - break; - } - case Surface_color_format::b8: - { - gsl::span dst_span{ (u8*)result[i].data(), gsl::narrow(dst_pitch * width / sizeof(u8)) }; - copy_pitched_src_to_dst(dst_span, gsl::as_span(raw_src), src_pitch, width, height); - break; - } - case Surface_color_format::g8b8: - case Surface_color_format::r5g6b5: - case Surface_color_format::x1r5g5b5_o1r5g5b5: - case Surface_color_format::x1r5g5b5_z1r5g5b5: - { - gsl::span> dst_span{ (be_t*)result[i].data(), gsl::narrow(dst_pitch * width / sizeof(be_t)) }; - copy_pitched_src_to_dst(dst_span, gsl::as_span(raw_src), src_pitch, width, height); - break; - } - // Note : may require some big endian swap - case Surface_color_format::w32z32y32x32: - { - gsl::span dst_span{ (u128*)result[i].data(), gsl::narrow(dst_pitch * width / sizeof(u128)) }; - copy_pitched_src_to_dst(dst_span, gsl::as_span(raw_src), src_pitch, width, height); - break; - } - case Surface_color_format::w16z16y16x16: - { - gsl::span dst_span{ (u64*)result[i].data(), gsl::narrow(dst_pitch * width / sizeof(u64)) }; - copy_pitched_src_to_dst(dst_span, gsl::as_span(raw_src), src_pitch, width, height); - break; - } - - } - Traits::unmap_downloaded_buffer(download_data[i], std::forward(args)...); - } - return result; - } - - template - std::array, 2> get_depth_stencil_data( - Surface_depth_format surface_depth_format, size_t width, size_t height, - Args&& ...args - ) - { - std::array, 2> result = {}; - if (std::get<0>(m_bound_depth_stencil) == 0) - return result; - size_t row_pitch = align(width * 4, 256); - - download_buffer_object stencil_data = {}; - download_buffer_object depth_data = Traits::issue_depth_download_command(std::get<1>(m_bound_depth_stencil), surface_depth_format, width, height, std::forward(args)...); - if (surface_depth_format == Surface_depth_format::z24s8) - stencil_data = std::move(Traits::issue_stencil_download_command(std::get<1>(m_bound_depth_stencil), width, height, std::forward(args)...)); - - gsl::span depth_buffer_raw_src = Traits::map_downloaded_buffer(depth_data, std::forward(args)...); - if (surface_depth_format == Surface_depth_format::z16) - { - result[0].resize(width * height * 2); - gsl::span dest{ (u16*)result[0].data(), gsl::narrow(width * height) }; - copy_pitched_src_to_dst(dest, gsl::as_span(depth_buffer_raw_src), row_pitch, width, height); - } - if (surface_depth_format == Surface_depth_format::z24s8) - { - result[0].resize(width * height * 4); - gsl::span dest{ (u32*)result[0].data(), gsl::narrow(width * height) }; - copy_pitched_src_to_dst(dest, gsl::as_span(depth_buffer_raw_src), row_pitch, width, height); - } - Traits::unmap_downloaded_buffer(depth_data, std::forward(args)...); - - if (surface_depth_format == Surface_depth_format::z16) - return result; - - gsl::span stencil_buffer_raw_src = Traits::map_downloaded_buffer(stencil_data, std::forward(args)...); - result[1].resize(width * height); - gsl::span dest{ (u8*)result[1].data(), gsl::narrow(width * height) }; - copy_pitched_src_to_dst(dest, gsl::as_span(stencil_buffer_raw_src), align(width, 256), width, height); - Traits::unmap_downloaded_buffer(stencil_data, std::forward(args)...); - return result; - } - }; -} struct render_target_traits { @@ -481,7 +135,7 @@ struct render_target_traits { ID3D12GraphicsCommandList* command_list = res_store.command_list.Get(); DXGI_FORMAT dxgi_format = get_color_surface_format(color_format); - size_t row_pitch = rsx::get_aligned_pitch(color_format, gsl::narrow(width)); + size_t row_pitch = rsx::utility::get_aligned_pitch(color_format, gsl::narrow(width)); size_t buffer_size = row_pitch * height; size_t heap_offset = readback_heap.alloc(buffer_size); @@ -593,6 +247,11 @@ struct render_target_traits { readback_heap.unmap(); } + + static ID3D12Resource* get(const ComPtr &in) + { + return in.Get(); + } }; struct render_targets : public rsx::surface_store @@ -605,3 +264,4 @@ struct render_targets : public rsx::surface_store void init(ID3D12Device *device); }; +} diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 87455f4718..37d7ee6b44 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -504,6 +504,38 @@ namespace rsx return get_system_time() * 1000; } + std::array thread::get_color_surface_addresses() const + { + u32 offset_color[] = + { + rsx::method_registers[NV4097_SET_SURFACE_COLOR_AOFFSET], + rsx::method_registers[NV4097_SET_SURFACE_COLOR_BOFFSET], + rsx::method_registers[NV4097_SET_SURFACE_COLOR_COFFSET], + rsx::method_registers[NV4097_SET_SURFACE_COLOR_DOFFSET] + }; + u32 context_dma_color[] = + { + rsx::method_registers[NV4097_SET_CONTEXT_DMA_COLOR_A], + rsx::method_registers[NV4097_SET_CONTEXT_DMA_COLOR_B], + rsx::method_registers[NV4097_SET_CONTEXT_DMA_COLOR_C], + rsx::method_registers[NV4097_SET_CONTEXT_DMA_COLOR_D] + }; + return + { + rsx::get_address(offset_color[0], context_dma_color[0]), + rsx::get_address(offset_color[1], context_dma_color[1]), + rsx::get_address(offset_color[2], context_dma_color[2]), + rsx::get_address(offset_color[3], context_dma_color[3]), + }; + } + + u32 thread::get_zeta_surface_address() const + { + u32 m_context_dma_z = rsx::method_registers[NV4097_SET_CONTEXT_DMA_ZETA]; + u32 offset_zeta = rsx::method_registers[NV4097_SET_SURFACE_ZETA_OFFSET]; + return rsx::get_address(offset_zeta, m_context_dma_z); + } + void thread::reset() { //setup method registers diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 434afb7005..aee737cb7c 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -287,6 +287,11 @@ namespace rsx bool draw_inline_vertex_array; std::vector inline_vertex_array; + bool m_rtts_dirty; + protected: + std::array get_color_surface_addresses() const; + u32 get_zeta_surface_address() const; + public: u32 draw_array_count; u32 draw_array_first; diff --git a/rpcs3/Emu/RSX/rsx_methods.cpp b/rpcs3/Emu/RSX/rsx_methods.cpp index 06e09d1db6..95af817e33 100644 --- a/rpcs3/Emu/RSX/rsx_methods.cpp +++ b/rpcs3/Emu/RSX/rsx_methods.cpp @@ -282,6 +282,11 @@ namespace rsx break; } } + + force_inline void set_surface_dirty_bit(thread* rsx, u32) + { + rsx->m_rtts_dirty = true; + } } namespace nv308a @@ -786,6 +791,16 @@ namespace rsx bind(); bind(); + /* + + // Store previous fbo addresses to detect RTT config changes. + std::array m_previous_color_address = {}; + u32 m_previous_address_z = 0; + u32 m_previous_target = 0; + u32 m_previous_clip_horizontal = 0; + u32 m_previous_clip_vertical = 0; + */ + // NV4097 bind(); bind(); @@ -806,6 +821,19 @@ namespace rsx bind_range(); bind_cpu_only(); bind_cpu_only(); + bind(); + bind(); + bind(); + bind(); + bind(); + bind(); + bind(); + bind(); + bind(); + bind(); + bind(); + bind(); + bind(); //NV308A bind_range(); diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index d6c4bedf81..fae5b2a950 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -87,6 +87,7 @@ + @@ -516,6 +517,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 616cd98818..d3b2a9660c 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -936,6 +936,9 @@ Emu\GPU\RSX\Common + + Emu\GPU\RSX\Common + @@ -1788,5 +1791,8 @@ Source Files + + Emu\GPU\RSX\Common + \ No newline at end of file