#pragma once #include "../Common/surface_store.h" #include "GLHelpers.h" #include "stdafx.h" #include "../RSXThread.h" #include "../rsx_utils.h" struct color_swizzle { gl::texture::channel a = gl::texture::channel::a; gl::texture::channel r = gl::texture::channel::r; gl::texture::channel g = gl::texture::channel::g; gl::texture::channel b = gl::texture::channel::b; color_swizzle() = default; color_swizzle(gl::texture::channel a, gl::texture::channel r, gl::texture::channel g, gl::texture::channel b) : a(a), r(r), g(g), b(b) { } }; struct color_format { gl::texture::type type; gl::texture::format format; bool swap_bytes; int channel_count; int channel_size; color_swizzle swizzle; }; struct depth_format { gl::texture::type type; gl::texture::format format; gl::texture::internal_format internal_format; }; namespace rsx { namespace internals { ::gl::texture::internal_format sized_internal_format(rsx::surface_color_format color_format); color_format surface_color_format_to_gl(rsx::surface_color_format color_format); depth_format surface_depth_format_to_gl(rsx::surface_depth_format depth_format); u8 get_pixel_size(rsx::surface_depth_format format); } } namespace gl { class render_target : public texture, public rsx::ref_counted, public rsx::render_target_descriptor { bool is_cleared = false; u32 rsx_pitch = 0; u16 native_pitch = 0; u16 internal_width = 0; u16 internal_height = 0; u16 surface_height = 0; u16 surface_width = 0; u16 surface_pixel_size = 0; texture::internal_format compatible_internal_format = texture::internal_format::rgba8; std::array native_component_mapping; u32 current_remap_encoding = 0; public: render_target *old_contents = nullptr; render_target() {} void set_cleared(bool clear=true) { is_cleared = clear; } bool cleared() const { return is_cleared; } // Internal pitch is the actual row length in bytes of the openGL texture void set_native_pitch(u16 pitch) { native_pitch = pitch; } u16 get_native_pitch() const override { return native_pitch; } // Rsx pitch void set_rsx_pitch(u16 pitch) { rsx_pitch = pitch; } u16 get_rsx_pitch() const override { return rsx_pitch; } u16 get_surface_width() const override { return surface_width; } u16 get_surface_height() const override { return surface_height; } u32 get_surface() override { return get_view(0xAAE4, rsx::default_remap_vector); } u32 get_view(u32 remap_encoding, const std::pair, std::array>& remap) { if (remap_encoding != current_remap_encoding) { current_remap_encoding = remap_encoding; bind(); apply_swizzle_remap(GL_TEXTURE_2D, native_component_mapping, remap); } return id(); } u32 get_view() { //Get view with components in true native layout //TODO: Implement real image views const GLenum rgba_remap[4] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA }; glBindTexture(GL_TEXTURE_2D, id()); glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, (GLint*)rgba_remap); //Reset view encoding current_remap_encoding = 0; return id(); } void set_compatible_format(texture::internal_format format) { compatible_internal_format = format; } texture::internal_format get_compatible_internal_format() const override { return compatible_internal_format; } void update_surface() { internal_width = width(); internal_height = height(); surface_width = rsx::apply_inverse_resolution_scale(internal_width, true); surface_height = rsx::apply_inverse_resolution_scale(internal_height, true); bind(); glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, (GLint*)&native_component_mapping[0]); glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, (GLint*)&native_component_mapping[1]); glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, (GLint*)&native_component_mapping[2]); glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, (GLint*)&native_component_mapping[3]); } bool matches_dimensions(u16 _width, u16 _height) const { //Use foward scaling to account for rounding and clamping errors return (rsx::apply_resolution_scale(_width, true) == internal_width) && (rsx::apply_resolution_scale(_height, true) == internal_height); } }; } struct gl_render_target_traits { using surface_storage_type = std::unique_ptr; using surface_type = gl::render_target*; using command_list_type = void*; using download_buffer_object = std::vector; static std::unique_ptr create_new_surface( u32 /*address*/, rsx::surface_color_format surface_color_format, size_t width, size_t height, gl::render_target* old_surface ) { std::unique_ptr result(new gl::render_target()); auto format = rsx::internals::surface_color_format_to_gl(surface_color_format); auto internal_fmt = rsx::internals::sized_internal_format(surface_color_format); result->recreate(gl::texture::target::texture2D); result->set_native_pitch((u16)width * format.channel_count * format.channel_size); result->set_compatible_format(internal_fmt); __glcheck result->config() .size({ (int)rsx::apply_resolution_scale((u16)width, true), (int)rsx::apply_resolution_scale((u16)height, true) }) .type(format.type) .format(format.format) .internal_format(internal_fmt) .swizzle(format.swizzle.r, format.swizzle.g, format.swizzle.b, format.swizzle.a) .wrap(gl::texture::wrap::clamp_to_border, gl::texture::wrap::clamp_to_border, gl::texture::wrap::clamp_to_border) .apply(); __glcheck result->pixel_pack_settings().swap_bytes(format.swap_bytes).aligment(1); __glcheck result->pixel_unpack_settings().swap_bytes(format.swap_bytes).aligment(1); result->old_contents = old_surface; result->set_cleared(); result->update_surface(); return result; } static std::unique_ptr create_new_surface( u32 /*address*/, rsx::surface_depth_format surface_depth_format, size_t width, size_t height, gl::render_target* old_surface ) { std::unique_ptr result(new gl::render_target()); auto format = rsx::internals::surface_depth_format_to_gl(surface_depth_format); result->recreate(gl::texture::target::texture2D); const auto scale = rsx::get_resolution_scale(); __glcheck result->config() .size({ (int)rsx::apply_resolution_scale((u16)width, true), (int)rsx::apply_resolution_scale((u16)height, true) }) .type(format.type) .format(format.format) .internal_format(format.internal_format) .swizzle(gl::texture::channel::r, gl::texture::channel::r, gl::texture::channel::r, gl::texture::channel::r) .wrap(gl::texture::wrap::clamp_to_border, gl::texture::wrap::clamp_to_border, gl::texture::wrap::clamp_to_border) .apply(); __glcheck result->pixel_pack_settings().aligment(1); __glcheck result->pixel_unpack_settings().aligment(1); u16 native_pitch = (u16)width * 2; if (surface_depth_format == rsx::surface_depth_format::z24s8) native_pitch *= 2; result->set_native_pitch(native_pitch); result->set_compatible_format(format.internal_format); result->old_contents = old_surface; result->update_surface(); return result; } static void get_surface_info(gl::render_target *surface, rsx::surface_format_info *info) { info->rsx_pitch = surface->get_rsx_pitch(); info->native_pitch = surface->get_native_pitch(); info->surface_width = surface->get_surface_width(); info->surface_height = surface->get_surface_height(); info->bpp = static_cast(info->native_pitch / info->surface_width); } static void prepare_rtt_for_drawing(void *, gl::render_target *rtt) { rtt->reset_refs(); } static void prepare_rtt_for_sampling(void *, gl::render_target*) {} 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 notify_surface_invalidated(const std::unique_ptr&) {} static bool rtt_has_format_width_height(const std::unique_ptr &rtt, rsx::surface_color_format format, size_t width, size_t height, bool check_refs=false) { if (check_refs) //TODO return false; auto internal_fmt = rsx::internals::sized_internal_format(format); return rtt->get_compatible_internal_format() == internal_fmt && rtt->matches_dimensions((u16)width, (u16)height); } static bool ds_has_format_width_height(const std::unique_ptr &rtt, rsx::surface_depth_format, size_t width, size_t height, bool check_refs=false) { if (check_refs) //TODO return false; // TODO: check format return rtt->matches_dimensions((u16)width, (u16)height); } // Note : pbo breaks fbo here so use classic texture copy static std::vector issue_download_command(gl::render_target* color_buffer, rsx::surface_color_format color_format, size_t width, size_t height) { auto pixel_format = rsx::internals::surface_color_format_to_gl(color_format); std::vector result(width * height * pixel_format.channel_count * pixel_format.channel_size); color_buffer->bind(); glGetTexImage(GL_TEXTURE_2D, 0, (GLenum)pixel_format.format, (GLenum)pixel_format.type, result.data()); return result; } static std::vector issue_depth_download_command(gl::render_target* depth_stencil_buffer, rsx::surface_depth_format depth_format, size_t width, size_t height) { std::vector result(width * height * 4); auto pixel_format = rsx::internals::surface_depth_format_to_gl(depth_format); depth_stencil_buffer->bind(); glGetTexImage(GL_TEXTURE_2D, 0, (GLenum)pixel_format.format, (GLenum)pixel_format.type, result.data()); return result; } static std::vector issue_stencil_download_command(gl::render_target*, size_t width, size_t height) { std::vector result(width * height * 4); return result; } static gsl::span map_downloaded_buffer(const std::vector &buffer) { return{ reinterpret_cast(buffer.data()), ::narrow(buffer.size()) }; } static void unmap_downloaded_buffer(const std::vector &) { } static gl::render_target* get(const std::unique_ptr &in) { return in.get(); } }; struct gl_render_targets : public rsx::surface_store { void free_invalidated() { invalidated_resources.remove_if([&](auto &rtt) { if (rtt->deref_count >= 2) { rtt->remove(); return true; } rtt->deref_count++; return false; }); } };