diff --git a/rpcs3/Emu/RSX/rsx_methods.cpp b/rpcs3/Emu/RSX/rsx_methods.cpp new file mode 100644 index 0000000000..7bbc95723f --- /dev/null +++ b/rpcs3/Emu/RSX/rsx_methods.cpp @@ -0,0 +1,839 @@ +#include "stdafx.h" +#include "rsx_methods.h" +#include "RSXThread.h" +#include "Emu/Memory/Memory.h" +#include "Emu/System.h" +#include "Emu/state.h" +#include "rsx_utils.h" +#include "Emu/SysCalls/Callback.h" +#include "Emu/SysCalls/CB_FUNC.h" + +namespace rsx +{ + u32 method_registers[0x10000 >> 2]; + rsx_method_t methods[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 nv406e + { + force_inline void set_reference(thread* rsx, u32 arg) + { + rsx->ctrl->ref.exchange(arg); + } + + force_inline void semaphore_acquire(thread* rsx, u32 arg) + { + //TODO: dma + while (vm::ps3::read32(rsx->label_addr + method_registers[NV406E_SEMAPHORE_OFFSET]) != arg) + { + if (Emu.IsStopped()) + break; + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + } + + force_inline void semaphore_release(thread* rsx, u32 arg) + { + //TODO: dma + vm::ps3::write32(rsx->label_addr + method_registers[NV406E_SEMAPHORE_OFFSET], arg); + } + } + + namespace nv4097 + { + force_inline void texture_read_semaphore_release(thread* rsx, u32 arg) + { + //TODO: dma + vm::ps3::write32(rsx->label_addr + method_registers[NV4097_SET_SEMAPHORE_OFFSET], arg); + } + + force_inline void back_end_write_semaphore_release(thread* rsx, u32 arg) + { + //TODO: dma + vm::ps3::write32(rsx->label_addr + method_registers[NV4097_SET_SEMAPHORE_OFFSET], + (arg & 0xff00ff00) | ((arg & 0xff) << 16) | ((arg >> 16) & 0xff)); + } + + //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->register_vertex_info[index]; + + info.type = vertex_data_type_from_element_type::type; + info.size = count; + info.frequency = 0; + info.stride = 0; + + auto& entry = rsx->register_vertex_data[index]; + + //find begin of data + size_t begin = id + index * element_size_in_words; + + size_t position = 0;//entry.size(); + entry.resize(position + element_size); + + memcpy(entry.data() + position, method_registers + begin, element_size); + } + + template + struct set_vertex_data4ub_m + { + force_inline static void impl(thread* rsx, u32 arg) + { + set_vertex_data_impl(rsx, arg); + } + }; + + template + struct set_vertex_data1f_m + { + force_inline static void impl(thread* rsx, u32 arg) + { + set_vertex_data_impl(rsx, arg); + } + }; + + template + struct set_vertex_data2f_m + { + force_inline static void impl(thread* rsx, u32 arg) + { + set_vertex_data_impl(rsx, arg); + } + }; + + template + struct set_vertex_data3f_m + { + force_inline static void impl(thread* rsx, u32 arg) + { + set_vertex_data_impl(rsx, arg); + } + }; + + template + struct set_vertex_data4f_m + { + force_inline static void impl(thread* rsx, u32 arg) + { + set_vertex_data_impl(rsx, arg); + } + }; + + template + struct set_vertex_data2s_m + { + force_inline static void impl(thread* rsx, u32 arg) + { + set_vertex_data_impl(rsx, arg); + } + }; + + template + struct set_vertex_data4s_m + { + force_inline static void impl(thread* rsx, u32 arg) + { + set_vertex_data_impl(rsx, arg); + } + }; + + template + struct set_vertex_data_array_format + { + force_inline static void impl(thread* rsx, u32 arg) + { + auto& info = rsx->vertex_arrays_info[index]; + info.unpack_array(arg); + } + }; + + force_inline void draw_arrays(thread* rsx, u32 arg) + { + rsx->draw_command = thread::Draw_command::draw_command_array; + u32 first = arg & 0xffffff; + u32 count = (arg >> 24) + 1; + + rsx->load_vertex_data(first, count); + } + + force_inline void draw_index_array(thread* rsx, u32 arg) + { + rsx->draw_command = thread::Draw_command::draw_command_indexed; + u32 first = arg & 0xffffff; + u32 count = (arg >> 24) + 1; + + rsx->load_vertex_data(first, count); + rsx->load_vertex_index_data(first, count); + } + + force_inline void draw_inline_array(thread* rsx, u32 arg) + { + rsx->draw_command = thread::Draw_command::draw_command_inlined_array; + rsx->draw_inline_vertex_array = true; + rsx->inline_vertex_array.push_back(arg); + } + + template + struct set_transform_constant + { + force_inline static void impl(thread* rsxthr, u32 arg) + { + u32 load = method_registers[NV4097_SET_TRANSFORM_CONSTANT_LOAD]; + + static const size_t count = 4; + static const size_t size = count * sizeof(f32); + + size_t reg = index / 4; + size_t subreg = index % 4; + + memcpy(rsxthr->transform_constants[load + reg].rgba + subreg, method_registers + NV4097_SET_TRANSFORM_CONSTANT + reg * count + subreg, sizeof(f32)); + } + }; + + template + struct set_transform_program + { + force_inline static void impl(thread* rsx, u32 arg) + { + u32& load = method_registers[NV4097_SET_TRANSFORM_PROGRAM_LOAD]; + + static const size_t count = 4; + static const size_t size = count * sizeof(u32); + + memcpy(rsx->transform_program + load++ * count, method_registers + NV4097_SET_TRANSFORM_PROGRAM + index * count, size); + } + }; + + force_inline void set_begin_end(thread* rsx, u32 arg) + { + if (arg) + { + rsx->draw_inline_vertex_array = false; + rsx->inline_vertex_array.clear(); + rsx->begin(); + return; + } + + if (!rsx->vertex_draw_count) + { + bool has_array = false; + + for (int i = 0; i < rsx::limits::vertex_count; ++i) + { + if (rsx->vertex_arrays_info[i].size > 0) + { + has_array = true; + break; + } + } + + if (!has_array) + { + u32 min_count = ~0; + + for (int i = 0; i < rsx::limits::vertex_count; ++i) + { + if (!rsx->register_vertex_info[i].size) + continue; + + u32 count = u32(rsx->register_vertex_data[i].size()) / + rsx::get_vertex_type_size(rsx->register_vertex_info[i].type) * rsx->register_vertex_info[i].size; + + if (count < min_count) + min_count = count; + } + + if (min_count && min_count < ~0) + { + rsx->vertex_draw_count = min_count; + } + } + } + + rsx->end(); + rsx->vertex_draw_count = 0; + } + + force_inline void get_report(thread* rsx, u32 arg) + { + u8 type = arg >> 24; + u32 offset = arg & 0xffffff; + + //TODO: use DMA + vm::ps3::ptr result = { rsx->local_mem_addr + offset, vm::addr }; + + result->timer = rsx->timestamp(); + + 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: + result->value = 0; + LOG_WARNING(RSX, "NV4097_GET_REPORT: Unimplemented type %d", type); + break; + + default: + result->value = 0; + LOG_ERROR(RSX, "NV4097_GET_REPORT: Bad type %d", type); + break; + } + + //result->padding = 0; + } + + force_inline void clear_report_value(thread* rsx, u32 arg) + { + switch (arg) + { + 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", arg); + break; + } + } + } + + namespace nv308a + { + template + struct color + { + force_inline static void impl(u32 arg) + { + u32 point = method_registers[NV308A_POINT]; + u16 x = point; + u16 y = point >> 16; + + if (y) + { + LOG_ERROR(RSX, "%s: y is not null (0x%x)", __FUNCTION__, y); + } + + u32 address = get_address(method_registers[NV3062_SET_OFFSET_DESTIN] + (x << 2) + index * 4, method_registers[NV3062_SET_CONTEXT_DMA_IMAGE_DESTIN]); + vm::ps3::write32(address, arg); + } + }; + } + + namespace nv3089 + { + never_inline void image_in(u32 arg) + { + u32 operation = method_registers[NV3089_SET_OPERATION]; + + u32 clip_x = method_registers[NV3089_CLIP_POINT] & 0xffff; + u32 clip_y = method_registers[NV3089_CLIP_POINT] >> 16; + u32 clip_w = method_registers[NV3089_CLIP_SIZE] & 0xffff; + u32 clip_h = method_registers[NV3089_CLIP_SIZE] >> 16; + + u32 out_x = method_registers[NV3089_IMAGE_OUT_POINT] & 0xffff; + u32 out_y = method_registers[NV3089_IMAGE_OUT_POINT] >> 16; + u32 out_w = method_registers[NV3089_IMAGE_OUT_SIZE] & 0xffff; + u32 out_h = method_registers[NV3089_IMAGE_OUT_SIZE] >> 16; + + u16 in_w = method_registers[NV3089_IMAGE_IN_SIZE]; + u16 in_h = method_registers[NV3089_IMAGE_IN_SIZE] >> 16; + u16 in_pitch = method_registers[NV3089_IMAGE_IN_FORMAT]; + u8 in_origin = method_registers[NV3089_IMAGE_IN_FORMAT] >> 16; + u8 in_inter = method_registers[NV3089_IMAGE_IN_FORMAT] >> 24; + u32 src_color_format = method_registers[NV3089_SET_COLOR_FORMAT]; + + f32 in_x = (method_registers[NV3089_IMAGE_IN] & 0xffff) / 16.f; + f32 in_y = (method_registers[NV3089_IMAGE_IN] >> 16) / 16.f; + + if (in_origin != CELL_GCM_TRANSFER_ORIGIN_CORNER) + { + LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: unknown origin (%d)", in_origin); + } + + if (in_inter != CELL_GCM_TRANSFER_INTERPOLATOR_ZOH && in_inter != CELL_GCM_TRANSFER_INTERPOLATOR_FOH) + { + LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: unknown inter (%d)", in_inter); + } + + if (operation != CELL_GCM_TRANSFER_OPERATION_SRCCOPY) + { + LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: unknown operation (%d)", operation); + } + + const u32 src_offset = method_registers[NV3089_IMAGE_IN_OFFSET]; + const u32 src_dma = method_registers[NV3089_SET_CONTEXT_DMA_IMAGE]; + + u32 dst_offset; + u32 dst_dma = 0; + u16 dst_color_format; + u32 out_pitch = 0; + u32 out_aligment = 64; + + switch (method_registers[NV3089_SET_CONTEXT_SURFACE]) + { + case CELL_GCM_CONTEXT_SURFACE2D: + dst_dma = method_registers[NV3062_SET_CONTEXT_DMA_IMAGE_DESTIN]; + dst_offset = method_registers[NV3062_SET_OFFSET_DESTIN]; + dst_color_format = method_registers[NV3062_SET_COLOR_FORMAT]; + out_pitch = method_registers[NV3062_SET_PITCH] >> 16; + out_aligment = method_registers[NV3062_SET_PITCH] & 0xffff; + break; + + case CELL_GCM_CONTEXT_SWIZZLE2D: + dst_dma = method_registers[NV309E_SET_CONTEXT_DMA_IMAGE]; + dst_offset = method_registers[NV309E_SET_OFFSET]; + dst_color_format = method_registers[NV309E_SET_FORMAT]; + break; + + default: + LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: unknown m_context_surface (0x%x)", method_registers[NV3089_SET_CONTEXT_SURFACE]); + return; + } + + u32 src_address = get_address(src_offset, src_dma); + u32 dst_address = get_address(dst_offset, dst_dma); + + u32 in_bpp = src_color_format == CELL_GCM_TRANSFER_SCALE_FORMAT_R5G6B5 ? 2 : 4; // bytes per pixel + u32 out_bpp = dst_color_format == CELL_GCM_TRANSFER_SURFACE_FORMAT_R5G6B5 ? 2 : 4; + + if (out_pitch == 0) + { + out_pitch = out_bpp * out_w; + } + + if (in_pitch == 0) + { + in_pitch = in_bpp * in_w; + } + + if (clip_w > out_w) + { + clip_w = out_w; + } + + if (clip_h > out_h) + { + clip_h = out_h; + } + + //LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: src = 0x%x, dst = 0x%x", src_address, dst_address); + + u8* pixels_src = vm::ps3::_ptr(src_address); + u8* pixels_dst = vm::ps3::_ptr(dst_address); + + if (dst_color_format != CELL_GCM_TRANSFER_SURFACE_FORMAT_R5G6B5 && + dst_color_format != CELL_GCM_TRANSFER_SURFACE_FORMAT_A8R8G8B8) + { + LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: unknown dst_color_format (%d)", dst_color_format); + } + + if (src_color_format != CELL_GCM_TRANSFER_SCALE_FORMAT_R5G6B5 && + src_color_format != CELL_GCM_TRANSFER_SCALE_FORMAT_A8R8G8B8) + { + LOG_ERROR(RSX, "NV3089_IMAGE_IN_SIZE: unknown src_color_format (%d)", src_color_format); + } + + //LOG_WARNING(RSX, "NV3089_IMAGE_IN_SIZE: SIZE=0x%08x, pitch=0x%x, offset=0x%x, scaleX=%f, scaleY=%f, CLIP_SIZE=0x%08x, OUT_SIZE=0x%08x", + // method_registers[NV3089_IMAGE_IN_SIZE], in_pitch, src_offset, double(1 << 20) / (method_registers[NV3089_DS_DX]), double(1 << 20) / (method_registers[NV3089_DT_DY]), + // method_registers[NV3089_CLIP_SIZE], method_registers[NV3089_IMAGE_OUT_SIZE]); + + std::unique_ptr temp1, temp2; + + AVPixelFormat in_format = src_color_format == CELL_GCM_TRANSFER_SCALE_FORMAT_R5G6B5 ? AV_PIX_FMT_RGB565BE : AV_PIX_FMT_ARGB; + AVPixelFormat out_format = dst_color_format == CELL_GCM_TRANSFER_SURFACE_FORMAT_R5G6B5 ? AV_PIX_FMT_RGB565BE : AV_PIX_FMT_ARGB; + + u32 out_offset = out_x * out_bpp + out_pitch * out_y; + + bool need_clip = method_registers[NV3089_CLIP_SIZE] != method_registers[NV3089_IMAGE_IN_SIZE] || method_registers[NV3089_CLIP_POINT]; + bool need_convert = out_format != in_format || out_w != in_w || out_h != in_h; + + u32 slice_h = (u32)(clip_h * (method_registers[NV3089_DS_DX] / 1048576.f)); + + if (slice_h) + { + if (clip_h < out_h) + { + --slice_h; + } + } + else + { + slice_h = clip_h; + } + + if (method_registers[NV3089_SET_CONTEXT_SURFACE] != CELL_GCM_CONTEXT_SWIZZLE2D) + { + if (need_convert || need_clip) + { + if (need_clip) + { + if (need_convert) + { + convert_scale_image(temp1, out_format, out_w, out_h, out_pitch, + pixels_src, in_format, in_w, in_h, in_pitch, slice_h, in_inter ? true : false); + + clip_image(pixels_dst + out_offset, temp1.get(), clip_x, clip_y, clip_w, clip_h, out_bpp, out_pitch, out_pitch); + } + else + { + clip_image(pixels_dst + out_offset, pixels_src, clip_x, clip_y, clip_w, clip_h, out_bpp, in_pitch, out_pitch); + } + } + else + { + convert_scale_image(pixels_dst + out_offset, out_format, out_w, out_h, out_pitch, + pixels_src, in_format, in_w, in_h, in_pitch, slice_h, in_inter ? true : false); + } + } + else + { + if (out_pitch != in_pitch || out_pitch != out_bpp * out_w) + { + for (u32 y = 0; y < out_h; ++y) + { + u8 *dst = pixels_dst + out_x * out_bpp + out_pitch * (y + out_y); + u8 *src = pixels_src + in_pitch * y; + + std::memmove(dst, src, out_w * out_bpp); + } + } + else + { + std::memmove(pixels_dst + out_offset, pixels_src, out_pitch * out_h); + } + } + } + else + { + if (need_convert || need_clip) + { + if (need_clip) + { + if (need_convert) + { + convert_scale_image(temp1, out_format, out_w, out_h, out_pitch, + pixels_src, in_format, in_w, in_h, in_pitch, slice_h, in_inter ? true : false); + + clip_image(temp2, temp1.get(), clip_x, clip_y, clip_w, clip_h, out_bpp, out_pitch, out_pitch); + } + else + { + clip_image(temp2, pixels_src, clip_x, clip_y, clip_w, clip_h, out_bpp, in_pitch, out_pitch); + } + } + else + { + convert_scale_image(temp2, out_format, out_w, out_h, out_pitch, + pixels_src, in_format, in_w, in_h, in_pitch, clip_h, in_inter ? true : false); + } + + pixels_src = temp2.get(); + } + + u8 sw_width_log2 = method_registers[NV309E_SET_FORMAT] >> 16; + u8 sw_height_log2 = method_registers[NV309E_SET_FORMAT] >> 24; + + // 0 indicates height of 1 pixel + sw_height_log2 = sw_height_log2 == 0 ? 1 : sw_height_log2; + + // swizzle based on destination size + u16 sw_width = 1 << sw_width_log2; + u16 sw_height = 1 << sw_height_log2; + + temp2.reset(new u8[out_bpp * sw_width * sw_height]); + + u8* linear_pixels = pixels_src; + u8* swizzled_pixels = temp2.get(); + + // Check and pad texture out if we are given non square texture for swizzle to be correct + if (sw_width != out_w || sw_height != out_h) + { + std::unique_ptr sw_temp(new u8[out_bpp * sw_width * sw_height]); + + switch (out_bpp) + { + case 1: + pad_texture(linear_pixels, sw_temp.get(), out_w, out_h, sw_width, sw_height); + break; + case 2: + pad_texture(linear_pixels, sw_temp.get(), out_w, out_h, sw_width, sw_height); + break; + case 4: + pad_texture(linear_pixels, sw_temp.get(), out_w, out_h, sw_width, sw_height); + break; + } + + linear_pixels = sw_temp.get(); + } + + switch (out_bpp) + { + case 1: + convert_linear_swizzle(linear_pixels, swizzled_pixels, sw_width, sw_height, false); + break; + case 2: + convert_linear_swizzle(linear_pixels, swizzled_pixels, sw_width, sw_height, false); + break; + case 4: + convert_linear_swizzle(linear_pixels, swizzled_pixels, sw_width, sw_height, false); + break; + } + + std::memcpy(pixels_dst, swizzled_pixels, out_bpp * sw_width * sw_height); + } + } + } + + namespace nv0039 + { + force_inline void buffer_notify(u32 arg) + { + const u32 inPitch = method_registers[NV0039_PITCH_IN]; + const u32 outPitch = method_registers[NV0039_PITCH_OUT]; + const u32 lineLength = method_registers[NV0039_LINE_LENGTH_IN]; + const u32 lineCount = method_registers[NV0039_LINE_COUNT]; + const u8 outFormat = method_registers[NV0039_FORMAT] >> 8; + const u8 inFormat = method_registers[NV0039_FORMAT]; + const u32 notify = arg; + + // 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) + { + std::memcpy( + vm::base(get_address(method_registers[NV0039_OFFSET_OUT], method_registers[NV0039_SET_CONTEXT_DMA_BUFFER_OUT])), + vm::base(get_address(method_registers[NV0039_OFFSET_IN], method_registers[NV0039_SET_CONTEXT_DMA_BUFFER_IN])), + 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", + method_registers[NV0039_OFFSET_IN], method_registers[NV0039_OFFSET_OUT], inPitch, outPitch, lineLength, lineCount, inFormat, outFormat, notify); + } + } + } + + void flip_command(thread* rsx, u32 arg) + { + if (user_asked_for_frame_capture) + { + rsx->capture_current_frame = true; + user_asked_for_frame_capture = false; + frame_debug.reset(); + } + else if (rsx->capture_current_frame) + { + rsx->capture_current_frame = false; + Emu.Pause(); + } + + rsx->gcm_current_buffer = arg; + rsx->flip(arg); + // After each flip PS3 system is executing a routine that changes registers value to some default. + // Some game use this default state (SH3). + rsx->reset(); + + rsx->last_flip_time = get_system_time() - 1000000; + rsx->gcm_current_buffer = arg; + rsx->flip_status = 0; + + if (rsx->flip_handler) + { + Emu.GetCallbackManager().Async([func = rsx->flip_handler](PPUThread& ppu) + { + func(ppu, 1); + }); + } + + rsx->sem_flip.post_and_wait(); + + //sync + double limit; + switch (rpcs3::state.config.rsx.frame_limit.value()) + { + case rsx_frame_limit::_50: limit = 50.; break; + case rsx_frame_limit::_59_94: limit = 59.94; break; + case rsx_frame_limit::_30: limit = 30.; break; + case rsx_frame_limit::_60: limit = 60.; break; + case rsx_frame_limit::Auto: limit = rsx->fps_limit; break; //TODO + + case rsx_frame_limit::Off: + default: + return; + } + + std::this_thread::sleep_for(std::chrono::milliseconds((s64)(1000.0 / limit - rsx->timer_sync.GetElapsedTimeInMilliSec()))); + rsx->timer_sync.Start(); + rsx->local_transform_constants.clear(); + } + + void user_command(thread* rsx, u32 arg) + { + if (rsx->user_handler) + { + Emu.GetCallbackManager().Async([func = rsx->user_handler, arg](PPUThread& ppu) + { + func(ppu, arg); + }); + } + else + { + throw EXCEPTION("User handler not set"); + } + } + + struct __rsx_methods_t + { + using rsx_impl_method_t = void(*)(u32); + + template + force_inline static void call_impl_func(thread *rsx, u32 arg) + { + impl_func(rsx, arg); + } + + template + force_inline static void call_impl_func(thread *rsx, u32 arg) + { + impl_func(arg); + } + + template + static void wrapper(thread *rsx, u32 arg) + { + // try process using gpu + if (rsx->do_method(id, arg)) + { + if (rsx->capture_current_frame && id == NV4097_CLEAR_SURFACE) + rsx->capture_frame("clear"); + return; + } + + // not handled by renderer + // try process using cpu + if (impl_func != nullptr) + call_impl_func(rsx, arg); + } + + template class T, int index = 0> + struct bind_range_impl_t + { + force_inline static void impl() + { + bind_range_impl_t::impl(); + bind::impl>(); + } + }; + + template class T> + struct bind_range_impl_t + { + force_inline static void impl() + { + } + }; + + template class T, int index = 0> + force_inline static void bind_range() + { + bind_range_impl_t::impl(); + } + + [[noreturn]] never_inline static void bind_redefinition_error(int id) + { + throw EXCEPTION("RSX method implementation redefinition (0x%04x)", id); + } + + template + static void bind_impl() + { + if (methods[id]) + { + bind_redefinition_error(id); + } + + methods[id] = wrapper; + } + + template + static void bind_cpu_only_impl() + { + if (methods[id]) + { + bind_redefinition_error(id); + } + + methods[id] = call_impl_func; + } + + template static void bind() { bind_impl(); } + template static void bind() { bind_impl(); } + + //do not try process on gpu + template static void bind_cpu_only() { bind_cpu_only_impl(); } + //do not try process on gpu + template static void bind_cpu_only() { bind_cpu_only_impl(); } + + __rsx_methods_t() + { + // NV406E + bind_cpu_only(); + bind(); + bind(); + + // NV4097 + bind(); + bind(); + bind(); + bind(); + bind(); + bind(); + bind(); + bind_range(); + bind_range(); + bind_range(); + bind_range(); + bind_range(); + bind_range(); + bind_range(); + bind_range(); + bind_range(); + bind_range(); + bind_cpu_only(); + bind_cpu_only(); + + //NV308A + bind_range(); + bind_range(); + + //NV3089 + bind(); + + //NV0039 + bind(); + + // custom methods + bind_cpu_only(); + bind_cpu_only(); + } + } __rsx_methods; +} diff --git a/rpcs3/Emu/RSX/rsx_methods.h b/rpcs3/Emu/RSX/rsx_methods.h new file mode 100644 index 0000000000..ef34418dee --- /dev/null +++ b/rpcs3/Emu/RSX/rsx_methods.h @@ -0,0 +1,69 @@ +#pragma once + +namespace rsx +{ + //TODO + union alignas(4) method_registers_t + { + u8 _u8[0x10000]; + u32 _u32[0x10000 >> 2]; + /* + struct alignas(4) + { + u8 pad[NV4097_SET_TEXTURE_OFFSET - 4]; + + struct alignas(4) texture_t + { + u32 offset; + + union format_t + { + u32 _u32; + + struct + { + u32: 1; + u32 location : 1; + u32 cubemap : 1; + u32 border_type : 1; + u32 dimension : 4; + u32 format : 8; + u32 mipmap : 16; + }; + } format; + + union address_t + { + u32 _u32; + + struct + { + u32 wrap_s : 4; + u32 aniso_bias : 4; + u32 wrap_t : 4; + u32 unsigned_remap : 4; + u32 wrap_r : 4; + u32 gamma : 4; + u32 signed_remap : 4; + u32 zfunc : 4; + }; + } address; + + u32 control0; + u32 control1; + u32 filter; + u32 image_rect; + u32 border_color; + } textures[limits::textures_count]; + }; + */ + u32& operator[](int index) + { + return _u32[index >> 2]; + } + }; + + using rsx_method_t = void(*)(class thread*, u32); + extern u32 method_registers[0x10000 >> 2]; + extern rsx_method_t methods[0x10000 >> 2]; +}