rsx: Preserve read AA state separate from write AA state

- Some applications (e.g Backbreaker) use an evil hack to resolve MSAA.
  The application respecifies a formerly AA region as a region with no AA then performs a framebuffer feedback lookup.
  The old memory keeps AA during read, but writes back to itself with AA resolved.
  This is evil on several levels but it just happens to work on PS3
This commit is contained in:
kd-11 2018-06-03 14:52:21 +03:00 committed by kd-11
parent 0f24379c0e
commit 3150619320
11 changed files with 115 additions and 107 deletions

View file

@ -61,14 +61,36 @@ namespace rsx
template <typename image_storage_type>
struct render_target_descriptor
{
bool dirty = false;
image_storage_type old_contents = nullptr;
rsx::surface_antialiasing read_aa_mode = rsx::surface_antialiasing::center_1_sample;
GcmTileInfo *tile = nullptr;
rsx::surface_antialiasing aa_mode = rsx::surface_antialiasing::center_1_sample;
rsx::surface_antialiasing write_aa_mode = rsx::surface_antialiasing::center_1_sample;
virtual image_storage_type get_surface() = 0;
virtual u16 get_surface_width() const = 0;
virtual u16 get_surface_height() const = 0;
virtual u16 get_rsx_pitch() const = 0;
virtual u16 get_native_pitch() const = 0;
void save_aa_mode()
{
read_aa_mode = write_aa_mode;
write_aa_mode = rsx::surface_antialiasing::center_1_sample;
}
void reset_aa_mode()
{
write_aa_mode = read_aa_mode = rsx::surface_antialiasing::center_1_sample;
}
void on_write()
{
read_aa_mode = write_aa_mode;
dirty = false;
old_contents = nullptr;
}
};
/**
@ -134,6 +156,7 @@ namespace rsx
std::list<surface_storage_type> invalidated_resources;
u64 cache_tag = 0ull;
u64 write_tag = 0ull;
surface_store() = default;
~surface_store() = default;
@ -175,6 +198,7 @@ namespace rsx
surface_storage_type &rtt = It->second;
if (Traits::rtt_has_format_width_height(rtt, color_format, width, height))
{
Traits::notify_surface_persist(rtt);
Traits::prepare_rtt_for_drawing(command_list, Traits::get(rtt));
return Traits::get(rtt);
}
@ -206,7 +230,7 @@ namespace rsx
invalidated_resources.erase(It);
new_surface = Traits::get(new_surface_storage);
Traits::invalidate_rtt_surface_contents(command_list, new_surface, contents_to_copy, true);
Traits::invalidate_surface_contents(command_list, new_surface, contents_to_copy);
Traits::prepare_rtt_for_drawing(command_list, new_surface);
break;
}
@ -259,6 +283,7 @@ namespace rsx
surface_storage_type &ds = It->second;
if (Traits::ds_has_format_width_height(ds, depth_format, width, height))
{
Traits::notify_surface_persist(ds);
Traits::prepare_ds_for_drawing(command_list, Traits::get(ds));
return Traits::get(ds);
}
@ -290,7 +315,7 @@ namespace rsx
new_surface = Traits::get(new_surface_storage);
Traits::prepare_ds_for_drawing(command_list, new_surface);
Traits::invalidate_depth_surface_contents(command_list, new_surface, contents_to_copy, true);
Traits::invalidate_surface_contents(command_list, new_surface, contents_to_copy);
break;
}
}
@ -527,19 +552,6 @@ namespace rsx
return result;
}
/**
* Invalidates cached surface data and marks surface contents as deleteable
* Called at the end of a frame (workaround, need to find the proper invalidate command)
*/
void invalidate_surface_cache_data(command_list_type command_list)
{
for (auto &rtt : m_render_targets_storage)
Traits::invalidate_rtt_surface_contents(command_list, Traits::get(std::get<1>(rtt)), nullptr, false);
for (auto &ds : m_depth_stencil_storage)
Traits::invalidate_depth_surface_contents(command_list, Traits::get(std::get<1>(ds)), nullptr, true);
}
/**
* Moves a single surface from surface storage to invalidated surface store.
* Can be triggered by the texture cache's blit functionality when formats do not match
@ -653,7 +665,7 @@ namespace rsx
bool doubled_x = false;
bool doubled_y = false;
switch (surface->aa_mode)
switch (surface->read_aa_mode)
{
case rsx::surface_antialiasing::square_rotated_4_samples:
case rsx::surface_antialiasing::square_centered_4_samples:
@ -741,7 +753,7 @@ namespace rsx
u16 real_width = requested_width;
u16 real_height = requested_height;
switch (surface->aa_mode)
switch (surface->read_aa_mode)
{
case rsx::surface_antialiasing::diagonal_centered_2_samples:
real_width /= 2;
@ -894,5 +906,26 @@ namespace rsx
process_list_function(m_depth_stencil_storage, true);
return result;
}
void on_write()
{
if (write_tag == cache_tag)
return;
for (auto &rtt : m_bound_render_targets)
{
if (auto surface = std::get<1>(rtt))
{
surface->on_write();
}
}
if (auto ds = std::get<1>(m_bound_depth_stencil))
{
ds->on_write();
}
write_tag = cache_tag;
}
};
}

View file

@ -740,7 +740,7 @@ namespace rsx
template <typename T, typename U>
inline void get_native_dimensions(T &width, T &height, U surface)
{
switch (surface->aa_mode)
switch (surface->read_aa_mode)
{
case rsx::surface_antialiasing::center_1_sample:
return;
@ -758,7 +758,7 @@ namespace rsx
template <typename T, typename U>
inline void get_rsx_dimensions(T &width, T &height, U surface)
{
switch (surface->aa_mode)
switch (surface->read_aa_mode)
{
case rsx::surface_antialiasing::center_1_sample:
return;
@ -776,7 +776,7 @@ namespace rsx
template <typename T>
inline f32 get_internal_scaling_x(T surface)
{
switch (surface->aa_mode)
switch (surface->read_aa_mode)
{
default:
case rsx::surface_antialiasing::center_1_sample:
@ -791,7 +791,7 @@ namespace rsx
template <typename T>
inline f32 get_internal_scaling_y(T surface)
{
switch (surface->aa_mode)
switch (surface->read_aa_mode)
{
default:
case rsx::surface_antialiasing::center_1_sample:

View file

@ -129,21 +129,17 @@ struct render_target_traits
}
static
void invalidate_rtt_surface_contents(
void invalidate_surface_contents(
gsl::not_null<ID3D12GraphicsCommandList*>,
ID3D12Resource*, ID3D12Resource*, bool)
ID3D12Resource*, ID3D12Resource*)
{}
static
void invalidate_depth_surface_contents(
gsl::not_null<ID3D12GraphicsCommandList*>,
ID3D12Resource*, ID3D12Resource*, bool)
{
//TODO
}
void notify_surface_invalidated(const ComPtr<ID3D12Resource>&)
{}
static
void notify_surface_invalidated(const ComPtr<ID3D12Resource>&)
void notify_surface_persist(const ComPtr<ID3D12Resource>&)
{}
static

View file

@ -214,11 +214,8 @@ void GLGSRender::end()
//Copy data from old contents onto this one
const auto region = rsx::get_transferable_region(surface);
gl::g_hw_blitter->scale_image(surface->old_contents, surface, { 0, 0, std::get<0>(region), std::get<1>(region) }, { 0, 0, std::get<2>(region) , std::get<3>(region) }, !is_depth, is_depth, {});
surface->set_cleared();
}
//TODO: download image contents and reupload them or do a memory cast to copy memory contents if not compatible
surface->old_contents = nullptr;
};
//Check if we have any 'recycled' surfaces in memory and if so, clear them
@ -273,15 +270,12 @@ void GLGSRender::end()
if (clear_depth)
gl_state.depth_mask(rsx::method_registers.depth_write_enabled());
ds->set_cleared();
}
if (ds && ds->old_contents != nullptr && ds->get_rsx_pitch() == ds->old_contents->get_rsx_pitch() &&
if (ds && ds->old_contents != nullptr && ds->get_rsx_pitch() == static_cast<gl::render_target*>(ds->old_contents)->get_rsx_pitch() &&
ds->old_contents->get_internal_format() == gl::texture::internal_format::rgba8)
{
m_depth_converter.run(ds->width(), ds->height(), ds->id(), ds->old_contents->id());
ds->old_contents = nullptr;
}
if (g_cfg.video.strict_rendering_mode)
@ -298,11 +292,6 @@ void GLGSRender::end()
}
}
}
else
{
// Old contents are one use only. Keep the depth conversion check from firing over and over
if (ds) ds->old_contents = nullptr;
}
glEnable(GL_SCISSOR_TEST);
@ -571,6 +560,8 @@ void GLGSRender::end()
}
}
m_rtts.on_write();
m_attrib_ring_buffer->notify();
m_index_ring_buffer->notify();
m_vertex_state_buffer->notify();
@ -988,11 +979,9 @@ void GLGSRender::clear_surface(u32 arg)
gl_state.clear_depth(f32(clear_depth) / max_depth_value);
mask |= GLenum(gl::buffers::depth);
gl::render_target *ds = std::get<1>(m_rtts.m_bound_depth_stencil);
if (ds && !ds->cleared())
if (auto ds = std::get<1>(m_rtts.m_bound_depth_stencil))
{
ds->set_cleared();
ds->old_contents = nullptr;
ds->on_write();
}
}
@ -1036,10 +1025,9 @@ void GLGSRender::clear_surface(u32 arg)
for (auto &rtt : m_rtts.m_bound_render_targets)
{
if (std::get<0>(rtt) != 0)
if (auto surface = std::get<1>(rtt))
{
std::get<1>(rtt)->set_cleared(true);
std::get<1>(rtt)->old_contents = nullptr;
surface->on_write();
}
}

View file

@ -375,13 +375,13 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool sk
{
if (auto surface = std::get<1>(m_rtts.m_bound_render_targets[index]))
{
surface->aa_mode = aa_mode;
surface->write_aa_mode = aa_mode;
}
}
if (auto ds = std::get<1>(m_rtts.m_bound_depth_stencil))
{
ds->aa_mode = aa_mode;
ds->write_aa_mode = aa_mode;
}
return;
@ -426,7 +426,7 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool sk
m_surface_info[i] = { surface_addresses[i], pitchs[i], false, surface_format, depth_format, clip_horizontal, clip_vertical };
rtt->tile = find_tile(color_offsets[i], color_locations[i]);
rtt->aa_mode = aa_mode;
rtt->write_aa_mode = aa_mode;
m_gl_texture_cache.notify_surface_changed(surface_addresses[i]);
m_gl_texture_cache.tag_framebuffer(surface_addresses[i]);
}
@ -455,7 +455,7 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool sk
std::get<1>(m_rtts.m_bound_depth_stencil)->set_rsx_pitch(rsx::method_registers.surface_z_pitch());
m_depth_surface_info = { depth_address, depth_surface_pitch, true, surface_format, depth_format, clip_horizontal, clip_vertical };
ds->aa_mode = aa_mode;
ds->write_aa_mode = aa_mode;
m_gl_texture_cache.notify_surface_changed(depth_address);
m_gl_texture_cache.tag_framebuffer(depth_address);

View file

@ -51,8 +51,6 @@ namespace gl
{
class render_target : public texture, public rsx::ref_counted, public rsx::render_target_descriptor<texture*>
{
bool is_cleared = false;
u32 rsx_pitch = 0;
u16 native_pitch = 0;
@ -65,20 +63,18 @@ namespace gl
std::unordered_map<u32, std::unique_ptr<texture_view>> views;
public:
render_target *old_contents = nullptr;
render_target(GLuint width, GLuint height, GLenum sized_format)
:texture(GL_TEXTURE_2D, width, height, 1, 1, sized_format)
{}
void set_cleared(bool clear=true)
{
is_cleared = clear;
dirty = !clear;
}
bool cleared() const
{
return is_cleared;
return !dirty;
}
// Internal pitch is the actual row length in bytes of the openGL texture
@ -228,13 +224,24 @@ struct gl_render_target_traits
static void prepare_ds_for_drawing(void *, gl::render_target *ds) { ds->reset_refs(); }
static void prepare_ds_for_sampling(void *, gl::render_target*) {}
static void invalidate_rtt_surface_contents(void *, gl::render_target *rtt, gl::render_target* /*old*/, bool forced) { if (forced) rtt->set_cleared(false); }
static void invalidate_depth_surface_contents(void *, gl::render_target *ds, gl::render_target* /*old*/, bool) { ds->set_cleared(false); }
static
void invalidate_surface_contents(void *, gl::render_target *surface, gl::render_target* old_surface)
{
surface->set_cleared(false);
surface->old_contents = old_surface;
surface->reset_aa_mode();
}
static
void notify_surface_invalidated(const std::unique_ptr<gl::render_target>&)
{}
static
void notify_surface_persist(const std::unique_ptr<gl::render_target>& surface)
{
surface->save_aa_mode();
}
static
bool rtt_has_format_width_height(const std::unique_ptr<gl::render_target> &rtt, rsx::surface_color_format format, size_t width, size_t height, bool check_refs=false)
{

View file

@ -265,7 +265,7 @@ namespace gl
if (pbo_id == 0)
init_buffer();
aa_mode = static_cast<gl::render_target*>(image)->aa_mode;
aa_mode = static_cast<gl::render_target*>(image)->read_aa_mode;
}
flushed = false;

View file

@ -1115,7 +1115,6 @@ void VKGSRender::end()
VkClearValue clear_value = {};
clear_value.depthStencil = { 1.f, 255 };
buffers_to_clear.push_back({ vk::get_aspect_flags(ds->info.format), 0, clear_value });
ds->dirty = false;
}
for (u32 index = 0; index < targets.size(); ++index)
@ -1125,7 +1124,6 @@ void VKGSRender::end()
if (rtt->dirty)
{
buffers_to_clear.push_back({ VK_IMAGE_ASPECT_COLOR_BIT, index, {} });
rtt->dirty = false;
}
}
}
@ -1148,14 +1146,9 @@ void VKGSRender::end()
{
auto rp = vk::get_render_pass_location(VK_FORMAT_UNDEFINED, ds->info.format, 0);
auto render_pass = m_render_passes[rp];
m_depth_converter->run(*m_current_command_buffer, ds->width(), ds->height(), ds, ds->old_contents->get_view(0xAAE4, rsx::default_remap_vector), render_pass, m_framebuffers_to_clean);
ds->old_contents = nullptr;
}
else if (!g_cfg.video.strict_rendering_mode)
{
//Clear this to avoid dereferencing stale ptr
ds->old_contents = nullptr;
m_depth_converter->run(*m_current_command_buffer, ds->width(), ds->height(), ds,
static_cast<vk::render_target*>(ds->old_contents)->get_view(0xAAE4, rsx::default_remap_vector),
render_pass, m_framebuffers_to_clean);
}
}
@ -1187,14 +1180,9 @@ void VKGSRender::end()
vk::change_image_layout(*m_current_command_buffer, surface->old_contents, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_depth_scaler->run(*m_current_command_buffer, { 0, 0, (f32)src_w, (f32)src_h }, { 0, 0, (f32)dst_w, (f32)dst_h }, surface,
surface->old_contents, surface->old_contents->get_view(0xAAE4, rsx::default_remap_vector), render_pass, m_framebuffers_to_clean);
surface->old_contents, static_cast<vk::render_target*>(surface->old_contents)->get_view(0xAAE4, rsx::default_remap_vector), render_pass, m_framebuffers_to_clean);
}
surface->dirty = false;
}
//TODO: download image contents and reupload them or do a memory cast to copy memory contents if not compatible
surface->old_contents = nullptr;
};
//Prepare surfaces if needed
@ -1519,6 +1507,8 @@ void VKGSRender::end()
close_render_pass();
vk::leave_uninterruptible();
m_rtts.on_write();
std::chrono::time_point<steady_clock> draw_end = steady_clock::now();
m_draw_time += std::chrono::duration_cast<std::chrono::microseconds>(draw_end - textures_end).count();
@ -1792,10 +1782,9 @@ void VKGSRender::clear_surface(u32 mask)
for (auto &rtt : m_rtts.m_bound_render_targets)
{
if (std::get<0>(rtt) != 0)
if (auto surface = std::get<1>(rtt))
{
std::get<1>(rtt)->dirty = false;
std::get<1>(rtt)->old_contents = nullptr;
surface->on_write();
}
}
}
@ -1804,11 +1793,9 @@ void VKGSRender::clear_surface(u32 mask)
if (mask & 0x3)
{
if (std::get<0>(m_rtts.m_bound_depth_stencil) != 0)
if (auto ds = std::get<1>(m_rtts.m_bound_depth_stencil))
{
std::get<1>(m_rtts.m_bound_depth_stencil)->dirty = false;
std::get<1>(m_rtts.m_bound_depth_stencil)->old_contents = nullptr;
ds->on_write();
clear_descriptors.push_back({ (VkImageAspectFlags)depth_stencil_mask, 0, depth_stencil_clear_values });
}
}
@ -2744,13 +2731,13 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context)
{
if (auto surface = std::get<1>(m_rtts.m_bound_render_targets[index]))
{
surface->aa_mode = aa_mode;
surface->write_aa_mode = aa_mode;
}
}
if (auto ds = std::get<1>(m_rtts.m_bound_depth_stencil))
{
ds->aa_mode = aa_mode;
ds->write_aa_mode = aa_mode;
}
return;
@ -2820,7 +2807,7 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context)
m_surface_info[index].pitch = surface_pitchs[index];
surface->rsx_pitch = surface_pitchs[index];
surface->aa_mode = aa_mode;
surface->write_aa_mode = aa_mode;
m_texture_cache.notify_surface_changed(surface_addresses[index]);
m_texture_cache.tag_framebuffer(surface_addresses[index]);
@ -2837,7 +2824,7 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context)
m_depth_surface_info.pitch = rsx::method_registers.surface_z_pitch();
ds->rsx_pitch = m_depth_surface_info.pitch;
ds->aa_mode = aa_mode;
ds->write_aa_mode = aa_mode;
m_texture_cache.notify_surface_changed(zeta_address);
m_texture_cache.tag_framebuffer(zeta_address);

View file

@ -12,7 +12,6 @@ namespace vk
{
struct render_target : public image, public rsx::ref_counted, public rsx::render_target_descriptor<vk::image*>
{
bool dirty = false;
u16 native_pitch = 0;
u16 rsx_pitch = 0;
@ -22,7 +21,6 @@ namespace vk
VkImageAspectFlags attachment_aspect_flag = VK_IMAGE_ASPECT_COLOR_BIT;
std::unordered_map<u32, std::unique_ptr<vk::image_view>> views;
render_target *old_contents = nullptr; //Data occupying the memory location that this surface is replacing
u64 frame_tag = 0; //frame id when invalidated, 0 if not invalid
render_target(vk::render_device &dev,
@ -240,19 +238,12 @@ namespace rsx
change_image_layout(*pcmd, surface, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range);
}
static void invalidate_rtt_surface_contents(vk::command_buffer* /*pcmd*/, vk::render_target *rtt, vk::render_target *old_surface, bool forced)
static
void invalidate_surface_contents(vk::command_buffer* /*pcmd*/, vk::render_target *surface, vk::render_target *old_surface)
{
if (forced)
{
rtt->old_contents = old_surface;
rtt->dirty = true;
}
}
static void invalidate_depth_surface_contents(vk::command_buffer* /*pcmd*/, vk::render_target *ds, vk::render_target *old_surface, bool /*forced*/)
{
ds->dirty = true;
ds->old_contents = old_surface;
surface->old_contents = old_surface;
surface->dirty = true;
surface->reset_aa_mode();
}
static
@ -262,6 +253,12 @@ namespace rsx
if (!surface->frame_tag) surface->frame_tag = 1;
}
static
void notify_surface_persist(const std::unique_ptr<vk::render_target> &surface)
{
surface->save_aa_mode();
}
static bool rtt_has_format_width_height(const std::unique_ptr<vk::render_target> &rtt, surface_color_format format, size_t width, size_t height, bool check_refs=false)
{
if (check_refs && rtt->deref_count == 0) //Surface may still have read refs from data 'copy'

View file

@ -296,7 +296,7 @@ namespace vk
//Scale image to fit
//usually we can just get away with nearest filtering
u8 samples_u = 1, samples_v = 1;
switch (static_cast<vk::render_target*>(vram_texture)->aa_mode)
switch (static_cast<vk::render_target*>(vram_texture)->read_aa_mode)
{
case rsx::surface_antialiasing::diagonal_centered_2_samples:
samples_u = 2;

View file

@ -597,7 +597,7 @@ namespace rsx
u16 dst_w = src_w;
u16 dst_h = src_h;
switch (surface->old_contents->aa_mode)
switch (static_cast<SurfaceType*>(surface->old_contents)->read_aa_mode)
{
case rsx::surface_antialiasing::center_1_sample:
break;
@ -611,7 +611,7 @@ namespace rsx
break;
}
switch (surface->aa_mode)
switch (surface->write_aa_mode)
{
case rsx::surface_antialiasing::center_1_sample:
break;