#include "stdafx.h" #include "rpcs3/Ini.h" #include "Utilities/Log.h" #include "Emu/Memory/Memory.h" #include "Emu/System.h" #include "Emu/RSX/GSManager.h" #include "Emu/RSX/GSRender.h" #include "Emu/SysCalls/Modules/cellVideoOut.h" #include "RSXThread.h" #include "Utilities/types.h" #include "Emu/SysCalls/Callback.h" #include "Emu/SysCalls/CB_FUNC.h" extern "C" { #include "libswscale/swscale.h" } extern u64 get_system_time(); #define ARGS(x) (x >= count ? OutOfArgsCount(x, cmd, count, args.addr()) : args[x].value()) #define CMD_DEBUG 0 namespace rsx { u32 method_registers[0x10000 >> 2]; template struct vertex_data_type_from_element_type; template<> struct vertex_data_type_from_element_type { enum { type = CELL_GCM_VERTEX_F }; }; template<> struct vertex_data_type_from_element_type { enum { type = CELL_GCM_VERTEX_SF }; }; template<> struct vertex_data_type_from_element_type { enum { type = CELL_GCM_VERTEX_UB }; }; template<> struct vertex_data_type_from_element_type { enum { type = CELL_GCM_VERTEX_S1 }; }; namespace nv4097 { //fire only when all data passed to rsx cmd buffer template force_inline void set_vertex_data_impl(thread* rsx, u32 arg) { static const size_t element_size = (count * sizeof(type)); static const size_t element_size_in_words = element_size / sizeof(u32); auto& info = rsx->vertex_arrays_info[index]; info.type = vertex_data_type_from_element_type::type; info.size = count; info.frequency = 0; info.stride = 0; info.array = false; auto& entry = rsx->vertex_arrays[index]; //find begin of data size_t begin = id + index * element_size_in_words; size_t position = entry.size(); entry.resize(position + element_size); memcpy(entry.data() + position, method_registers + begin, element_size); } template force_inline void set_vertex_data4ub_m(thread* rsx, u32 arg) { set_vertex_data_impl(rsx, arg); } template force_inline void set_vertex_data1f_m(thread* rsx, u32 arg) { set_vertex_data_impl(rsx, arg); } template force_inline void set_vertex_data2f_m(thread* rsx, u32 arg) { set_vertex_data_impl(rsx, arg); } template force_inline void set_vertex_data3f_m(thread* rsx, u32 arg) { set_vertex_data_impl(rsx, arg); } template force_inline void set_vertex_data4f_m(thread* rsx, u32 arg) { set_vertex_data_impl(rsx, arg); } template force_inline void set_vertex_data2s_m(thread* rsx, u32 arg) { set_vertex_data_impl(rsx, arg); } template force_inline void set_vertex_data4s_m(thread* rsx, u32 arg) { set_vertex_data_impl(rsx, arg); } template force_inline void set_vertex_data_array_format(thread* rsx, u32 arg) { auto& info = rsx->vertex_arrays_info[index]; info.unpack(arg); info.array = info.size > 0; } } u32 linear_to_swizzle(u32 x, u32 y, u32 z, u32 log2_width, u32 log2_height, u32 log2_depth) { u32 offset = 0; u32 shift_count = 0; while (log2_width | log2_height | log2_depth) { if (log2_width) { offset |= (x & 0x01) << shift_count; x >>= 1; ++shift_count; --log2_width; } if (log2_height) { offset |= (y & 0x01) << shift_count; y >>= 1; ++shift_count; --log2_height; } if (log2_depth) { offset |= (z & 0x01) << shift_count; z >>= 1; ++shift_count; --log2_depth; } } return offset; } u32 get_address(u32 offset, u32 location) { u32 res = 0; switch (location) { case CELL_GCM_CONTEXT_DMA_MEMORY_FRAME_BUFFER: case CELL_GCM_LOCATION_LOCAL: { //TODO: don't use not named constants like 0xC0000000 res = 0xC0000000 + offset; break; } case CELL_GCM_CONTEXT_DMA_MEMORY_HOST_BUFFER: case CELL_GCM_LOCATION_MAIN: { res = (u32)RSXIOMem.RealAddr(offset); // TODO: Error Check? if (res == 0) { throw fmt::format("GetAddress(offset=0x%x, location=0x%x): RSXIO memory not mapped", offset, location); } //if (Emu.GetGSManager().GetRender().strict_ordering[offset >> 20]) //{ // _mm_mfence(); // probably doesn't have any effect on current implementation //} break; } default: { throw EXCEPTION("Invalid location (offset=0x%x, location=0x%x)", offset, location); } } return res; } u32 get_vertex_type_size(u32 type) { switch (type) { case CELL_GCM_VERTEX_S1: return sizeof(u16); case CELL_GCM_VERTEX_F: return sizeof(f32); case CELL_GCM_VERTEX_SF: return sizeof(f16); case CELL_GCM_VERTEX_UB: return sizeof(u8); case CELL_GCM_VERTEX_S32K: return sizeof(u32); case CELL_GCM_VERTEX_CMP: return sizeof(u32); case CELL_GCM_VERTEX_UB256: return sizeof(u8) * 4; default: LOG_ERROR(RSX, "RSXVertexData::GetTypeSize: Bad vertex data type (%d)!", type); assert(0); return 1; } } u32 thread::OutOfArgsCount(const uint x, const u32 cmd, const u32 count, const u32 args_addr) { auto args = vm::ps3::ptr::make(args_addr); std::string debug = GetMethodName(cmd); debug += "("; for (u32 i = 0; i < count; ++i) debug += (i ? ", " : "") + fmt::format("0x%x", ARGS(i)); debug += ")"; LOG_NOTICE(RSX, "OutOfArgsCount(x=%d, count=%d): %s", x, count, debug.c_str()); return 0; } #define case_2(offset, step) \ case offset: \ case offset + step: #define case_4(offset, step) \ case_2(offset, step) \ case_2(offset + 2*step, step) #define case_8(offset, step) \ case_4(offset, step) \ case_4(offset + 4*step, step) #define case_16(offset, step) \ case_8(offset, step) \ case_8(offset + 8*step, step) #define case_32(offset, step) \ case_16(offset, step) \ case_16(offset + 16*step, step) #define case_range(n, offset, step) \ case_##n(offset, step) \ index = (cmd - offset) / step void thread::DoCmd(const u32 fcmd, const u32 cmd, const u32 args_addr, const u32 count) { auto args = vm::ps3::ptr::make(args_addr); #if CMD_DEBUG std::string debug = GetMethodName(cmd); debug += "("; for (u32 i = 0; i < count; ++i) debug += (i ? ", " : "") + fmt::format("0x%x", ARGS(i)); debug += ")"; LOG_NOTICE(RSX, debug); #endif u32 index = 0; m_used_gcm_commands.insert(cmd); switch (cmd) { // NV406E case NV406E_SET_REFERENCE: { ctrl->ref.exchange(ARGS(0)); break; } case NV406E_SET_CONTEXT_DMA_SEMAPHORE: { if (ARGS(0)) { LOG_WARNING(RSX, "TODO: NV406E_SET_CONTEXT_DMA_SEMAPHORE: 0x%x", ARGS(0)); } break; } case NV4097_SET_SEMAPHORE_OFFSET: { m_PGRAPH_semaphore_offset = ARGS(0); break; } case NV406E_SEMAPHORE_OFFSET: { m_PFIFO_semaphore_offset = ARGS(0); break; } case NV406E_SEMAPHORE_ACQUIRE: { semaphorePFIFOAcquire(m_PFIFO_semaphore_offset, ARGS(0)); break; } case NV406E_SEMAPHORE_RELEASE: { m_PFIFO_semaphore_release_value = ARGS(0); break; } case NV4097_TEXTURE_READ_SEMAPHORE_RELEASE: { semaphorePGRAPHTextureReadRelease(m_PGRAPH_semaphore_offset, ARGS(0)); break; } case NV4097_BACK_END_WRITE_SEMAPHORE_RELEASE: { u32 value = ARGS(0); value = (value & 0xff00ff00) | ((value & 0xff) << 16) | ((value >> 16) & 0xff); semaphorePGRAPHBackendRelease(m_PGRAPH_semaphore_offset, value); break; } // NV4097 case 0x0003fead: { flip(0); last_flip_time = get_system_time(); gcm_current_buffer = ARGS(0); m_read_buffer = true; flip_status = 0; if (auto cb = flip_handler) { Emu.GetCallbackManager().Async([=](CPUThread& cpu) { cb(static_cast(cpu), 1); }); } sem_flip.post_and_wait(); auto sync = [&]() { double limit; switch (Ini.GSFrameLimit.GetValue()) { case 1: limit = 50.; break; case 2: limit = 59.94; break; case 3: limit = 30.; break; case 4: limit = 60.; break; case 5: limit = m_fps_limit; break; //TODO case 0: default: return; } std::this_thread::sleep_for(std::chrono::milliseconds((s64)(1000.0 / limit - timer_sync.GetElapsedTimeInMilliSec()))); timer_sync.Start(); }; sync(); //Emu.Pause(); break; } case NV4097_NO_OPERATION: { // Nothing to do here break; } case NV4097_SET_CONTEXT_DMA_REPORT: { if (ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_CONTEXT_DMA_REPORT: 0x%x", ARGS(0)); dma_report = ARGS(0); } break; } case NV4097_NOTIFY: { if (ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_NOTIFY: 0x%x", ARGS(0)); } break; } case NV4097_WAIT_FOR_IDLE: { if (ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_WAIT_FOR_IDLE: 0x%x", ARGS(0)); } break; } case NV4097_PM_TRIGGER: { if (ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_PM_TRIGGER: 0x%x", ARGS(0)); } break; } // Texture case_range(16, NV4097_SET_TEXTURE_FORMAT, 0x20); case_range(16, NV4097_SET_TEXTURE_OFFSET, 0x20); case_range(16, NV4097_SET_TEXTURE_FILTER, 0x20); case_range(16, NV4097_SET_TEXTURE_ADDRESS, 0x20); case_range(16, NV4097_SET_TEXTURE_IMAGE_RECT, 32); case_range(16, NV4097_SET_TEXTURE_BORDER_COLOR, 0x20); case_range(16, NV4097_SET_TEXTURE_CONTROL0, 0x20); case_range(16, NV4097_SET_TEXTURE_CONTROL1, 0x20); { // Done using methodRegisters in RSXTexture.cpp break; } case_range(16, NV4097_SET_TEX_COORD_CONTROL, 4); { const u32 a0 = ARGS(0); u8 texMask2D = a0 & 1; u8 texMaskCentroid = (a0 >> 4) & 1; LOG_WARNING(RSX, "TODO: NV4097_SET_TEX_COORD_CONTROL(texMask2D=%d, texMaskCentroid=%d)", texMask2D, texMaskCentroid); break; } case_range(16, NV4097_SET_TEXTURE_CONTROL2, 4); { LOG_WARNING(RSX, "TODO: NV4097_SET_TEXTURE_CONTROL2"); const u32 a0 = ARGS(0); // TODO: Use these u8 unknown = (a0 >> 8) & 0xFF; u8 iso = (a0 >> 6) & 1; u8 aniso = (a0 >> 7) & 1; u8 slope = a0 & 0x1F; break; } case_range(16, NV4097_SET_TEXTURE_CONTROL3, 4); { rsx::texture& tex = textures[index]; const u32 a0 = ARGS(0); u32 pitch = a0 & 0xFFFFF; u16 depth = a0 >> 20; //tex.SetControl3(depth, pitch); break; } // Vertex Texture case_range(4, NV4097_SET_VERTEX_TEXTURE_FORMAT, 0x20); case_range(4, NV4097_SET_VERTEX_TEXTURE_OFFSET, 0x20); case_range(4, NV4097_SET_VERTEX_TEXTURE_FILTER, 0x20); case_range(4, NV4097_SET_VERTEX_TEXTURE_ADDRESS, 0x20); case_range(4, NV4097_SET_VERTEX_TEXTURE_IMAGE_RECT, 0x20); case_range(4, NV4097_SET_VERTEX_TEXTURE_BORDER_COLOR, 0x20); case_range(4, NV4097_SET_VERTEX_TEXTURE_CONTROL0, 0x20); { // Done using methodRegisters in RSXTexture.cpp break; } case_range(4, NV4097_SET_VERTEX_TEXTURE_CONTROL3, 0x20); { rsx::vertex_texture& tex = m_vertex_textures[index]; const u32 a0 = ARGS(0); u32 pitch = a0 & 0xFFFFF; u16 depth = a0 >> 20; //tex.SetControl3(depth, pitch); break; } #define bind_2(index, offset, step, func) \ case offset : \ func(this, ARGS(0)); \ break; \ case offset + step: \ func(this, ARGS(0)); \ break; #define bind_4(index, offset, step, func) \ bind_2(index, offset, step, func); \ bind_2(index + 2, offset + 2*step, step, func) #define bind_8(index, offset, step, func) \ bind_4(index, offset, step, func); \ bind_4(index + 4, offset + 4*step, step, func) #define bind_16(index, offset, step, func) \ bind_8(index, offset, step, func); \ bind_8(index + 8, offset + 8*step, step, func) #define bind_32(index, offset, step, func) \ bind_16(index, offset, step, func); \ bind_16(index + 16, offset + 16*step, step, func) #define bind_64(index, offset, step, func) \ bind_32(index, offset, step, func); \ bind_32(index + 32, offset + 32*step, step, func) #define bind_128(index, offset, step, func) \ bind_64(index, offset, step, func); \ bind_64(index + 64, offset + 64*step, step, func) #define bind_256(index, offset, step, func) \ bind_128(index, offset, step, func); \ bind_128(index + 128, offset + 128*step, step, func) #define bind_512(index, offset, step, func) \ bind_256(index, offset, step, func); \ bind_256(index + 256, offset + 256*step, step, func) // Vertex data bind_16(0, NV4097_SET_VERTEX_DATA4UB_M, 4, rsx::nv4097::set_vertex_data4ub_m); bind_16(0, NV4097_SET_VERTEX_DATA2F_M, 8, rsx::nv4097::set_vertex_data2f_m); bind_16(0, NV4097_SET_VERTEX_DATA4F_M, 16, rsx::nv4097::set_vertex_data4f_m); case_range(16, NV4097_SET_VERTEX_DATA_ARRAY_OFFSET, 4); break; case_range(16, NV4097_SET_VERTEX_DATA_ARRAY_FORMAT, 4); { const u32 a0 = ARGS(0); rsx::data_array_format_info &cv = vertex_arrays_info[index]; cv.unpack(a0); cv.array = cv.size > 0; break; } // Vertex Attribute case NV4097_SET_VERTEX_ATTRIB_INPUT_MASK: { if (u32 mask = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_VERTEX_ATTRIB_INPUT_MASK: 0x%x", mask); } //VertexData[0].prog.attributeInputMask = ARGS(0); break; } case NV4097_SET_VERTEX_ATTRIB_OUTPUT_MASK: { if (u32 mask = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_VERTEX_ATTRIB_OUTPUT_MASK: 0x%x", mask); } //VertexData[0].prog.attributeOutputMask = ARGS(0); //FragmentData.prog.attributeInputMask = ARGS(0)/* & ~0x20*/; break; } // Color Mask case NV4097_SET_COLOR_MASK: notifyRasterizerStateChange(); break; case NV4097_SET_COLOR_MASK_MRT: { if (u32 mask = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_COLOR_MASK_MRT: 0x%x", mask); } break; } // Alpha testing case NV4097_SET_ALPHA_TEST_ENABLE: break; case NV4097_SET_ALPHA_FUNC: break; case NV4097_SET_ALPHA_REF: break; // Cull face case NV4097_SET_CULL_FACE_ENABLE: notifyRasterizerStateChange(); break; case NV4097_SET_CULL_FACE: notifyRasterizerStateChange(); break; // Front face case NV4097_SET_FRONT_FACE: notifyRasterizerStateChange(); break; // Blending case NV4097_SET_BLEND_ENABLE: notifyBlendStateChange(); break; case NV4097_SET_BLEND_ENABLE_MRT: notifyBlendStateChange(); break; case NV4097_SET_BLEND_FUNC_SFACTOR: notifyBlendStateChange(); break; case NV4097_SET_BLEND_FUNC_DFACTOR: notifyBlendStateChange(); break; case NV4097_SET_BLEND_COLOR: notifyBlendStateChange(); break; case NV4097_SET_BLEND_COLOR2: break; case NV4097_SET_BLEND_EQUATION: notifyBlendStateChange(); break; case NV4097_SET_REDUCE_DST_COLOR: break; // Depth bound testing case NV4097_SET_DEPTH_BOUNDS_TEST_ENABLE: { m_set_depth_bounds_test = ARGS(0) ? true : false; break; } case NV4097_SET_DEPTH_BOUNDS_MIN: { m_set_depth_bounds = true; const u32 a0 = ARGS(0); m_depth_bounds_min = (float&)a0; if (count == 2) { const u32 a1 = ARGS(1); m_depth_bounds_max = (float&)a1; } break; } case NV4097_SET_DEPTH_BOUNDS_MAX: { m_set_depth_bounds = true; const u32 a0 = ARGS(0); m_depth_bounds_max = (float&)a0; break; } // Viewport case NV4097_SET_VIEWPORT_HORIZONTAL: break; case NV4097_SET_VIEWPORT_VERTICAL: break; case NV4097_SET_VIEWPORT_SCALE: case NV4097_SET_VIEWPORT_OFFSET: { // Done in Vertex Shader break; } // Clipping case NV4097_SET_CLIP_MIN: { const u32 a0 = ARGS(0); const u32 a1 = ARGS(1); m_set_clip = true; m_clip_min = (float&)a0; m_clip_max = (float&)a1; //LOG_NOTICE(RSX, "NV4097_SET_CLIP_MIN: clip_min=%.01f, clip_max=%.01f", m_clip_min, m_clip_max); break; } case NV4097_SET_CLIP_MAX: { const u32 a0 = ARGS(0); m_set_clip = true; m_clip_max = (float&)a0; //LOG_NOTICE(RSX, "NV4097_SET_CLIP_MAX: clip_max=%.01f", m_clip_max); break; } // Depth testing case NV4097_SET_DEPTH_TEST_ENABLE: break; case NV4097_SET_DEPTH_FUNC: notifyDepthStencilStateChange(); break; case NV4097_SET_DEPTH_MASK: notifyDepthStencilStateChange(); break; // Polygon mode/offset case NV4097_SET_FRONT_POLYGON_MODE: { m_set_front_polygon_mode = true; m_front_polygon_mode = ARGS(0); break; } case NV4097_SET_BACK_POLYGON_MODE: { m_set_back_polygon_mode = true; m_back_polygon_mode = ARGS(0); break; } case NV4097_SET_POLY_OFFSET_FILL_ENABLE: { m_set_poly_offset_fill = ARGS(0) ? true : false; break; } case NV4097_SET_POLY_OFFSET_LINE_ENABLE: { m_set_poly_offset_line = ARGS(0) ? true : false; break; } case NV4097_SET_POLY_OFFSET_POINT_ENABLE: { m_set_poly_offset_point = ARGS(0) ? true : false; break; } case NV4097_SET_POLYGON_OFFSET_SCALE_FACTOR: { //m_set_depth_test = true; m_set_poly_offset_mode = true; const u32 a0 = ARGS(0); m_poly_offset_scale_factor = (float&)a0; if (count == 2) { const u32 a1 = ARGS(1); m_poly_offset_bias = (float&)a1; } break; } case NV4097_SET_POLYGON_OFFSET_BIAS: { //m_set_depth_test = true; m_set_poly_offset_mode = true; const u32 a0 = ARGS(0); m_poly_offset_bias = (float&)a0; break; } case NV4097_SET_CYLINDRICAL_WRAP: { if (ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_CYLINDRICAL_WRAP: 0x%x", ARGS(0)); } break; } // Clearing case NV4097_CLEAR_ZCULL_SURFACE: break; case NV4097_CLEAR_SURFACE: { const u32 a0 = ARGS(0); domethod(NV4097_CLEAR_SURFACE, a0); break; } case NV4097_SET_ZSTENCIL_CLEAR_VALUE: break; case NV4097_SET_COLOR_CLEAR_VALUE: break; case NV4097_SET_CLEAR_RECT_HORIZONTAL: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_CLEAR_RECT_HORIZONTAL: 0x%x", value); } break; } case NV4097_SET_CLEAR_RECT_VERTICAL: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_CLEAR_RECT_VERTICAL: 0x%x", value); } break; } // Arrays case NV4097_INLINE_ARRAY: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_INLINE_ARRAY: 0x%x", value); } break; } case NV4097_DRAW_ARRAYS: { for (u32 c = 0; c> 24) + 1; //LOG_WARNING(RSX, "NV4097_DRAW_ARRAYS: %d - %d", first, _count); if (first < draw_array_first) { draw_array_first = first; } draw_array_count += _count; } break; } case NV4097_SET_INDEX_ARRAY_ADDRESS: { m_indexed_array.m_addr = rsx::get_address(ARGS(0), ARGS(1) & 0xf); m_indexed_array.m_type = ARGS(1) >> 4; break; } case NV4097_DRAW_INDEX_ARRAY: { for (u32 c=0; c> 24) + 1; if (first < m_indexed_array.m_first) m_indexed_array.m_first = first; int pos = (int)m_indexed_array.m_data.size(); switch (m_indexed_array.m_type) { case CELL_GCM_DRAW_INDEX_ARRAY_TYPE_32: m_indexed_array.m_data.resize(pos + 4 * _count); break; case CELL_GCM_DRAW_INDEX_ARRAY_TYPE_16: m_indexed_array.m_data.resize(pos + 2 * _count); break; } for (u32 i=first; i< first + _count; ++i) { u32 index; switch(m_indexed_array.m_type) { case CELL_GCM_DRAW_INDEX_ARRAY_TYPE_32: index = vm::ps3::read32(m_indexed_array.m_addr + i * 4); *(u32*)&m_indexed_array.m_data[i * 4] = index; break; case CELL_GCM_DRAW_INDEX_ARRAY_TYPE_16: index = vm::ps3::read16(m_indexed_array.m_addr + i * 2); *(u16*)&m_indexed_array.m_data[i * 2] = index; break; } if (index < m_indexed_array.index_min) m_indexed_array.index_min = index; if (index > m_indexed_array.index_max) m_indexed_array.index_max = index; } m_indexed_array.m_count += _count; } break; } case NV4097_SET_VERTEX_DATA_BASE_OFFSET: break; case NV4097_SET_VERTEX_DATA_BASE_INDEX: { m_vertex_data_base_index = ARGS(0); break; } case NV4097_SET_BEGIN_END: { const u32 a0 = ARGS(0); //LOG_WARNING(RSX, "NV4097_SET_BEGIN_END: 0x%x", a0); if (!m_indexed_array.m_count && !draw_array_count) { u32 min_vertex_size = ~0; for (unsigned id = 0; id < rsx::limits::vertex_count; id++) { auto &i = vertex_arrays_info[id]; if (!i.size) continue; u32 vertex_size = vertex_arrays[id].size() / (i.size * rsx::get_vertex_type_size(i.type)); if (min_vertex_size > vertex_size) min_vertex_size = vertex_size; } draw_array_count = min_vertex_size; draw_array_first = 0; } m_read_buffer = Ini.GSReadColorBuffer.GetValue() || (!m_indexed_array.m_count && !draw_array_count); if (a0) { begin(a0); } else { end(); } break; } // Shader case NV4097_SET_SHADER_PROGRAM: notifyProgramChange(); break; case NV4097_SET_SHADER_CONTROL: { m_shader_ctrl = ARGS(0); break; } case NV4097_SET_SHADE_MODE: { m_set_shade_mode = true; m_shade_mode = ARGS(0); break; } case NV4097_SET_SHADER_PACKER: { if (ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_SHADER_PACKER: 0x%x", ARGS(0)); } } case NV4097_SET_SHADER_WINDOW: { const u32 a0 = ARGS(0); m_shader_window_height = a0 & 0xfff; m_shader_window_origin = (a0 >> 12) & 0xf; m_shader_window_pixel_centers = a0 >> 16; break; } // Transform case NV4097_SET_TRANSFORM_PROGRAM_LOAD: { //LOG_WARNING(RSX, "NV4097_SET_TRANSFORM_PROGRAM_LOAD: prog = %d", ARGS(0)); m_cur_vertex_prog = &m_vertex_progs[ARGS(0)]; m_cur_vertex_prog->data.clear(); if (count == 2) { const u32 start = ARGS(1); if (start) { LOG_WARNING(RSX, "NV4097_SET_TRANSFORM_PROGRAM_LOAD: start = %d", start); } } notifyProgramChange(); break; } case NV4097_SET_TRANSFORM_PROGRAM_START: { const u32 start = ARGS(0); if (start) { LOG_WARNING(RSX, "NV4097_SET_TRANSFORM_PROGRAM_START: start = %d", start); } break; } case_range(32, NV4097_SET_TRANSFORM_PROGRAM, 4); { //LOG_WARNING(RSX, "NV4097_SET_TRANSFORM_PROGRAM[%d](%d)", index, count); if (!m_cur_vertex_prog) { LOG_ERROR(RSX, "NV4097_SET_TRANSFORM_PROGRAM: m_cur_vertex_prog is null"); break; } for (u32 i = 0; i < count; ++i) { m_cur_vertex_prog->data.push_back(ARGS(i)); } notifyProgramChange(); break; } case NV4097_SET_TRANSFORM_TIMEOUT: { // TODO: // (cmd)[1] = CELL_GCM_ENDIAN_SWAP((count) | ((registerCount) << 16)); \ if (!m_cur_vertex_prog) { LOG_ERROR(RSX, "NV4097_SET_TRANSFORM_TIMEOUT: m_cur_vertex_prog is null"); break; } //m_cur_vertex_prog->Decompile(); break; } case NV4097_SET_TRANSFORM_BRANCH_BITS: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_TRANSFORM_BRANCH_BITS: 0x%x", value); } break; } case NV4097_SET_TRANSFORM_CONSTANT_LOAD: { if ((count - 1) % 4) { LOG_ERROR(RSX, "NV4097_SET_TRANSFORM_CONSTANT_LOAD: bad count %d", count); break; } for (u32 id = ARGS(0), i = 1; i> 16; if (count == 2) { m_set_scissor_vertical = true; m_scissor_y = ARGS(1) & 0xffff; m_scissor_h = ARGS(1) >> 16; } break; } case NV4097_SET_SCISSOR_VERTICAL: { m_set_scissor_vertical = true; m_scissor_y = ARGS(0) & 0xffff; m_scissor_h = ARGS(0) >> 16; break; } // Depth/Color buffer usage case NV4097_SET_SURFACE_FORMAT: { auto buffers = vm::get_ptr(m_gcm_buffers_addr); m_width = buffers[gcm_current_buffer].width; m_height = buffers[gcm_current_buffer].height; CellVideoOutResolution res = ResolutionTable[ResolutionIdToNum(Ini.GSResolution.GetValue())]; m_width_scale = (float)res.width / m_width * 2.0f; m_height_scale = (float)res.height / m_height * 2.0f; m_width = (u32)res.width; m_height = (u32)res.height; break; } case NV4097_SET_SURFACE_COLOR_TARGET: break; case NV4097_SET_SURFACE_COLOR_AOFFSET: break; case NV4097_SET_SURFACE_COLOR_BOFFSET: break; case NV4097_SET_SURFACE_COLOR_COFFSET: break; case NV4097_SET_SURFACE_COLOR_DOFFSET: break; case NV4097_SET_SURFACE_ZETA_OFFSET: break; case NV4097_SET_SURFACE_PITCH_A: break; case NV4097_SET_SURFACE_PITCH_B: break; case NV4097_SET_SURFACE_PITCH_C: break; case NV4097_SET_SURFACE_PITCH_D: break; case NV4097_SET_SURFACE_PITCH_Z: break; case NV4097_SET_CONTEXT_DMA_COLOR_A: break; case NV4097_SET_CONTEXT_DMA_COLOR_B: break; case NV4097_SET_CONTEXT_DMA_COLOR_C: break; case NV4097_SET_CONTEXT_DMA_COLOR_D: break; case NV4097_SET_CONTEXT_DMA_ZETA: break; case NV4097_SET_CONTEXT_DMA_SEMAPHORE: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_CONTEXT_DMA_SEMAPHORE: 0x%x", value); } break; } case NV4097_SET_CONTEXT_DMA_NOTIFIES: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_CONTEXT_DMA_NOTIFIES: 0x%x", value); } break; } case NV4097_SET_SURFACE_CLIP_HORIZONTAL: break; case NV4097_SET_SURFACE_CLIP_VERTICAL: break; // Anti-aliasing case NV4097_SET_ANTI_ALIASING_CONTROL: { const u32 a0 = ARGS(0); const u8 enable = a0 & 0xf; const u8 alphaToCoverage = (a0 >> 4) & 0xf; const u8 alphaToOne = (a0 >> 8) & 0xf; const u16 sampleMask = a0 >> 16; if (a0) { LOG_WARNING(RSX, "TODO: NV4097_SET_ANTI_ALIASING_CONTROL: 0x%x", a0); } break; } // Line/Polygon smoothing case NV4097_SET_LINE_SMOOTH_ENABLE: { m_set_line_smooth = ARGS(0) ? true : false; break; } case NV4097_SET_POLY_SMOOTH_ENABLE: { m_set_poly_smooth = ARGS(0) ? true : false; break; } // Line width case NV4097_SET_LINE_WIDTH: { m_set_line_width = true; const u32 a0 = ARGS(0); m_line_width = (float)a0 / 8.0f; break; } // Line/Polygon stipple case NV4097_SET_LINE_STIPPLE: { m_set_line_stipple = ARGS(0) ? true : false; break; } case NV4097_SET_LINE_STIPPLE_PATTERN: { m_set_line_stipple = true; const u32 a0 = ARGS(0); m_line_stipple_factor = a0 & 0xffff; m_line_stipple_pattern = a0 >> 16; break; } case NV4097_SET_POLYGON_STIPPLE: { m_set_polygon_stipple = ARGS(0) ? true : false; break; } case NV4097_SET_POLYGON_STIPPLE_PATTERN: { for (u32 i = 0; i < 32; i++) { m_polygon_stipple_pattern[i] = ARGS(i); } break; } // Zcull case NV4097_SET_ZCULL_EN: notifyDepthStencilStateChange(); break; case NV4097_SET_ZCULL_CONTROL0: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_ZCULL_CONTROL0: 0x%x", value); } break; } case NV4097_SET_ZCULL_CONTROL1: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_ZCULL_CONTROL1: 0x%x", value); } break; } case NV4097_SET_ZCULL_STATS_ENABLE: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_SET_ZCULL_STATS_ENABLE: 0x%x", value); } break; } case NV4097_ZCULL_SYNC: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV4097_ZCULL_SYNC: 0x%x", value); } break; } // Reports case NV4097_GET_REPORT: { const u32 a0 = ARGS(0); u8 type = a0 >> 24; u32 offset = a0 & 0xffffff; u32 value; switch(type) { case CELL_GCM_ZPASS_PIXEL_CNT: case CELL_GCM_ZCULL_STATS: case CELL_GCM_ZCULL_STATS1: case CELL_GCM_ZCULL_STATS2: case CELL_GCM_ZCULL_STATS3: value = 0; LOG_WARNING(RSX, "NV4097_GET_REPORT: Unimplemented type %d", type); break; default: value = 0; LOG_ERROR(RSX, "NV4097_GET_REPORT: Bad type %d", type); break; } // Get timestamp, and convert it from microseconds to nanoseconds u64 timestamp = get_system_time() * 1000; // NOTE: DMA broken, implement proper lpar mapping (sys_rsx) //dma_write64(dma_report, offset + 0x0, timestamp); //dma_write32(dma_report, offset + 0x8, value); //dma_write32(dma_report, offset + 0xc, 0); vm::ps3::write64(local_mem_addr + offset + 0x0, timestamp); vm::ps3::write32(local_mem_addr + offset + 0x8, value); vm::ps3::write32(local_mem_addr + offset + 0xc, 0); break; } case NV4097_CLEAR_REPORT_VALUE: { const u32 type = ARGS(0); switch (type) { case CELL_GCM_ZPASS_PIXEL_CNT: LOG_WARNING(RSX, "TODO: NV4097_CLEAR_REPORT_VALUE: ZPASS_PIXEL_CNT"); break; case CELL_GCM_ZCULL_STATS: LOG_WARNING(RSX, "TODO: NV4097_CLEAR_REPORT_VALUE: ZCULL_STATS"); break; default: LOG_ERROR(RSX, "NV4097_CLEAR_REPORT_VALUE: Bad type: %d", type); break; } break; } // Clip Plane case NV4097_SET_USER_CLIP_PLANE_CONTROL: { const u32 a0 = ARGS(0); m_set_clip_plane = true; m_clip_plane_0 = (a0 & 0xf) ? true : false; m_clip_plane_1 = ((a0 >> 4)) & 0xf ? true : false; m_clip_plane_2 = ((a0 >> 8)) & 0xf ? true : false; m_clip_plane_3 = ((a0 >> 12)) & 0xf ? true : false; m_clip_plane_4 = ((a0 >> 16)) & 0xf ? true : false; m_clip_plane_5 = (a0 >> 20) ? true : false; break; } // Fog case NV4097_SET_FOG_MODE: { m_set_fog_mode = true; m_fog_mode = ARGS(0); break; } case NV4097_SET_FOG_PARAMS: { m_set_fog_params = true; const u32 a0 = ARGS(0); const u32 a1 = ARGS(1); m_fog_param0 = (float&)a0; m_fog_param1 = (float&)a1; break; } // Zmin_max case NV4097_SET_ZMIN_MAX_CONTROL: { const u8 cullNearFarEnable = ARGS(0) & 0xf; const u8 zclampEnable = (ARGS(0) >> 4) & 0xf; const u8 cullIgnoreW = (ARGS(0) >> 8) & 0xf; LOG_WARNING(RSX, "TODO: NV4097_SET_ZMIN_MAX_CONTROL: cullNearFarEnable=%d, zclampEnable=%d, cullIgnoreW=%d", cullNearFarEnable, zclampEnable, cullIgnoreW); break; } case NV4097_SET_WINDOW_OFFSET: { const u16 x = ARGS(0); const u16 y = ARGS(0) >> 16; LOG_WARNING(RSX, "TODO: NV4097_SET_WINDOW_OFFSET: x=%d, y=%d", x, y); break; } case NV4097_SET_FREQUENCY_DIVIDER_OPERATION: { m_set_frequency_divider_operation = ARGS(0); LOG_WARNING(RSX, "TODO: NV4097_SET_FREQUENCY_DIVIDER_OPERATION: %d", m_set_frequency_divider_operation); break; } case NV4097_SET_RENDER_ENABLE: { const u32 offset = ARGS(0) & 0xffffff; const u8 mode = ARGS(0) >> 24; LOG_WARNING(RSX, "TODO: NV4097_SET_RENDER_ENABLE: Offset=0x%06x, Mode=0x%x", offset, mode); break; } case NV4097_SET_ZPASS_PIXEL_COUNT_ENABLE: { const u32 enable = ARGS(0); LOG_WARNING(RSX, "TODO: NV4097_SET_ZPASS_PIXEL_COUNT_ENABLE: %d", enable); break; } // NV0039 case NV0039_SET_CONTEXT_DMA_BUFFER_IN: { const u32 srcContext = ARGS(0); const u32 dstContext = ARGS(1); m_context_dma_buffer_in_src = srcContext; m_context_dma_buffer_in_dst = dstContext; break; } case NV0039_OFFSET_IN: { const u32 inOffset = ARGS(0); const u32 outOffset = ARGS(1); const u32 inPitch = ARGS(2); const u32 outPitch = ARGS(3); const u32 lineLength = ARGS(4); const u32 lineCount = ARGS(5); const u8 outFormat = (ARGS(6) >> 8); const u8 inFormat = (ARGS(6) >> 0); const u32 notify = ARGS(7); // The existing GCM commands use only the value 0x1 for inFormat and outFormat if (inFormat != 0x01 || outFormat != 0x01) { LOG_ERROR(RSX, "NV0039_OFFSET_IN: Unsupported format: inFormat=%d, outFormat=%d", inFormat, outFormat); } if (lineCount == 1 && !inPitch && !outPitch && !notify) { memcpy(vm::get_ptr(rsx::get_address(outOffset, 0)), vm::get_ptr(rsx::get_address(inOffset, 0)), lineLength); } else { LOG_ERROR(RSX, "NV0039_OFFSET_IN: bad offset(in=0x%x, out=0x%x), pitch(in=0x%x, out=0x%x), line(len=0x%x, cnt=0x%x), fmt(in=0x%x, out=0x%x), notify=0x%x", inOffset, outOffset, inPitch, outPitch, lineLength, lineCount, inFormat, outFormat, notify); } break; } case NV0039_OFFSET_OUT: // [E : RSXThread]: TODO: unknown/illegal method [0x00002310](0x0) { const u32 offset = ARGS(0); if (!offset) { } else { LOG_ERROR(RSX, "TODO: NV0039_OFFSET_OUT: offset=0x%x", offset); } break; } case NV0039_PITCH_IN: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV0039_PITCH_IN: 0x%x", value); } break; } case NV0039_BUFFER_NOTIFY: { if (u32 value = ARGS(0)) { LOG_WARNING(RSX, "TODO: NV0039_BUFFER_NOTIFY: 0x%x", value); } break; } // NV3062 case NV3062_SET_CONTEXT_DMA_IMAGE_DESTIN: { if (count == 1) { m_context_dma_img_dst = ARGS(0); } else { LOG_ERROR(RSX, "NV3062_SET_CONTEXT_DMA_IMAGE__DESTIN: unknown arg count (%d)", count); } break; } case NV3062_SET_OFFSET_DESTIN: { if (count == 1) { m_dst_offset = ARGS(0); } else { LOG_ERROR(RSX, "NV3062_SET_OFFSET_DESTIN: unknown arg count (%d)", count); } break; } case NV3062_SET_COLOR_FORMAT: { if (count == 2 || count == 4) { m_color_format = ARGS(0); m_color_format_src_pitch = ARGS(1); m_color_format_dst_pitch = ARGS(1) >> 16; if (count == 4) { if (ARGS(2)) { LOG_ERROR(RSX, "NV3062_SET_COLOR_FORMAT: unknown arg2 value (0x%x)", ARGS(2)); } m_dst_offset = ARGS(3); } } else { LOG_ERROR(RSX, "NV3062_SET_COLOR_FORMAT: unknown arg count (%d)", count); } break; } // NV309E case NV309E_SET_CONTEXT_DMA_IMAGE: { if (count == 1) { m_context_dma_img_src = ARGS(0); } else { LOG_ERROR(RSX, "NV309E_SET_CONTEXT_DMA_IMAGE: unknown arg count (%d)", count); } break; } case NV309E_SET_FORMAT: { if (count == 2) { m_swizzle_format = ARGS(0); m_swizzle_width = ARGS(0) >> 16; m_swizzle_height = ARGS(0) >> 24; m_swizzle_offset = ARGS(1); } else { LOG_ERROR(RSX, "NV309E_SET_FORMAT: unknown arg count (%d)", count); } break; } // NV308A case NV308A_POINT: { const u32 a0 = ARGS(0); m_point_x = a0 & 0xffff; m_point_y = a0 >> 16; break; } case NV308A_COLOR: { color4f c; u32 id = m_dst_offset | ((u32)m_point_x << 2); if (count >= 1) { u32 a = ARGS(0); a = a << 16 | a >> 16; c.x = (float&)a; } if (count >= 2) { u32 a = ARGS(1); a = a << 16 | a >> 16; c.y = (float&)a; } if (count >= 3) { u32 a = ARGS(2); a = a << 16 | a >> 16; c.z = (float&)a; } if (count >= 4) { u32 a = ARGS(3); a = a << 16 | a >> 16; c.w = (float&)a; } if (count >= 5) { LOG_ERROR(RSX, "NV308A_COLOR: unknown arg count (%d)", count); } fragment_constants[id] = c; break; } // NV3089 case NV3089_SET_CONTEXT_DMA_IMAGE: { if (count == 1) { m_context_dma_img_src = ARGS(0); } else { LOG_ERROR(RSX, "NV3089_SET_CONTEXT_DMA_IMAGE: unknown arg count (%d)", count); } break; } case NV3089_SET_CONTEXT_SURFACE: { if (count == 1) { m_context_surface = ARGS(0); if (m_context_surface != CELL_GCM_CONTEXT_SURFACE2D && m_context_surface != CELL_GCM_CONTEXT_SWIZZLE2D) { LOG_ERROR(RSX, "NV3089_SET_CONTEXT_SURFACE: unknown surface (0x%x)", ARGS(0)); } } else { LOG_ERROR(RSX, "NV3089_SET_CONTEXT_SURFACE: unknown arg count (%d)", count); } break; } case NV3089_IMAGE_IN_SIZE: { const u16 width = ARGS(0); const u16 height = ARGS(0) >> 16; const u16 pitch = ARGS(1); const u8 origin = ARGS(1) >> 16; if (origin != 2 /* CELL_GCM_TRANSFER_ORIGIN_CORNER */) { LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: unknown origin (%d)", origin); } const u8 inter = ARGS(1) >> 24; if (inter != 0 /* CELL_GCM_TRANSFER_INTERPOLATOR_ZOH */ && inter != 1 /* CELL_GCM_TRANSFER_INTERPOLATOR_FOH */) { LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: unknown inter (%d)", inter); } const u32 offset = ARGS(2); const u16 u = ARGS(3); // inX (currently ignored) const u16 v = ARGS(3) >> 16; // inY (currently ignored) u8* pixels_src = vm::get_ptr(rsx::get_address(offset, m_context_dma_img_src - 0xfeed0000)); u8* pixels_dst = vm::get_ptr(rsx::get_address(m_dst_offset, m_context_dma_img_dst - 0xfeed0000)); if (m_context_surface == CELL_GCM_CONTEXT_SWIZZLE2D) { LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: Swizzle2D not implemented"); } else if (m_context_surface != CELL_GCM_CONTEXT_SURFACE2D) { LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: unknown m_context_surface (0x%x)", m_context_surface); } if (m_color_format != 4 /* CELL_GCM_TRANSFER_SURFACE_FORMAT_R5G6B5 */ && m_color_format != 10 /* CELL_GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8 */) { LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: unknown m_color_format (%d)", m_color_format); } const u32 in_bpp = m_color_format == 4 ? 2 : 4; // bytes per pixel const u32 out_bpp = m_color_conv_fmt == 7 ? 2 : 4; const s32 out_w = (s32)(u64(width) * (1 << 20) / m_color_conv_dsdx); const s32 out_h = (s32)(u64(height) * (1 << 20) / m_color_conv_dtdy); LOG_WARNING(RSX, "NV3089_IMAGE_IN_SIZE: w=%d, h=%d, pitch=%d, offset=0x%x, inX=%f, inY=%f, scaleX=%f, scaleY=%f", width, height, pitch, offset, double(u) / 16, double(v) / 16, double(1 << 20) / (m_color_conv_dsdx), double(1 << 20) / (m_color_conv_dtdy)); std::unique_ptr temp; if (in_bpp != out_bpp && width != out_w && height != out_h) { // resize/convert if necessary temp.reset(new u8[out_bpp * out_w * out_h]); AVPixelFormat in_format = m_color_format == 4 ? AV_PIX_FMT_RGB565BE : AV_PIX_FMT_ARGB; // ??? AVPixelFormat out_format = m_color_conv_fmt == 7 ? AV_PIX_FMT_RGB565BE : AV_PIX_FMT_ARGB; // ??? std::unique_ptr sws(sws_getContext(width, height, in_format, out_w, out_h, out_format, inter ? SWS_FAST_BILINEAR : SWS_POINT, NULL, NULL, NULL), sws_freeContext); int in_line = in_bpp * width; u8* out_ptr = temp.get(); int out_line = out_bpp * out_w; sws_scale(sws.get(), &pixels_src, &in_line, 0, height, &out_ptr, &out_line); pixels_src = temp.get(); // use resized image as a source } if (m_color_conv_out_w != m_color_conv_clip_w || m_color_conv_out_w != out_w || m_color_conv_out_h != m_color_conv_clip_h || m_color_conv_out_h != out_h || m_color_conv_out_x || m_color_conv_out_y || m_color_conv_clip_x || m_color_conv_clip_y) { // clip if necessary for (s32 y = m_color_conv_clip_y, dst_y = m_color_conv_out_y; y < out_h; y++, dst_y++) { if (dst_y >= 0 && dst_y < m_color_conv_out_h) { // destination line u8* dst_line = pixels_dst + dst_y * out_bpp * m_color_conv_out_w + std::min(std::max(m_color_conv_out_x, 0), m_color_conv_out_w); size_t dst_max = std::min(std::max((s32)m_color_conv_out_w - m_color_conv_out_x, 0), m_color_conv_out_w) * out_bpp; if (y >= 0 && y < std::min(m_color_conv_clip_h, out_h)) { // source line u8* src_line = pixels_src + y * out_bpp * out_w + std::min(std::max(m_color_conv_clip_x, 0), m_color_conv_clip_w); size_t src_max = std::min(std::max((s32)m_color_conv_clip_w - m_color_conv_clip_x, 0), m_color_conv_clip_w) * out_bpp; std::pair z0 = { src_line + 0, std::min(dst_max, std::max(0, m_color_conv_clip_x)) }, d0 = { src_line + z0.second, std::min(dst_max - z0.second, src_max) }, z1 = { src_line + d0.second, dst_max - z0.second - d0.second }; memset(z0.first, 0, z0.second); memcpy(d0.first, src_line, d0.second); memset(z1.first, 0, z1.second); } else { memset(dst_line, 0, dst_max); } } } } else { memcpy(pixels_dst, pixels_src, out_w * out_h * out_bpp); } break; } case NV3089_SET_COLOR_CONVERSION: { m_color_conv = ARGS(0); if (m_color_conv != 1 /* CELL_GCM_TRANSFER_CONVERSION_TRUNCATE */) { LOG_ERROR(RSX, "NV3089_SET_COLOR_CONVERSION: unknown color conv (%d)", m_color_conv); } m_color_conv_fmt = ARGS(1); if (m_color_conv_fmt != 3 /* CELL_GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8 */ && m_color_conv_fmt != 7 /* CELL_GCM_TRANSFER_SCALE_FORMAT_R5G6B5 */) { LOG_ERROR(RSX, "NV3089_SET_COLOR_CONVERSION: unknown format (%d)", m_color_conv_fmt); } m_color_conv_op = ARGS(2); if (m_color_conv_op != 3 /* CELL_GCM_TRANSFER_OPERATION_SRCCOPY */) { LOG_ERROR(RSX, "NV3089_SET_COLOR_CONVERSION: unknown color conv op (%d)", m_color_conv_op); } m_color_conv_clip_x = ARGS(3); m_color_conv_clip_y = ARGS(3) >> 16; m_color_conv_clip_w = ARGS(4); m_color_conv_clip_h = ARGS(4) >> 16; m_color_conv_out_x = ARGS(5); m_color_conv_out_y = ARGS(5) >> 16; m_color_conv_out_w = ARGS(6); m_color_conv_out_h = ARGS(6) >> 16; m_color_conv_dsdx = ARGS(7); m_color_conv_dtdy = ARGS(8); break; } case GCM_SET_USER_COMMAND: { const u32 cause = ARGS(0); if (auto cb = user_handler) { Emu.GetCallbackManager().Async([=](CPUThread& cpu) { cb(static_cast(cpu), cause); }); } else { throw EXCEPTION("User handler not set"); } break; } // Note: What is this? NV4097 offsets? case 0x000002c8: case 0x000002d0: case 0x000002d8: case 0x000002e0: case 0x000002e8: case 0x000002f0: case 0x000002f8: break; // The existing GCM commands don't use any of the following NV4097 / NV0039 / NV3062 / NV309E / NV308A / NV3089 methods case NV4097_SET_WINDOW_CLIP_TYPE: case NV4097_SET_WINDOW_CLIP_HORIZONTAL: case NV4097_SET_WINDOW_CLIP_VERTICAL: { LOG_WARNING(RSX, "Unused NV4097 method 0x%x detected!", cmd); break; } case NV0039_SET_CONTEXT_DMA_BUFFER_OUT: case NV0039_PITCH_OUT: case NV0039_LINE_LENGTH_IN: case NV0039_LINE_COUNT: case NV0039_FORMAT: case NV0039_SET_OBJECT: case NV0039_SET_CONTEXT_DMA_NOTIFIES: { LOG_WARNING(RSX, "Unused NV0039 method 0x%x detected!", cmd); break; } case NV3062_SET_OBJECT: case NV3062_SET_CONTEXT_DMA_NOTIFIES: case NV3062_SET_CONTEXT_DMA_IMAGE_SOURCE: case NV3062_SET_PITCH: case NV3062_SET_OFFSET_SOURCE: { LOG_WARNING(RSX, "Unused NV3062 method 0x%x detected!", cmd); break; } case NV308A_SET_OBJECT: case NV308A_SET_CONTEXT_DMA_NOTIFIES: case NV308A_SET_CONTEXT_COLOR_KEY: case NV308A_SET_CONTEXT_CLIP_RECTANGLE: case NV308A_SET_CONTEXT_PATTERN: case NV308A_SET_CONTEXT_ROP: case NV308A_SET_CONTEXT_BETA1: case NV308A_SET_CONTEXT_BETA4: case NV308A_SET_CONTEXT_SURFACE: case NV308A_SET_COLOR_CONVERSION: case NV308A_SET_OPERATION: case NV308A_SET_COLOR_FORMAT: case NV308A_SIZE_OUT: case NV308A_SIZE_IN: { LOG_WARNING(RSX, "Unused NV308A method 0x%x detected!", cmd); break; } case NV309E_SET_OBJECT: case NV309E_SET_CONTEXT_DMA_NOTIFIES: case NV309E_SET_OFFSET: { LOG_WARNING(RSX, "Unused NV309E method 0x%x detected!", cmd); break; } case NV3089_SET_OBJECT: case NV3089_SET_CONTEXT_DMA_NOTIFIES: case NV3089_SET_CONTEXT_PATTERN: case NV3089_SET_CONTEXT_ROP: case NV3089_SET_CONTEXT_BETA1: case NV3089_SET_CONTEXT_BETA4: case NV3089_SET_COLOR_FORMAT: case NV3089_SET_OPERATION: case NV3089_CLIP_POINT: case NV3089_CLIP_SIZE: case NV3089_IMAGE_OUT_POINT: case NV3089_IMAGE_OUT_SIZE: case NV3089_DS_DX: case NV3089_DT_DY: case NV3089_IMAGE_IN_FORMAT: case NV3089_IMAGE_IN_OFFSET: case NV3089_IMAGE_IN: { LOG_WARNING(RSX, "Unused NV3089 methods 0x%x detected!", cmd); break; } default: { std::string log = GetMethodName(cmd); log += "("; for (u32 i = 0; i < count; ++i) { log += (i ? ", " : "") + fmt::format("0x%x", ARGS(i)); } log += ")"; LOG_ERROR(RSX, "TODO: %s", log.c_str()); break; } } } void thread::begin(u32 drawMode) { m_begin_end = 1; draw_mode = drawMode; draw_array_count = 0; draw_array_first = ~0; } void thread::end() { for (auto &vdata : vertex_arrays) { vdata.clear(); } m_indexed_array.Reset(); fragment_constants.clear(); m_clear_surface_mask = 0; m_begin_end = 0; OnReset(); } void thread::task() { u8 inc; LOG_NOTICE(RSX, "RSX thread started"); oninit_thread(); last_flip_time = get_system_time() - 1000000; autojoin_thread_t vblank(WRAP_EXPR("VBlank Thread"), [this]() { const u64 start_time = get_system_time(); vblank_count = 0; while (joinable()) { CHECK_EMU_STATUS; if (get_system_time() - start_time > vblank_count * 1000000 / 60) { vblank_count++; if (auto cb = vblank_handler) { Emu.GetCallbackManager().Async([=](CPUThread& cpu) { cb(static_cast(cpu), 1); }); } } else { std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack } } }); while (joinable() && !Emu.IsStopped()) { std::lock_guard lock(cs_main); inc = 1; const be_t put = ctrl->put; const be_t get = ctrl->get; if (put == get || !Emu.IsRunning()) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack continue; } const u32 cmd = ReadIO32(get); const u32 count = (cmd >> 18) & 0x7ff; if (Ini.RSXLogging.GetValue()) { LOG_NOTICE(Log::RSX, "%s (cmd=0x%x)", GetMethodName(cmd & 0xffff).c_str(), cmd); } if (cmd & CELL_GCM_METHOD_FLAG_JUMP) { u32 offs = cmd & 0x1fffffff; //LOG_WARNING(RSX, "rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", offs, m_ioAddress + get, cmd, get, put); ctrl->get.exchange(offs); continue; } if (cmd & CELL_GCM_METHOD_FLAG_CALL) { m_call_stack.push(get + 4); u32 offs = cmd & ~3; //LOG_WARNING(RSX, "rsx call(0x%x) #0x%x - 0x%x", offs, cmd, get); ctrl->get.exchange(offs); continue; } if (cmd == CELL_GCM_METHOD_FLAG_RETURN) { u32 get = m_call_stack.top(); m_call_stack.pop(); //LOG_WARNING(RSX, "rsx return(0x%x)", get); ctrl->get.exchange(get); continue; } if (cmd & CELL_GCM_METHOD_FLAG_NON_INCREMENT) { //LOG_WARNING(RSX, "rsx non increment cmd! 0x%x", cmd); inc = 0; } if (cmd == 0) //nop { ctrl->get += 4; continue; } auto args = vm::ps3::ptr::make((u32)RSXIOMem.RealAddr(get + 4)); for (u32 i = 0; i < count; i++) { rsx::method_registers[(cmd & 0xffff) + (i * 4 * inc)] = ARGS(i); } DoCmd(cmd, cmd & 0x3ffff, args.addr(), count); ctrl->get += (count + 1) * 4; } onexit_thread(); } u64 thread::timestamp() const { // Get timestamp, and convert it from microseconds to nanoseconds return get_system_time() * 1000; } void thread::reset() { rsx::method_registers[NV4097_SET_DEPTH_TEST_ENABLE] = false; rsx::method_registers[NV4097_SET_DEPTH_MASK] = 1; rsx::method_registers[NV4097_SET_DEPTH_FUNC] = 0x0201; m_set_dither = false; rsx::method_registers[NV4097_SET_COLOR_MASK] = -1; m_set_clip = false; m_set_depth_bounds_test = false; m_set_depth_bounds = false; m_set_scissor_horizontal = false; m_set_scissor_vertical = false; m_set_front_polygon_mode = false; m_set_back_polygon_mode = false; rsx::method_registers[NV4097_SET_BLEND_ENABLE_MRT] = 0; rsx::method_registers[NV4097_SET_BLEND_ENABLE] = false; m_set_two_side_light_enable = false; m_set_point_sprite_control = false; m_set_point_size = false; m_set_line_width = false; m_set_line_smooth = false; m_set_shade_mode = false; m_set_fog_mode = false; m_set_fog_params = false; m_set_clip_plane = false; rsx::method_registers[NV4097_SET_CULL_FACE_ENABLE] = false; rsx::method_registers[NV4097_SET_ALPHA_TEST_ENABLE] = false; rsx::method_registers[NV4097_SET_ALPHA_FUNC] = false; rsx::method_registers[NV4097_SET_ALPHA_REF] = false; m_set_poly_smooth = false; m_set_poly_offset_fill = false; m_set_poly_offset_line = false; m_set_poly_offset_point = false; m_set_poly_offset_mode = false; m_set_restart_index = false; m_set_specular = false; m_set_line_stipple = false; m_set_polygon_stipple = false; m_set_surface_clip_horizontal = false; m_set_surface_clip_vertical = false; m_clear_surface_mask = 0; m_begin_end = 0; for (uint i = 0; i < rsx::limits::textures_count; ++i) { textures[i].init(i); } } void thread::init(const u32 ioAddress, const u32 io_size, const u32 ctrlAddress, const u32 localAddress) { ctrl = vm::get_ptr(ctrlAddress); this->ioAddress = ioAddress; this->ioSize = io_size; m_ctrlAddress = ctrlAddress; local_mem_addr = localAddress; m_cur_vertex_prog = nullptr; m_used_gcm_commands.clear(); oninit(); start(WRAP_EXPR("RSXThread"), WRAP_EXPR(task())); } u32 thread::ReadIO32(u32 addr) { u32 value; if (!RSXIOMem.Read32(addr, &value)) { throw EXCEPTION("RSXIO memory not mapped (addr=0x%x)", addr); } return value; } void thread::WriteIO32(u32 addr, u32 value) { if (!RSXIOMem.Write32(addr, value)) { throw EXCEPTION("RSXIO memory not mapped (addr=0x%x)", addr); } } }