#pragma once #include "GCM.h" #include "RSXTexture.h" #include "RSXVertexProgram.h" #include "RSXFragmentProgram.h" #include #include "Utilities/Semaphore.h" #include "Utilities/Thread.h" #include "Utilities/Timer.h" #include "Utilities/convert.h" extern u64 get_system_time(); struct frame_capture_data { struct draw_state { std::string name; std::pair programs; size_t width = 0, height = 0; rsx::surface_color_format color_format; std::array, 4> color_buffer; rsx::surface_depth_format depth_format; std::array, 2> depth_stencil; rsx::index_array_type index_type; std::vector index; u32 vertex_count; }; std::vector > command_queue; std::vector draw_calls; void reset() { command_queue.clear(); draw_calls.clear(); } }; extern bool user_asked_for_frame_capture; extern frame_capture_data frame_debug; namespace rsx { enum class shader_language { glsl, hlsl }; } namespace convert { template<> struct to_impl_t { static rsx::shader_language func(const std::string &from) { if (from == "glsl") return rsx::shader_language::glsl; if (from == "hlsl") return rsx::shader_language::hlsl; throw; } }; template<> struct to_impl_t { static std::string func(rsx::shader_language from) { switch (from) { case rsx::shader_language::glsl: return "glsl"; case rsx::shader_language::hlsl: return "hlsl"; } throw; } }; } namespace rsx { namespace limits { enum { textures_count = 16, vertex_textures_count = 4, vertex_count = 16, fragment_count = 32, tiles_count = 15, zculls_count = 8, color_buffers_count = 4 }; } struct decompiled_shader { std::string code; }; struct finalized_shader { u64 ucode_hash; std::string code; }; template> struct cache { private: std::unordered_map m_entries; public: const Type* find(u64 key) const { auto found = m_entries.find(key); if (found == m_entries.end()) return nullptr; return &found->second; } void insert(KeyType key, const Type &shader) { m_entries.insert({ key, shader }); } }; struct shaders_cache { cache decompiled_fragment_shaders; cache decompiled_vertex_shaders; cache finailized_fragment_shaders; cache finailized_vertex_shaders; void load(const std::string &path, shader_language lang); void load(shader_language lang); static std::string path_to_root(); }; u32 get_vertex_type_size_on_host(vertex_base_type type, u32 size); u32 get_address(u32 offset, u32 location); struct tiled_region { u32 address; u32 base; GcmTileInfo *tile; u8 *ptr; void write(const void *src, u32 width, u32 height, u32 pitch); void read(void *dst, u32 width, u32 height, u32 pitch); }; struct surface_info { u8 log2height; u8 log2width; surface_antialiasing antialias; surface_depth_format depth_format; surface_color_format color_format; u32 width; u32 height; u32 format; void unpack(u32 surface_format) { format = surface_format; log2height = surface_format >> 24; log2width = (surface_format >> 16) & 0xff; antialias = to_surface_antialiasing((surface_format >> 12) & 0xf); depth_format = to_surface_depth_format((surface_format >> 5) & 0x7); color_format = to_surface_color_format(surface_format & 0x1f); width = 1 << (u32(log2width) + 1); height = 1 << (u32(log2width) + 1); } }; struct data_array_format_info { u16 frequency = 0; u8 stride = 0; u8 size = 0; vertex_base_type type = vertex_base_type::f; void unpack_array(u32 data_array_format) { frequency = data_array_format >> 16; stride = (data_array_format >> 8) & 0xff; size = (data_array_format >> 4) & 0xf; type = to_vertex_base_type(data_array_format & 0xf); } }; enum class draw_command { array, inlined_array, indexed, }; class thread : public named_thread_t { protected: std::stack m_call_stack; public: struct shaders_cache shaders_cache; CellGcmControl* ctrl = nullptr; Timer timer_sync; GcmTileInfo tiles[limits::tiles_count]; GcmZcullInfo zculls[limits::zculls_count]; rsx::texture textures[limits::textures_count]; rsx::vertex_texture vertex_textures[limits::vertex_textures_count]; /** * RSX can sources vertex attributes from 2 places: * - Immediate values passed by NV4097_SET_VERTEX_DATA*_M + ARRAY_ID write. * For a given ARRAY_ID the last command of this type defines the actual type of the immediate value. * Since there can be only a single value per ARRAY_ID passed this way, all vertex in the draw call * shares it. * - Vertex array values passed by offset/stride/size/format description. * * A given ARRAY_ID can have both an immediate value and a vertex array enabled at the same time * (See After Burner Climax intro cutscene). In such case the vertex array has precedence over the * immediate value. As soon as the vertex array is disabled (size set to 0) the immediate value * must be used if the vertex attrib mask request it. * * Note that behavior when both vertex array and immediate value system are disabled but vertex attrib mask * request inputs is unknow. */ data_array_format_info register_vertex_info[limits::vertex_count]; std::vector register_vertex_data[limits::vertex_count]; data_array_format_info vertex_arrays_info[limits::vertex_count]; u32 vertex_draw_count = 0; std::unordered_map> transform_constants; /** * Stores the first and count argument from draw/draw indexed parameters between begin/end clauses. */ std::vector > first_count_commands; // Constant stored for whole frame std::unordered_map local_transform_constants; u32 transform_program[512 * 4] = {}; bool capture_current_frame = false; void capture_frame(const std::string &name); public: u32 ioAddress, ioSize; int flip_status; int flip_mode; int debug_level; int frequency_mode; u32 tiles_addr; u32 zculls_addr; vm::ps3::ptr gcm_buffers; u32 gcm_buffers_count; u32 gcm_current_buffer; u32 ctxt_addr; u32 report_main_addr; u32 label_addr; rsx::draw_command draw_command; primitive_type draw_mode; u32 local_mem_addr, main_mem_addr; bool strict_ordering[0x1000]; bool draw_inline_vertex_array; std::vector inline_vertex_array; bool m_rtts_dirty; bool m_transform_constants_dirty; bool m_textures_dirty[16]; protected: std::array get_color_surface_addresses() const; u32 get_zeta_surface_address() const; RSXVertexProgram get_current_vertex_program() const; RSXFragmentProgram get_current_fragment_program() const; public: u32 draw_array_count; u32 draw_array_first; double fps_limit = 59.94; public: semaphore_t sem_flip; u64 last_flip_time; vm::ps3::ptr flip_handler = vm::null; vm::ps3::ptr user_handler = vm::null; vm::ps3::ptr vblank_handler = vm::null; u64 vblank_count; public: std::set m_used_gcm_commands; protected: thread(); virtual ~thread(); virtual void on_task() override; public: virtual std::string get_name() const override; virtual void begin(); virtual void end(); virtual void on_init() = 0; virtual void on_init_thread() = 0; virtual bool do_method(u32 cmd, u32 value) { return false; } virtual void flip(int buffer) = 0; virtual u64 timestamp() const; virtual bool on_access_violation(u32 address, bool is_writing) { return false; } private: std::mutex m_mtx_task; struct internal_task_entry { std::function callback; std::promise promise; internal_task_entry(std::function callback) : callback(callback) { } }; std::deque m_internal_tasks; void do_internal_task(); public: std::future add_internal_task(std::function callback); void invoke(std::function callback); /** * Fill buffer with 4x4 scale offset matrix. * Vertex shader's position is to be multiplied by this matrix. * if is_d3d is set, the matrix is modified to use d3d convention. */ void fill_scale_offset_data(void *buffer, bool is_d3d = true) const; /** * Fill buffer with vertex program constants. * Buffer must be at least 512 float4 wide. */ void fill_vertex_program_constants_data(void *buffer); /** * Write inlined array data to buffer. * The storage of inlined data looks different from memory stored arrays. * There is no swapping required except for 4 u8 (according to Bleach Soul Resurection) */ void write_inline_array_to_buffer(void *dst_buffer); /** * Copy rtt values to buffer. * TODO: It's more efficient to combine multiple call of this function into one. */ virtual std::array, 4> copy_render_targets_to_memory() { return std::array, 4>(); }; /** * Copy depth and stencil content to buffers. * TODO: It's more efficient to combine multiple call of this function into one. */ virtual std::array, 2> copy_depth_stencil_buffer_to_memory() { return std::array, 2>(); }; virtual std::pair get_programs() const { return std::make_pair("", ""); }; public: void reset(); void init(const u32 ioAddress, const u32 ioSize, const u32 ctrlAddress, const u32 localAddress); tiled_region get_tiled_address(u32 offset, u32 location); GcmTileInfo *find_tile(u32 offset, u32 location); u32 ReadIO32(u32 addr); void WriteIO32(u32 addr, u32 value); }; }