rpcs3/rpcs3/Emu/RSX/RSXThread.cpp
2015-10-13 00:04:19 +02:00

2306 lines
50 KiB
C++

#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<typename Type> struct vertex_data_type_from_element_type;
template<> struct vertex_data_type_from_element_type<float> { enum { type = CELL_GCM_VERTEX_F }; };
template<> struct vertex_data_type_from_element_type<f16> { enum { type = CELL_GCM_VERTEX_SF }; };
template<> struct vertex_data_type_from_element_type<u8> { enum { type = CELL_GCM_VERTEX_UB }; };
template<> struct vertex_data_type_from_element_type<u16> { enum { type = CELL_GCM_VERTEX_S1 }; };
namespace nv4097
{
//fire only when all data passed to rsx cmd buffer
template<u32 id, u32 index, int count, typename type>
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>::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<u32 index>
force_inline void set_vertex_data4ub_m(thread* rsx, u32 arg)
{
set_vertex_data_impl<NV4097_SET_VERTEX_DATA4UB_M, index, 4, u8>(rsx, arg);
}
template<u32 index>
force_inline void set_vertex_data1f_m(thread* rsx, u32 arg)
{
set_vertex_data_impl<NV4097_SET_VERTEX_DATA1F_M, index, 1, f32>(rsx, arg);
}
template<u32 index>
force_inline void set_vertex_data2f_m(thread* rsx, u32 arg)
{
set_vertex_data_impl<NV4097_SET_VERTEX_DATA1F_M, index, 2, f32>(rsx, arg);
}
template<u32 index>
force_inline void set_vertex_data3f_m(thread* rsx, u32 arg)
{
set_vertex_data_impl<NV4097_SET_VERTEX_DATA1F_M, index, 3, f32>(rsx, arg);
}
template<u32 index>
force_inline void set_vertex_data4f_m(thread* rsx, u32 arg)
{
set_vertex_data_impl<NV4097_SET_VERTEX_DATA1F_M, index, 4, f32>(rsx, arg);
}
template<u32 index>
force_inline void set_vertex_data2s_m(thread* rsx, u32 arg)
{
set_vertex_data_impl<NV4097_SET_VERTEX_DATA2S_M, index, 2, u16>(rsx, arg);
}
template<u32 index>
force_inline void set_vertex_data4s_m(thread* rsx, u32 arg)
{
set_vertex_data_impl<NV4097_SET_VERTEX_DATA4S_M, index, 4, u16>(rsx, arg);
}
template<u32 index>
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<u32>::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<u32>::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<PPUThread&>(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<index>(this, ARGS(0)); \
break; \
case offset + step: \
func<index + 1>(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<count; ++c)
{
u32 ac = ARGS(c);
const u32 first = ac & 0xffffff;
const u32 _count = (ac >> 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<count; ++c)
{
const u32 first = ARGS(c) & 0xffffff;
const u32 _count = (ARGS(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<count; ++id)
{
const u32 x = ARGS(i); i++;
const u32 y = ARGS(i); i++;
const u32 z = ARGS(i); i++;
const u32 w = ARGS(i); i++;
color4f c((float&)x, (float&)y, (float&)z, (float&)w);
transform_constants[id] = c;
}
break;
}
// Invalidation
case NV4097_INVALIDATE_L2:
{
if (u32 value = ARGS(0))
{
LOG_WARNING(RSX, "TODO: NV4097_INVALIDATE_L2: 0x%x", value);
}
break;
}
case NV4097_SET_NO_PARANOID_TEXTURE_FETCHES:
{
// Nothing to do here
break;
}
case NV4097_INVALIDATE_VERTEX_CACHE_FILE:
{
// Nothing to do here
break;
}
case NV4097_INVALIDATE_VERTEX_FILE:
{
// Nothing to do here
break;
}
case NV4097_INVALIDATE_ZCULL:
{
if (u32 value = ARGS(0))
{
LOG_WARNING(RSX, "TODO: NV4097_INVALIDATE_ZCULL: 0x%x", value);
}
break;
}
// Logic Ops
case NV4097_SET_LOGIC_OP_ENABLE:
notifyBlendStateChange();
break;
case NV4097_SET_LOGIC_OP:
notifyBlendStateChange();
break;
// Dithering
case NV4097_SET_DITHER_ENABLE:
{
m_set_dither = ARGS(0) ? true : false;
break;
}
// Stencil testing
case NV4097_SET_STENCIL_TEST_ENABLE:
notifyDepthStencilStateChange();
break;
case NV4097_SET_TWO_SIDED_STENCIL_TEST_ENABLE:
break;
case NV4097_SET_TWO_SIDE_LIGHT_EN:
{
m_set_two_side_light_enable = ARGS(0) ? true : false;
break;
}
case NV4097_SET_STENCIL_MASK:
notifyDepthStencilStateChange();
break;
case NV4097_SET_STENCIL_FUNC:
notifyDepthStencilStateChange();
break;
case NV4097_SET_STENCIL_FUNC_REF:
notifyDepthStencilStateChange();
break;
case NV4097_SET_STENCIL_FUNC_MASK:
notifyDepthStencilStateChange();
break;
case NV4097_SET_STENCIL_OP_FAIL:
notifyDepthStencilStateChange();
break;
case NV4097_SET_BACK_STENCIL_MASK:
notifyDepthStencilStateChange();
break;
case NV4097_SET_BACK_STENCIL_FUNC:
notifyDepthStencilStateChange();
break;
case NV4097_SET_BACK_STENCIL_FUNC_REF:
notifyDepthStencilStateChange();
break;
case NV4097_SET_BACK_STENCIL_FUNC_MASK:
notifyDepthStencilStateChange();
break;
case NV4097_SET_BACK_STENCIL_OP_FAIL:
notifyDepthStencilStateChange();
break;
case NV4097_SET_SCULL_CONTROL:
{
if (u32 value = ARGS(0))
{
LOG_WARNING(RSX, "TODO: NV4097_SET_SCULL_CONTROL: 0x%x", value);
}
break;
}
// Primitive restart index
case NV4097_SET_RESTART_INDEX_ENABLE:
{
m_set_restart_index = ARGS(0) ? true : false;
break;
}
case NV4097_SET_RESTART_INDEX:
{
m_restart_index = ARGS(0);
break;
}
// Point size
case NV4097_SET_POINT_SIZE:
{
m_set_point_size = true;
const u32 a0 = ARGS(0);
m_point_size = (float&)a0;
break;
}
// Point sprite
case NV4097_SET_POINT_PARAMS_ENABLE:
{
if (u32 value = ARGS(0))
{
LOG_WARNING(RSX, "TODO: NV4097_SET_POINT_PARAMS_ENABLE: 0x%x", value);
}
break;
}
case NV4097_SET_POINT_SPRITE_CONTROL:
{
m_set_point_sprite_control = ARGS(0) ? true : false;
// TODO:
//(cmd)[1] = CELL_GCM_ENDIAN_SWAP((enable) | ((rmode) << 1) | (texcoordMask));
break;
}
// Lighting
case NV4097_SET_SPECULAR_ENABLE:
{
m_set_specular = ARGS(0) ? true : false;
break;
}
// Scissor
case NV4097_SET_SCISSOR_HORIZONTAL:
{
m_set_scissor_horizontal = true;
m_scissor_x = ARGS(0) & 0xffff;
m_scissor_w = ARGS(0) >> 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<CellGcmDisplayInfo>(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<void>(rsx::get_address(outOffset, 0)), vm::get_ptr<void>(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<u8>(rsx::get_address(offset, m_context_dma_img_src - 0xfeed0000));
u8* pixels_dst = vm::get_ptr<u8>(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<u8[]> 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<SwsContext, void(*)(SwsContext*)> 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<s32>(std::max<s32>(m_color_conv_out_x, 0), m_color_conv_out_w);
size_t dst_max = std::min<s32>(std::max<s32>((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<s32>(m_color_conv_clip_h, out_h))
{
// source line
u8* src_line = pixels_src + y * out_bpp * out_w + std::min<s32>(std::max<s32>(m_color_conv_clip_x, 0), m_color_conv_clip_w);
size_t src_max = std::min<s32>(std::max<s32>((s32)m_color_conv_clip_w - m_color_conv_clip_x, 0), m_color_conv_clip_w) * out_bpp;
std::pair<u8*, size_t>
z0 = { src_line + 0, std::min<size_t>(dst_max, std::max<s64>(0, m_color_conv_clip_x)) },
d0 = { src_line + z0.second, std::min<size_t>(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<PPUThread&>(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<PPUThread&>(cpu), 1);
});
}
}
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
}
}
});
while (joinable() && !Emu.IsStopped())
{
std::lock_guard<std::mutex> lock(cs_main);
inc = 1;
const be_t<u32> put = ctrl->put;
const be_t<u32> 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<u32>::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<CellGcmControl>(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);
}
}
}