#include "stdafx.h" #include "GLGSRender.h" #include "Emu/Cell/PPCInstrTable.h" #define CMD_DEBUG 0 #define DUMP_VERTEX_DATA 0 #if CMD_DEBUG #define CMD_LOG ConLog.Write #else #define CMD_LOG(...) #endif gcmBuffer gcmBuffers[8]; void printGlError(GLenum err, const char* situation) { if(err != GL_NO_ERROR) { ConLog.Error("%s: opengl error 0x%04x", situation, err); Emu.Pause(); } } void checkForGlError(const char* situation) { printGlError(glGetError(), situation); } #if 0 #define checkForGlError(x) /*x*/ #endif GLGSFrame::GLGSFrame() : GSFrame(nullptr, "GSFrame[OpenGL]") , m_frames(0) { canvas = new wxGLCanvas(this, wxID_ANY, NULL); canvas->SetSize(GetClientSize()); canvas->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(GSFrame::OnLeftDclick), (wxObject*)0, this); } void GLGSFrame::Flip() { if(!canvas) return; canvas->SetCurrent(); static Timer fps_t; canvas->SwapBuffers(); m_frames++; if(fps_t.GetElapsedTimeInSec() >= 0.5) { SetTitle(wxString::Format("FPS: %.2f", (double)m_frames / fps_t.GetElapsedTimeInSec())); m_frames = 0; fps_t.Start(); } } void GLGSFrame::OnSize(wxSizeEvent& event) { if(canvas) canvas->SetSize(GetClientSize()); event.Skip(); } void GLGSFrame::SetViewport(int x, int y, u32 w, u32 h) { /* //ConLog.Warning("SetViewport(x=%d, y=%d, w=%d, h=%d)", x, y, w, h); const wxSize client = GetClientSize(); const wxSize viewport = AspectRatio(client, wxSize(w, h)); const int vx = (client.GetX() - viewport.GetX()) / 2; const int vy = (client.GetY() - viewport.GetY()) / 2; glViewport(vx + x, vy + y, viewport.GetWidth(), viewport.GetHeight()); */ } GLGSRender::GLGSRender() : GSRender() , m_frame(nullptr) , m_rsx_thread(nullptr) , m_fp_buf_num(-1) , m_vp_buf_num(-1) { m_draw = false; m_frame = new GLGSFrame(); } GLGSRender::~GLGSRender() { Close(); m_frame->Close(); } void GLGSRender::Enable(bool enable, const u32 cap) { if(enable) { glEnable(cap); } else { glDisable(cap); } } GLRSXThread::GLRSXThread(wxWindow* parent) : ThreadBase(true, "OpenGL Thread") , m_parent(parent) { } extern CellGcmContextData current_context; void GLRSXThread::Task() { ConLog.Write("GL RSX thread entry"); GLGSRender& p = *(GLGSRender*)m_parent; wxGLContext cntxt(p.m_frame->GetCanvas()); p.m_frame->GetCanvas()->SetCurrent(cntxt); InitProcTable(); glEnable(GL_TEXTURE_2D); glEnable(GL_SCISSOR_TEST); glSwapInterval(Ini.GSVSyncEnable.GetValue() ? 1 : 0); bool draw = true; u32 drawed = 0; u32 skipped = 0; p.Init(); while(!TestDestroy() && p.m_frame && !p.m_frame->IsBeingDeleted()) { wxCriticalSectionLocker lock(p.m_cs_main); const u32 get = re(p.m_ctrl->get); const u32 put = re(p.m_ctrl->put); if(put == get || !Emu.IsRunning()) { if(put == get) SemaphorePostAndWait(p.m_sem_flush); Sleep(1); continue; } //ConLog.Write("addr = 0x%x", p.m_ioAddress + get); const u32 cmd = Memory.Read32(p.m_ioAddress + get); const u32 count = (cmd >> 18) & 0x7ff; //if(cmd == 0) continue; if(cmd & CELL_GCM_METHOD_FLAG_JUMP) { u32 addr = cmd & ~(CELL_GCM_METHOD_FLAG_JUMP | CELL_GCM_METHOD_FLAG_NON_INCREMENT); //ConLog.Warning("rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", addr, p.m_ioAddress + get, cmd, get, put); re(p.m_ctrl->get, addr); continue; } if(cmd & CELL_GCM_METHOD_FLAG_CALL) { call_stack.Push(get + 4); u32 offs = cmd & ~CELL_GCM_METHOD_FLAG_CALL; u32 addr = p.m_ioAddress + offs; //ConLog.Warning("rsx call(0x%x) #0x%x - 0x%x - 0x%x", offs, addr, cmd, get); p.m_ctrl->get = re32(offs); continue; } if(cmd == CELL_GCM_METHOD_FLAG_RETURN) { //ConLog.Warning("rsx return!"); u32 get = call_stack.Pop(); //ConLog.Warning("rsx return(0x%x)", get); p.m_ctrl->get = re32(get); continue; } if(cmd & CELL_GCM_METHOD_FLAG_NON_INCREMENT) { //ConLog.Warning("non increment cmd! 0x%x", cmd); } if(cmd == 0) { ConLog.Warning("null cmd: addr=0x%x, put=0x%x, get=0x%x", p.m_ioAddress + get, re(p.m_ctrl->put), get); Emu.Pause(); continue; } mem32_ptr_t args(p.m_ioAddress + get + 4); if(!draw) { if((cmd & 0x3ffff) == NV406E_SET_REFERENCE) { p.m_ctrl->ref = re32(args[0]); } } else { p.DoCmd(cmd, cmd & 0x3ffff, args, count); } re(p.m_ctrl->get, get + (count + 1) * 4); //memset(Memory.GetMemFromAddr(p.m_ioAddress + get), 0, (count + 1) * 4); } ConLog.Write("GL RSX thread exit..."); call_stack.Clear(); p.CloseOpenGL(); } void GLGSRender::Init(const u32 ioAddress, const u32 ioSize, const u32 ctrlAddress, const u32 localAddress) { if(m_frame->IsShown()) return; m_draw_frames = 1; m_skip_frames = 0; m_width = 720; m_height = 576; m_frame->Show(); m_ioAddress = ioAddress; m_ioSize = ioSize; m_ctrlAddress = ctrlAddress; m_local_mem_addr = localAddress; m_ctrl = (CellGcmControl*)Memory.GetMemFromAddr(m_ctrlAddress); m_cur_vertex_prog = nullptr; m_cur_shader_prog = nullptr; m_cur_shader_prog_num = 0; (m_rsx_thread = new GLRSXThread(this))->Start(); } void GLGSRender::Draw() { m_draw = true; //if(m_frame && !m_frame->IsBeingDeleted()) m_frame->Flip(); } void GLGSRender::Close() { if(m_rsx_thread) { m_rsx_thread->Stop(); delete m_rsx_thread; } if(m_frame->IsShown()) m_frame->Hide(); m_ctrl = nullptr; } void GLGSRender::EnableVertexData(bool indexed_draw) { static u32 offset_list[16]; u32 cur_offset = 0; for(u32 i=0; i<16; ++i) { offset_list[i] = cur_offset; if(!m_vertex_data[i].IsEnabled() || !m_vertex_data[i].addr) continue; cur_offset += m_vertex_data[i].data.GetCount(); const u32 pos = m_vdata.GetCount(); m_vdata.InsertRoomEnd(m_vertex_data[i].data.GetCount()); memcpy(&m_vdata[pos], &m_vertex_data[i].data[0], m_vertex_data[i].data.GetCount()); } m_vao.Create(); m_vao.Bind(); checkForGlError("initializing vao"); m_vbo.Create(indexed_draw ? 2 : 1); m_vbo.Bind(0); m_vbo.SetData(&m_vdata[0], m_vdata.GetCount()); if(indexed_draw) { m_vbo.Bind(GL_ELEMENT_ARRAY_BUFFER, 1); m_vbo.SetData(GL_ELEMENT_ARRAY_BUFFER, &m_indexed_array.m_data[0], m_indexed_array.m_data.GetCount()); } checkForGlError("initializing vbo"); #if DUMP_VERTEX_DATA wxFile dump("VertexDataArray.dump", wxFile::write); #endif for(u32 i=0; i= 1 && m_vertex_data[i].type <= 7) { if(!m_vertex_data[i].addr) { switch(m_vertex_data[i].type) { case 5: case 1: switch(m_vertex_data[i].size) { case 1: glVertexAttrib1s(i, (GLshort&)m_vertex_data[i].data[0]); break; case 2: glVertexAttrib2sv(i, (GLshort*)&m_vertex_data[i].data[0]); break; case 3: glVertexAttrib3sv(i, (GLshort*)&m_vertex_data[i].data[0]); break; case 4: glVertexAttrib4sv(i, (GLshort*)&m_vertex_data[i].data[0]); break; } break; case 2: switch(m_vertex_data[i].size) { case 1: glVertexAttrib1f(i, (GLfloat&)m_vertex_data[i].data[0]); break; case 2: glVertexAttrib2fv(i, (GLfloat*)&m_vertex_data[i].data[0]); break; case 3: glVertexAttrib3fv(i, (GLfloat*)&m_vertex_data[i].data[0]); break; case 4: glVertexAttrib4fv(i, (GLfloat*)&m_vertex_data[i].data[0]); break; } break; case 6: case 4: glVertexAttrib4ubv(i, (GLubyte*)&m_vertex_data[i].data[0]); break; } checkForGlError("glVertexAttrib"); } else { u32 gltype = gl_types[m_vertex_data[i].type - 1]; bool normalized = gl_normalized[m_vertex_data[i].type - 1]; glEnableVertexAttribArray(i); checkForGlError("glEnableVertexAttribArray"); glVertexAttribPointer(i, m_vertex_data[i].size, gltype, normalized, 0, (void*)offset_list[i]); checkForGlError("glVertexAttribPointer"); } } } } void GLGSRender::DisableVertexData() { m_vdata.Clear(); for(u32 i=0; ioffset + 2 * 4 * 4; const wxString name = wxString::Format("fc%u", id); const int l = m_program.GetLocation(name); checkForGlError("glGetUniformLocation " + name); //ConLog.Write(name + " x: %.02f y: %.02f z: %.02f w: %.02f", c.x, c.y, c.z, c.w); glUniform4f(l, c.x, c.y, c.z, c.w); checkForGlError("glUniform4f " + name + wxString::Format(" %d [%f %f %f %f]", l, c.x, c.y, c.z, c.w)); } } #define case_16(a, m) \ case a + m: \ case a + m * 2: \ case a + m * 3: \ case a + m * 4: \ case a + m * 5: \ case a + m * 6: \ case a + m * 7: \ case a + m * 8: \ case a + m * 9: \ case a + m * 10: \ case a + m * 11: \ case a + m * 12: \ case a + m * 13: \ case a + m * 14: \ case a + m * 15: \ index = (cmd - a) / m; \ case a \ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_ptr_t& args, const u32 count) { #if CMD_DEBUG wxString debug = GetMethodName(cmd); debug += "("; for(u32 i=0; i= m_skip_frames) { skipped = 0; draw = true; } } else { if(drawed++ >= m_draw_frames) { drawed = 0; draw = false; } } }*/ //if(draw) { //if(m_frame->GetClientSize() != wxSize(m_viewport_w, m_viewport_h)) // m_frame->SetClientSize(m_viewport_w, m_viewport_h); if(m_fbo.IsCreated()) { m_fbo.Bind(GL_READ_FRAMEBUFFER); GLfbo::Bind(GL_DRAW_FRAMEBUFFER, 0); GLfbo::Blit( m_surface_clip_x, m_surface_clip_y, m_surface_clip_x + m_surface_clip_w, m_surface_clip_y + m_surface_clip_h, m_surface_clip_x, m_surface_clip_y, m_surface_clip_x + m_surface_clip_w, m_surface_clip_y + m_surface_clip_h, GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); m_fbo.Bind(); } m_frame->Flip(); } m_gcm_current_buffer = args[0]; m_flip_status = 0; if(m_flip_handler) { m_flip_handler.Handle(1, 0, 0); m_flip_handler.Branch(false); } SemaphorePostAndWait(m_sem_flip); } } break; case NV4097_NO_OPERATION: break; case NV406E_SET_REFERENCE: m_ctrl->ref = re32(args[0]); break; case_16(NV4097_SET_TEXTURE_OFFSET, 0x20): { GLTexture& tex = m_frame->GetTexture(index); const u32 offset = args[0]; u32 a1 = args[1]; u8 location = (a1 & 0x3) - 1; const bool cubemap = (a1 >> 2) & 0x1; const u8 dimension = (a1 >> 4) & 0xf; const u8 format = (a1 >> 8) & 0xff; const u16 mipmap = (a1 >> 16) & 0xffff; CMD_LOG("index = %d, offset=0x%x, location=0x%x, cubemap=0x%x, dimension=0x%x, format=0x%x, mipmap=0x%x", index, offset, location, cubemap, dimension, format, mipmap); if(location == 2) { ConLog.Error("Bad texture location."); location = 1; } u32 tex_addr = GetAddress(offset, location); //ConLog.Warning("texture addr = 0x%x #offset = 0x%x, location=%d", tex_addr, offset, location); tex.SetOffset(tex_addr); tex.SetFormat(cubemap, dimension, format, mipmap); } break; case_16(NV4097_SET_TEXTURE_CONTROL0, 0x20): { GLTexture& tex = m_frame->GetTexture(index); u32 a0 = args[0]; bool enable = a0 >> 31 ? true : false; u16 minlod = (a0 >> 19) & 0xfff; u16 maxlod = (a0 >> 7) & 0xfff; u8 maxaniso = (a0 >> 2) & 0x7; tex.SetControl0(enable, minlod, maxlod, maxaniso); } break; case_16(NV4097_SET_VERTEX_DATA4UB_M, 4): { u32 v = args[0]; u8 v0 = v; u8 v1 = v >> 8; u8 v2 = v >> 16; u8 v3 = v >> 24; m_vertex_data[index].size = 4; m_vertex_data[index].type = 4; m_vertex_data[index].data.AddCpy(v0); m_vertex_data[index].data.AddCpy(v1); m_vertex_data[index].data.AddCpy(v2); m_vertex_data[index].data.AddCpy(v3); //ConLog.Warning("index = %d, v0 = 0x%x, v1 = 0x%x, v2 = 0x%x, v3 = 0x%x", index, v0, v1, v2, v3); } break; case_16(NV4097_SET_VERTEX_DATA2F_M, 8): { u32 a0 = args[0]; u32 a1 = args[1]; float v0 = (float&)a0; float v1 = (float&)a1; m_vertex_data[index].type = 2; m_vertex_data[index].size = 2; m_vertex_data[index].data.SetCount(sizeof(float) * 2); (float&)m_vertex_data[index].data[sizeof(float)*0] = v0; (float&)m_vertex_data[index].data[sizeof(float)*1] = v1; //ConLog.Warning("index = %d, v0 = %f, v1 = %f", index, v0, v1); } break; case_16(NV4097_SET_VERTEX_DATA4F_M, 16): { u32 a0 = args[0]; u32 a1 = args[1]; u32 a2 = args[2]; u32 a3 = args[3]; float v0 = (float&)a0; float v1 = (float&)a1; float v2 = (float&)a2; float v3 = (float&)a3; m_vertex_data[index].type = 2; m_vertex_data[index].size = 4; m_vertex_data[index].data.SetCount(sizeof(float) * 4); (float&)m_vertex_data[index].data[sizeof(float)*0] = v0; (float&)m_vertex_data[index].data[sizeof(float)*1] = v1; (float&)m_vertex_data[index].data[sizeof(float)*2] = v2; (float&)m_vertex_data[index].data[sizeof(float)*3] = v3; //ConLog.Warning("index = %d, v0 = %f, v1 = %f, v2 = %f, v3 = %f", index, v0, v1, v2, v3); } break; case_16(NV4097_SET_TEXTURE_CONTROL1, 0x20): { GLTexture& tex = m_frame->GetTexture(index); tex.SetControl1(args[0]); } break; case_16(NV4097_SET_TEXTURE_CONTROL3, 4): { GLTexture& tex = m_frame->GetTexture(index); u32 a0 = args[0]; u32 pitch = a0 & 0xFFFFF; u16 depth = a0 >> 20; tex.SetControl3(pitch, depth); } break; case_16(NV4097_SET_TEXTURE_FILTER, 0x20): { GLTexture& tex = m_frame->GetTexture(index); u32 a0 = args[0]; u16 bias = a0 & 0x1fff; u8 conv = (a0 >> 13) & 0xf; u8 min = (a0 >> 16) & 0x7; u8 mag = (a0 >> 24) & 0x7; u8 a_signed = (a0 >> 28) & 0x1; u8 r_signed = (a0 >> 29) & 0x1; u8 g_signed = (a0 >> 30) & 0x1; u8 b_signed = (a0 >> 31) & 0x1; tex.SetFilter(bias, min, mag, conv, a_signed, r_signed, g_signed, b_signed); } break; case_16(NV4097_SET_TEXTURE_ADDRESS, 0x20): { GLTexture& tex = m_frame->GetTexture(index); u32 a0 = args[0]; u8 wraps = a0 & 0xf; u8 aniso_bias = (a0 >> 4) & 0xf; u8 wrapt = (a0 >> 8) & 0xf; u8 unsigned_remap = (a0 >> 12) & 0xf; u8 wrapr = (a0 >> 16) & 0xf; u8 gamma = (a0 >> 20) & 0xf; u8 signed_remap = (a0 >> 24) & 0xf; u8 zfunc = a0 >> 28; tex.SetAddress(wraps, wrapt, wrapr, unsigned_remap, zfunc, gamma, aniso_bias, signed_remap); } break; case_16(NV4097_SET_TEX_COORD_CONTROL, 4): //TODO break; case_16(NV4097_SET_TEXTURE_IMAGE_RECT, 32): { GLTexture& tex = m_frame->GetTexture(index); const u16 height = args[0] & 0xffff; const u16 width = args[0] >> 16; CMD_LOG("width=%d, height=%d", width, height); tex.SetRect(width, height); } break; case NV4097_SET_SURFACE_FORMAT: { u32 a0 = args[0]; m_set_surface_format = true; m_surface_color_format = a0 & 0x1f; m_surface_depth_format = (a0 >> 5) & 0x7; m_surface_type = (a0 >> 8) & 0xf; m_surface_antialias = (a0 >> 12) & 0xf; m_surface_width = (a0 >> 16) & 0xff; m_surface_height = (a0 >> 24) & 0xff; m_surface_pitch_a = args[1]; m_surface_offset_a = args[2]; m_surface_offset_z = args[3]; m_surface_offset_b = args[4]; m_surface_pitch_b = args[5]; gcmBuffer* buffers = (gcmBuffer*)Memory.GetMemFromAddr(m_gcm_buffers_addr); m_width = re(buffers[m_gcm_current_buffer].width); m_height = re(buffers[m_gcm_current_buffer].height); } break; case NV4097_SET_COLOR_MASK_MRT: { } break; case NV4097_SET_COLOR_MASK: { const u32 flags = args[0]; m_set_color_mask = true; m_color_mask_a = flags & 0x1000000 ? true : false; m_color_mask_r = flags & 0x0010000 ? true : false; m_color_mask_g = flags & 0x0000100 ? true : false; m_color_mask_b = flags & 0x0000001 ? true : false; } break; case NV4097_SET_ALPHA_TEST_ENABLE: m_set_alpha_test = args[0] ? true : false; break; case NV4097_SET_BLEND_ENABLE: m_set_blend = args[0] ? true : false; break; case NV4097_SET_DEPTH_BOUNDS_TEST_ENABLE: m_set_depth_bounds_test = args[0] ? true : false; break; case NV4097_SET_ALPHA_FUNC: m_set_alpha_func = true; m_alpha_func = args[0]; if(count >= 2) { m_set_alpha_ref = true; m_alpha_ref = args[1]; } break; case NV4097_SET_ALPHA_REF: m_set_alpha_ref = true; m_alpha_ref = args[0]; break; case NV4097_SET_CULL_FACE: m_set_cull_face = true; m_cull_face = args[0]; break; case NV4097_SET_VIEWPORT_VERTICAL: { m_set_viewport_vertical = true; m_viewport_y = args[0] & 0xffff; m_viewport_h = args[0] >> 16; } break; case NV4097_SET_VIEWPORT_HORIZONTAL: { m_set_viewport_horizontal = true; m_viewport_x = args[0] & 0xffff; m_viewport_w = args[0] >> 16; if(count == 2) { m_set_viewport_vertical = true; m_viewport_y = args[1] & 0xffff; m_viewport_h = args[1] >> 16; } CMD_LOG("x=%d, y=%d, w=%d, h=%d", m_viewport_x, m_viewport_y, m_viewport_w, m_viewport_h); } break; case NV4097_SET_CLIP_MIN: { const u32 clip_min = args[0]; const u32 clip_max = args[1]; m_set_clip = true; m_clip_min = (float&)clip_min; m_clip_max = (float&)clip_max; CMD_LOG("clip_min=%.01f, clip_max=%.01f", m_clip_min, m_clip_max); } break; case NV4097_SET_DEPTH_FUNC: m_set_depth_func = true; m_depth_func = args[0]; break; case NV4097_SET_DEPTH_TEST_ENABLE: m_depth_test_enable = args[0] ? true : false; break; case NV4097_SET_FRONT_POLYGON_MODE: m_set_front_polygon_mode = true; m_front_polygon_mode = args[0]; break; case NV4097_CLEAR_SURFACE: { u32 a0 = args[0]; if(a0 & 0x01) m_clear_surface_z = m_clear_z; if(a0 & 0x02) m_clear_surface_s = m_clear_s; if(a0 & 0x10) m_clear_surface_color_r = m_clear_color_r; if(a0 & 0x20) m_clear_surface_color_g = m_clear_color_g; if(a0 & 0x40) m_clear_surface_color_b = m_clear_color_b; if(a0 & 0x80) m_clear_surface_color_a = m_clear_color_a; m_clear_surface_mask |= a0; } break; case NV4097_SET_BLEND_FUNC_SFACTOR: { m_set_blend_sfactor = true; m_blend_sfactor_rgb = args[0] & 0xffff; m_blend_sfactor_alpha = args[0] >> 16; if(count >= 2) { m_set_blend_dfactor = true; m_blend_dfactor_rgb = args[1] & 0xffff; m_blend_dfactor_alpha = args[1] >> 16; } } break; case NV4097_SET_BLEND_FUNC_DFACTOR: { m_set_blend_dfactor = true; m_blend_dfactor_rgb = args[0] & 0xffff; m_blend_dfactor_alpha = args[0] >> 16; } break; case_16(NV4097_SET_VERTEX_DATA_ARRAY_OFFSET, 4): { const u32 addr = GetAddress(args[0] & 0x7fffffff, args[0] >> 31); CMD_LOG("num=%d, addr=0x%x", index, addr); m_vertex_data[index].addr = addr; } break; case_16(NV4097_SET_VERTEX_DATA_ARRAY_FORMAT, 4): { u32 a0 = args[0]; const u16 frequency = a0 >> 16; const u8 stride = (a0 >> 8) & 0xff; const u8 size = (a0 >> 4) & 0xf; const u8 type = a0 & 0xf; CMD_LOG("index=%d, frequency=%d, stride=%d, size=%d, type=%d", index, frequency, stride, size, type); VertexData& cv = m_vertex_data[index]; cv.frequency = frequency; cv.stride = stride; cv.size = size; cv.type = type; } break; case NV4097_DRAW_ARRAYS: { for(u32 c=0; c> 24) + 1; LoadVertexData(first, _count); m_draw_array_count += _count; } } break; case NV4097_SET_INDEX_ARRAY_ADDRESS: { m_indexed_array.m_addr = GetAddress(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; for(u32 i=first; i<_count; ++i) { u32 index; switch(m_indexed_array.m_type) { case 0: { int pos = m_indexed_array.m_data.GetCount(); m_indexed_array.m_data.InsertRoomEnd(4); index = Memory.Read32(m_indexed_array.m_addr + i * 4); *(u32*)&m_indexed_array.m_data[pos] = index; //ConLog.Warning("index 4: %d", *(u32*)&m_indexed_array.m_data[pos]); } break; case 1: { int pos = m_indexed_array.m_data.GetCount(); m_indexed_array.m_data.InsertRoomEnd(2); index = Memory.Read16(m_indexed_array.m_addr + i * 2); //ConLog.Warning("index 2: %d", index); *(u16*)&m_indexed_array.m_data[pos] = 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_BEGIN_END: { if(args[0]) { //begin if(Emu.GetCallbackManager().m_exit_callback.m_callbacks.GetCount()) { //Emu.GetCallbackManager().m_exit_callback.Handle(0x0121, 0); } m_draw_mode = args[0] - 1; } else { //end ExecCMD(); if(Emu.GetCallbackManager().m_exit_callback.m_callbacks.GetCount()) { //Emu.GetCallbackManager().m_exit_callback.Handle(0x0122, 0); } } } break; case NV4097_SET_COLOR_CLEAR_VALUE: { const u32 color = args[0]; m_clear_color_a = (color >> 24) & 0xff; m_clear_color_r = (color >> 16) & 0xff; m_clear_color_g = (color >> 8) & 0xff; m_clear_color_b = color & 0xff; } break; case NV4097_SET_SHADER_PROGRAM: { m_cur_shader_prog = &m_shader_progs[m_cur_shader_prog_num++]; m_cur_shader_prog->Delete(); u32 a0 = args[0]; m_cur_shader_prog->offset = a0 & ~0x3; m_cur_shader_prog->addr = GetAddress(m_cur_shader_prog->offset, (a0 & 0x3) - 1); } break; case NV4097_SET_VERTEX_ATTRIB_OUTPUT_MASK: { //VertexData[0].prog.attributeOutputMask = args[0]; //FragmentData.prog.attributeInputMask = args[0]/* & ~0x20*/; } break; case NV4097_SET_SHADER_CONTROL: { const u32 arg0 = args[0]; //const u8 controlTxp = (arg0 >> 15) & 0x1; //FragmentData.prog.registerCount = arg0 >> 24; //FragmentData.prog. } break; case NV4097_SET_TRANSFORM_PROGRAM_LOAD: { m_cur_vertex_prog = &m_vertex_progs[args[0]]; m_cur_vertex_prog->Delete(); if(count == 2) { const u32 start = args[1]; if(start) ConLog.Warning("NV4097_SET_TRANSFORM_PROGRAM_LOAD: start = %d", start); } } break; case NV4097_SET_TRANSFORM_PROGRAM: { if(!m_cur_vertex_prog) { ConLog.Warning("NV4097_SET_TRANSFORM_PROGRAM: m_cur_vertex_prog == NULL"); break; } for(u32 i=0; idata.AddCpy(args[i]); } break; case NV4097_SET_TRANSFORM_TIMEOUT: if(!m_cur_vertex_prog) { ConLog.Warning("NV4097_SET_TRANSFORM_TIMEOUT: m_cur_vertex_prog == NULL"); break; } //m_cur_vertex_prog->Decompile(); break; case NV4097_SET_VERTEX_ATTRIB_INPUT_MASK: //VertexData[0].prog.attributeInputMask = args[0]; break; case NV4097_INVALIDATE_VERTEX_CACHE_FILE: break; case NV4097_SET_TRANSFORM_CONSTANT_LOAD: { if((count - 1) % 4) { CMD_LOG("NV4097_SET_TRANSFORM_CONSTANT_LOAD [%d]", count); break; } for(u32 id = args[0], i = 1; i= 2) { m_set_stencil_func_ref = true; m_stencil_func_ref = args[1]; if(count >= 3) { m_set_stencil_func_mask = true; m_stencil_func_mask = args[2]; } } break; case NV4097_SET_STENCIL_FUNC_REF: m_set_stencil_func_ref = true; m_stencil_func_ref = args[0]; break; case NV4097_SET_STENCIL_FUNC_MASK: m_set_stencil_func_mask = true; m_stencil_func_mask = args[0]; break; case NV4097_SET_STENCIL_OP_FAIL: m_set_stencil_fail = true; m_stencil_fail = args[0]; if(count >= 2) { m_set_stencil_zfail = true; m_stencil_zfail = args[1]; if(count >= 3) { m_set_stencil_zpass = true; m_stencil_zpass = args[2]; } } break; case NV4097_SET_TWO_SIDED_STENCIL_TEST_ENABLE: m_set_two_sided_stencil_test_enable = args[0] ? true : false; break; case NV4097_SET_BACK_STENCIL_MASK: m_set_back_stencil_mask = true; m_back_stencil_mask = args[0]; break; case NV4097_SET_BACK_STENCIL_FUNC: m_set_back_stencil_func = true; m_back_stencil_func = args[0]; if(count >= 2) { m_set_back_stencil_func_ref = true; m_back_stencil_func_ref = args[1]; if(count >= 3) { m_set_back_stencil_func_mask = true; m_back_stencil_func_mask = args[2]; } } break; case NV4097_SET_BACK_STENCIL_FUNC_REF: m_set_back_stencil_func_ref = true; m_back_stencil_func_ref = args[0]; break; case NV4097_SET_BACK_STENCIL_FUNC_MASK: m_set_back_stencil_func_mask = true; m_back_stencil_func_mask = args[0]; break; case NV4097_SET_BACK_STENCIL_OP_FAIL: m_set_stencil_fail = true; m_stencil_fail = args[0]; if(count >= 2) { m_set_back_stencil_zfail = true; m_back_stencil_zfail = args[1]; if(count >= 3) { m_set_back_stencil_zpass = true; m_back_stencil_zpass = args[2]; } } break; case NV4097_SET_POLY_OFFSET_FILL_ENABLE: if(args[0]) ConLog.Error("NV4097_SET_POLY_OFFSET_FILL_ENABLE"); break; case NV4097_SET_RESTART_INDEX_ENABLE: if(args[0]) ConLog.Error("NV4097_SET_RESTART_INDEX_ENABLE"); break; case NV4097_SET_POINT_PARAMS_ENABLE: if(args[0]) ConLog.Error("NV4097_SET_POINT_PARAMS_ENABLE"); break; case NV4097_SET_POINT_SPRITE_CONTROL: if(args[0] & 0x1) { ConLog.Error("NV4097_SET_POINT_SPRITE_CONTROL enable"); } break; case NV4097_SET_POLY_SMOOTH_ENABLE: m_set_poly_smooth = args[0] ? true : false; break; case NV4097_SET_BLEND_COLOR: m_set_blend_color = true; m_blend_color_r = args[0] & 0xff; m_blend_color_g = (args[0] >> 8) & 0xff; m_blend_color_b = (args[0] >> 16) & 0xff; m_blend_color_a = (args[0] >> 24) & 0xff; break; case NV4097_SET_BLEND_COLOR2: if(args[0]) ConLog.Error("NV4097_SET_BLEND_COLOR2"); break; case NV4097_SET_BLEND_EQUATION: m_set_blend_equation = true; m_blend_equation_rgb = args[0] & 0xffff; m_blend_equation_alpha = args[0] >> 16; break; case NV4097_SET_REDUCE_DST_COLOR: if(args[0]) ConLog.Error("NV4097_SET_REDUCE_DST_COLOR"); break; case NV4097_SET_DEPTH_MASK: m_set_depth_mask = true; m_depth_mask = args[0]; break; case NV4097_SET_SCISSOR_VERTICAL: { m_set_scissor_vertical = true; m_scissor_y = args[0] & 0xffff; m_scissor_h = args[0] >> 16; } break; 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_INVALIDATE_VERTEX_FILE: break; case NV4097_SET_VIEWPORT_OFFSET: { //TODO } break; case NV4097_SET_SEMAPHORE_OFFSET: case NV406E_SEMAPHORE_OFFSET: { m_set_semaphore_offset = true; m_semaphore_offset = args[0]; } break; case NV4097_BACK_END_WRITE_SEMAPHORE_RELEASE: { if(m_set_semaphore_offset) { m_set_semaphore_offset = false; u32 value = args[0]; value = (value & 0xff00ff00) | ((value & 0xff) << 16) | ((value >> 16) & 0xff); Memory.Write32(Memory.RSXCMDMem.GetStartAddr() + m_semaphore_offset, value); } } break; case NV406E_SEMAPHORE_RELEASE: case NV4097_TEXTURE_READ_SEMAPHORE_RELEASE: if(m_set_semaphore_offset) { m_set_semaphore_offset = false; Memory.Write32(Memory.RSXCMDMem.GetStartAddr() + m_semaphore_offset, args[0]); } break; case NV406E_SEMAPHORE_ACQUIRE: { //TODO } break; case NV4097_SET_RESTART_INDEX: { //TODO } break; case NV4097_INVALIDATE_L2: { //TODO } break; case NV4097_SET_CONTEXT_DMA_COLOR_A: { m_set_context_dma_color_a = true; m_context_dma_color_a = args[0]; } break; case NV4097_SET_CONTEXT_DMA_COLOR_B: { m_set_context_dma_color_b = true; m_context_dma_color_b = args[0]; } break; case NV4097_SET_CONTEXT_DMA_COLOR_C: { m_set_context_dma_color_c = true; m_context_dma_color_c = args[0]; if(count > 1) { m_set_context_dma_color_d = true; m_context_dma_color_d = args[1]; } } break; case NV4097_SET_CONTEXT_DMA_ZETA: { m_set_context_dma_z = true; m_context_dma_z = args[0]; } break; /* case NV4097_SET_SURFACE_PITCH_A: { //TODO } break; case NV4097_SET_SURFACE_PITCH_B: { //TODO } break; */ case NV4097_SET_SURFACE_PITCH_C: { if(count != 4) { ConLog.Error("NV4097_SET_SURFACE_PITCH_C: Bad count (%d)", count); break; } m_surface_pitch_c = args[0]; m_surface_pitch_d = args[1]; m_surface_offset_c = args[2]; m_surface_offset_d = args[3]; } break; case NV4097_SET_SURFACE_PITCH_Z: { m_surface_pitch_z = args[0]; } break; case NV4097_SET_SHADER_WINDOW: { 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; case NV4097_SET_SURFACE_CLIP_VERTICAL: { u32 a0 = args[0]; m_set_surface_clip_vertical = true; m_surface_clip_y = a0; m_surface_clip_h = a0 >> 16; } break; case NV4097_SET_SURFACE_CLIP_HORIZONTAL: { u32 a0 = args[0]; m_set_surface_clip_horizontal = true; m_surface_clip_x = a0; m_surface_clip_w = a0 >> 16; if(count >= 2) { u32 a1 = args[1]; m_set_surface_clip_vertical = true; m_surface_clip_y = a1; m_surface_clip_h = a1 >> 16; } } break; case NV4097_SET_WINDOW_OFFSET: { //TODO } break; case NV4097_SET_SURFACE_COLOR_TARGET: { m_surface_colour_target = args[0]; } break; case NV4097_SET_ANTI_ALIASING_CONTROL: { //TODO } break; case NV4097_SET_LINE_SMOOTH_ENABLE: m_set_line_smooth = args[0] ? true : false; break; case NV4097_SET_LINE_WIDTH: m_set_line_width = true; m_line_width = args[0]; break; case NV4097_SET_SHADE_MODE: m_set_shade_mode = true; m_shade_mode = args[0]; break; case NV4097_SET_ZSTENCIL_CLEAR_VALUE: { u32 a0 = args[0]; m_clear_s = a0 & 0xff; m_clear_z = a0 >> 8; } break; case NV4097_SET_ZCULL_CONTROL0: { m_set_depth_func = true; m_depth_func = args[0] >> 4; } break; case NV4097_SET_ZCULL_CONTROL1: { //TODO } break; case NV4097_SET_SCULL_CONTROL: { u32 a0 = args[0]; m_set_stencil_func = m_set_stencil_func_ref = m_set_stencil_func_mask = true; m_stencil_func = a0 & 0xffff; m_stencil_func_ref = (a0 >> 16) & 0xff; m_stencil_func_mask = (a0 >> 24) & 0xff; } break; case NV4097_SET_ZCULL_EN: { u32 a0 = args[0]; m_depth_test_enable = a0 & 0x1 ? true : false; m_set_stencil_test = a0 & 0x2 ? true : false; } break; case NV4097_GET_REPORT: { u32 a0 = args[0]; u8 type = a0 >> 24; u32 offset = a0 & 0xffffff; u64 data; switch(type) { case 1: data = std::chrono::steady_clock::now().time_since_epoch().count(); data *= 1000000; break; default: data = 0; ConLog.Error("NV4097_GET_REPORT: bad type %d", type); break; } Memory.Write64(m_local_mem_addr + offset, data); } break; case NV3062_SET_OFFSET_DESTIN: m_dst_offset = args[0]; break; case NV308A_COLOR: { TransformConstant c; c.id = m_dst_offset; 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) { ConLog.Warning("NV308A_COLOR: count = %d", count); } //ConLog.Warning("NV308A_COLOR: [%d]: %f, %f, %f, %f", c.id, c.x, c.y, c.z, c.w); m_fragment_constants.AddCpy(c); } break; case NV308A_POINT: //TODO break; case NV3062_SET_COLOR_FORMAT: { m_color_format = args[0]; m_color_format_src_pitch = args[1]; m_color_format_dst_pitch = args[1] >> 16; } break; case NV3089_SET_COLOR_CONVERSION: { m_color_conv = args[0]; m_color_conv_fmt = args[1]; m_color_conv_op = args[2]; m_color_conv_in_x = args[3]; m_color_conv_in_y = args[3] >> 16; m_color_conv_in_w = args[4]; m_color_conv_in_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 NV3089_IMAGE_IN_SIZE: { u16 w = args[0]; u16 h = args[0] >> 16; u16 pitch = args[1]; u8 origin = args[1] >> 16; u8 inter = args[1] >> 24; u32 offset = args[2]; u16 u = args[3]; u16 v = args[3] >> 16; u8* pixels_src = &Memory[GetAddress(offset, m_context_dma_img_src - 0xfeed0000)]; u8* pixels_dst = &Memory[GetAddress(m_dst_offset, m_context_dma_img_dst - 0xfeed0000)]; for(u16 y=0; y> 4) & 0xf; m_clip_plane_2 = (a0 >> 8) & 0xf; m_clip_plane_3 = (a0 >> 12) & 0xf; m_clip_plane_4 = (a0 >> 16) & 0xf; m_clip_plane_5 = a0 >> 20; } break; case NV4097_SET_FOG_PARAMS: { m_set_fog_params = true; u32 a0 = args[0]; u32 a1 = args[1]; m_fog_param0 = (float&)a0; m_fog_param1 = (float&)a1; } break; default: { wxString log = GetMethodName(cmd); log += "("; for(u32 i=0; iDecompile(); if(!m_cur_vertex_prog) { ConLog.Warning("LoadProgram: m_cur_vertex_prog == NULL"); return false; } if(m_program.IsCreated()) { m_program.Use(); return true; } //ConLog.Write("Create program"); m_vp_buf_num = m_prog_buffer.SearchVp(*m_cur_vertex_prog); if(m_vp_buf_num == -1) { ConLog.Warning("VP not found in buffer!"); m_cur_vertex_prog->Decompile(); } if(m_fp_buf_num == -1) { ConLog.Warning("FP not found in buffer!"); m_cur_shader_prog->Wait(); m_cur_shader_prog->Compile(); wxFile f(wxGetCwd() + "/FragmentProgram.txt", wxFile::write); f.Write(m_cur_shader_prog->shader); } if(m_vp_buf_num == -1) { m_cur_vertex_prog->Wait(); m_cur_vertex_prog->Compile(); wxFile f(wxGetCwd() + "/VertexProgram.txt", wxFile::write); f.Write(m_cur_vertex_prog->shader); } if(m_fp_buf_num != -1 && m_vp_buf_num != -1) { m_program.id = m_prog_buffer.GetProg(m_fp_buf_num, m_vp_buf_num); } if(m_program.id) { m_program.Use(); } else { m_program.Create(m_cur_vertex_prog->id, m_cur_shader_prog->id); checkForGlError("m_program.Create"); m_prog_buffer.Add(m_program, *m_cur_shader_prog, *m_cur_vertex_prog); checkForGlError("m_prog_buffer.Add"); m_program.Use(); GLint r = GL_FALSE; glGetProgramiv(m_program.id, GL_VALIDATE_STATUS, &r); if(r != GL_TRUE) { glGetProgramiv(m_program.id, GL_INFO_LOG_LENGTH, &r); if(r) { char* buf = new char[r+1]; GLsizei len; memset(buf, 0, r+1); glGetProgramInfoLog(m_program.id, r, &len, buf); ConLog.Error("Failed to validate program: %s", buf); delete[] buf; } Emu.Pause(); } } return true; } void GLGSRender::WriteDepthBuffer() { if(!m_set_context_dma_z) { return; } u32 address = GetAddress(m_surface_offset_z, m_context_dma_z - 0xfeed0000); if(!Memory.IsGoodAddr(address)) { ConLog.Warning("Bad depth address: address=0x%x, offset=0x%x, dma=0x%x", address, m_surface_offset_z, m_context_dma_z); return; } glReadPixels(0, 0, m_width, m_height, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, &Memory[address]); checkForGlError("glReadPixels"); GLuint depth_tex; glGenTextures(1, &depth_tex); glBindTexture(GL_TEXTURE_2D, depth_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &Memory[address]); checkForGlError("glTexImage2D"); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &Memory[address]); checkForGlError("glGetTexImage"); glDeleteTextures(1, &depth_tex); } void GLGSRender::WriteColourBufferA() { if(!m_set_context_dma_color_a) { return; } u32 address = GetAddress(m_surface_offset_a, m_context_dma_color_a - 0xfeed0000); if(!Memory.IsGoodAddr(address)) { ConLog.Warning("Bad colour buffer a address: address=0x%x, offset=0x%x, dma=0x%x", address, m_surface_offset_a, m_context_dma_color_a); return; } glReadBuffer(GL_COLOR_ATTACHMENT0); checkForGlError("glReadBuffer(GL_COLOR_ATTACHMENT0)"); glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &Memory[address]); checkForGlError("glReadPixels(GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)"); } void GLGSRender::WriteColourBufferB() { if(!m_set_context_dma_color_b) { return; } u32 address = GetAddress(m_surface_offset_b, m_context_dma_color_b - 0xfeed0000); if(!Memory.IsGoodAddr(address)) { ConLog.Warning("Bad colour buffer b address: address=0x%x, offset=0x%x, dma=0x%x", address, m_surface_offset_b, m_context_dma_color_b); return; } glReadBuffer(GL_COLOR_ATTACHMENT1); checkForGlError("glReadBuffer(GL_COLOR_ATTACHMENT1)"); glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &Memory[address]); checkForGlError("glReadPixels(GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)"); } void GLGSRender::WriteColourBufferC() { if(!m_set_context_dma_color_c) { return; } u32 address = GetAddress(m_surface_offset_c, m_context_dma_color_c - 0xfeed0000); if(!Memory.IsGoodAddr(address)) { ConLog.Warning("Bad colour buffer c address: address=0x%x, offset=0x%x, dma=0x%x", address, m_surface_offset_c, m_context_dma_color_c); return; } glReadBuffer(GL_COLOR_ATTACHMENT2); checkForGlError("glReadBuffer(GL_COLOR_ATTACHMENT2)"); glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &Memory[address]); checkForGlError("glReadPixels(GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)"); } void GLGSRender::WriteColourBufferD() { if(!m_set_context_dma_color_d) { return; } u32 address = GetAddress(m_surface_offset_d, m_context_dma_color_d - 0xfeed0000); if(!Memory.IsGoodAddr(address)) { ConLog.Warning("Bad colour buffer d address: address=0x%x, offset=0x%x, dma=0x%x", address, m_surface_offset_d, m_context_dma_color_d); return; } glReadBuffer(GL_COLOR_ATTACHMENT3); checkForGlError("glReadBuffer(GL_COLOR_ATTACHMENT3)"); glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, &Memory[address]); checkForGlError("glReadPixels(GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)"); } void GLGSRender::WriteBuffers() { glPixelStorei(GL_PACK_ROW_LENGTH, 0); glPixelStorei(GL_PACK_ALIGNMENT, 4); switch(m_surface_colour_target) { case 0x0: return; case 0x1: WriteColourBufferA(); break; case 0x2: WriteColourBufferB(); break; case 0x13: WriteColourBufferA(); WriteColourBufferB(); break; case 0x17: WriteColourBufferA(); WriteColourBufferB(); WriteColourBufferC(); break; case 0x1f: WriteColourBufferA(); WriteColourBufferB(); WriteColourBufferC(); WriteColourBufferD(); break; } } void GLGSRender::ExecCMD() { if(LoadProgram()) { static int last_width = 0, last_height = 0, last_depth_format = 0; if(m_width != last_width || m_height != last_height || last_depth_format != m_surface_depth_format) { ConLog.Warning("New FBO (%dx%d)", m_width, m_height); last_width = m_width; last_height = m_height; last_depth_format = m_surface_depth_format; m_fbo.Create(); checkForGlError("m_fbo.Create"); m_fbo.Bind(); m_rbo.Create(4 + 1); checkForGlError("m_rbo.Create"); for(int i=0; i<4; ++i) { m_rbo.Bind(i); m_rbo.Storage(GL_RGBA, m_width, m_height); checkForGlError("m_rbo.Storage(GL_RGBA)"); } m_rbo.Bind(4); switch(m_surface_depth_format) { case 1: m_rbo.Storage(GL_DEPTH_COMPONENT16, m_width, m_height); checkForGlError("m_rbo.Storage(GL_DEPTH_COMPONENT16)"); break; case 2: m_rbo.Storage(GL_DEPTH24_STENCIL8, m_width, m_height); checkForGlError("m_rbo.Storage(GL_DEPTH24_STENCIL8)"); break; default: ConLog.Error("Bad depth format! (%d)", m_surface_depth_format); assert(0); break; } for(int i=0; i<4; ++i) { m_fbo.Renderbuffer(GL_COLOR_ATTACHMENT0 + i, m_rbo.GetId(i)); checkForGlError(wxString::Format("m_fbo.Renderbuffer(GL_COLOR_ATTACHMENT%d)", i)); } m_fbo.Renderbuffer(GL_DEPTH_ATTACHMENT, m_rbo.GetId(4)); checkForGlError("m_fbo.Renderbuffer(GL_DEPTH_ATTACHMENT)"); if(m_surface_depth_format == 2) { m_fbo.Renderbuffer(GL_STENCIL_ATTACHMENT, m_rbo.GetId(4)); checkForGlError("m_fbo.Renderbuffer(GL_STENCIL_ATTACHMENT)"); } } if(!m_set_surface_clip_horizontal) { m_surface_clip_x = 0; m_surface_clip_w = m_width; } if(!m_set_surface_clip_vertical) { m_surface_clip_y = 0; m_surface_clip_h = m_height; } m_fbo.Bind(); WriteDepthBuffer(); static const GLenum draw_buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 }; switch(m_surface_colour_target) { case 0x0: break; case 0x1: glDrawBuffer(draw_buffers[0]); break; case 0x2: glDrawBuffer(draw_buffers[1]); break; case 0x13: glDrawBuffers(2, draw_buffers); break; case 0x17: glDrawBuffers(3, draw_buffers); break; case 0x1f: glDrawBuffers(4, draw_buffers); break; default: ConLog.Error("Bad surface colour target: %d", m_surface_colour_target); break; } if(m_set_color_mask) { glColorMask(m_color_mask_r, m_color_mask_g, m_color_mask_b, m_color_mask_a); checkForGlError("glColorMask"); } if(m_set_viewport_horizontal && m_set_viewport_vertical) { glViewport(m_scissor_x, m_height-m_viewport_y-m_viewport_h, m_viewport_w, m_viewport_h); checkForGlError("glViewport"); } if(m_set_scissor_horizontal && m_set_scissor_vertical) { glScissor(m_scissor_x, m_height-m_scissor_y-m_scissor_h, m_scissor_w, m_scissor_h); checkForGlError("glScissor"); } if(m_clear_surface_mask) { GLbitfield f = 0; if (m_clear_surface_mask & 0x1) { glClearDepth(m_clear_surface_z / (float)0xffffff); f |= GL_DEPTH_BUFFER_BIT; } if (m_clear_surface_mask & 0x2) { glClearStencil(m_clear_surface_s); f |= GL_STENCIL_BUFFER_BIT; } if (m_clear_surface_mask & 0xF0) { glClearColor( m_clear_surface_color_r / 255.0f, m_clear_surface_color_g / 255.0f, m_clear_surface_color_b / 255.0f, m_clear_surface_color_a / 255.0f); f |= GL_COLOR_BUFFER_BIT; } glClear(f); } if(m_set_front_polygon_mode) { glPolygonMode(GL_FRONT, m_front_polygon_mode); checkForGlError("glPolygonMode"); } Enable(m_depth_test_enable, GL_DEPTH_TEST); Enable(m_set_alpha_test, GL_ALPHA_TEST); Enable(m_set_depth_bounds_test, GL_DEPTH_CLAMP); Enable(m_set_blend, GL_BLEND); Enable(m_set_logic_op, GL_LOGIC_OP); Enable(m_set_cull_face_enable, GL_CULL_FACE); Enable(m_set_dither, GL_DITHER); Enable(m_set_stencil_test, GL_STENCIL_TEST); Enable(m_set_line_smooth, GL_LINE_SMOOTH); Enable(m_set_poly_smooth, GL_POLYGON_SMOOTH); if(m_set_clip_plane) { Enable(m_clip_plane_0, GL_CLIP_PLANE0); Enable(m_clip_plane_1, GL_CLIP_PLANE1); Enable(m_clip_plane_2, GL_CLIP_PLANE2); Enable(m_clip_plane_3, GL_CLIP_PLANE3); Enable(m_clip_plane_4, GL_CLIP_PLANE4); Enable(m_clip_plane_5, GL_CLIP_PLANE5); checkForGlError("m_set_clip_plane"); } checkForGlError("glEnable"); if(m_set_two_sided_stencil_test_enable) { if(m_set_stencil_fail && m_set_stencil_zfail && m_set_stencil_zpass) { glStencilOpSeparate(GL_FRONT, m_stencil_fail, m_stencil_zfail, m_stencil_zpass); checkForGlError("glStencilOpSeparate"); } if(m_set_stencil_mask) { glStencilMaskSeparate(GL_FRONT, m_stencil_mask); checkForGlError("glStencilMaskSeparate"); } if(m_set_stencil_func && m_set_stencil_func_ref && m_set_stencil_func_mask) { glStencilFuncSeparate(GL_FRONT, m_stencil_func, m_stencil_func_ref, m_stencil_func_mask); checkForGlError("glStencilFuncSeparate"); } if(m_set_back_stencil_fail && m_set_back_stencil_zfail && m_set_back_stencil_zpass) { glStencilOpSeparate(GL_BACK, m_back_stencil_fail, m_back_stencil_zfail, m_back_stencil_zpass); checkForGlError("glStencilOpSeparate(GL_BACK)"); } if(m_set_back_stencil_mask) { glStencilMaskSeparate(GL_BACK, m_back_stencil_mask); checkForGlError("glStencilMaskSeparate(GL_BACK)"); } if(m_set_back_stencil_func && m_set_back_stencil_func_ref && m_set_back_stencil_func_mask) { glStencilFuncSeparate(GL_BACK, m_back_stencil_func, m_back_stencil_func_ref, m_back_stencil_func_mask); checkForGlError("glStencilFuncSeparate(GL_BACK)"); } } else { if(m_set_stencil_fail && m_set_stencil_zfail && m_set_stencil_zpass) { glStencilOp(m_stencil_fail, m_stencil_zfail, m_stencil_zpass); checkForGlError("glStencilOp"); } if(m_set_stencil_mask) { glStencilMask(m_stencil_mask); checkForGlError("glStencilMask"); } if(m_set_stencil_func && m_set_stencil_func_ref && m_set_stencil_func_mask) { glStencilFunc(m_stencil_func, m_stencil_func_ref, m_stencil_func_mask); checkForGlError("glStencilFunc"); } } if(m_set_shade_mode) { glShadeModel(m_shade_mode); checkForGlError("glShadeModel"); } if(m_set_depth_mask) { glDepthMask(m_depth_mask); checkForGlError("glDepthMask"); } if(m_set_depth_func) { glDepthFunc(m_depth_func); checkForGlError("glDepthFunc"); } if(m_set_clip) { glDepthRangef(m_clip_min, m_clip_max); checkForGlError("glDepthRangef"); } if(m_set_line_width) { glLineWidth(m_line_width / 255.f); checkForGlError("glLineWidth"); } if(m_set_blend_equation) { glBlendEquationSeparate(m_blend_equation_rgb, m_blend_equation_alpha); checkForGlError("glBlendEquationSeparate"); } if(m_set_blend_sfactor && m_set_blend_dfactor) { glBlendFuncSeparate(m_blend_sfactor_rgb, m_blend_dfactor_rgb, m_blend_sfactor_alpha, m_blend_dfactor_alpha); checkForGlError("glBlendFuncSeparate"); } if(m_set_blend_color) { glBlendColor(m_blend_color_r, m_blend_color_g, m_blend_color_b, m_blend_color_a); checkForGlError("glBlendColor"); } if(m_set_cull_face) { glCullFace(m_cull_face); checkForGlError("glCullFace"); } if(m_set_alpha_func && m_set_alpha_ref) { glAlphaFunc(m_alpha_func, m_alpha_ref); checkForGlError("glAlphaFunc"); } if(m_set_fog_mode) { glFogi(GL_FOG_MODE, m_fog_mode); checkForGlError("glFogi(GL_FOG_MODE)"); } if(m_set_fog_params) { glFogf(GL_FOG_START, m_fog_param0); checkForGlError("glFogf(GL_FOG_START)"); glFogf(GL_FOG_END, m_fog_param1); checkForGlError("glFogf(GL_FOG_END)"); } if(m_indexed_array.m_count && m_draw_array_count) { ConLog.Warning("m_indexed_array.m_count && draw_array_count"); } for(u32 i=0; iGetTexture(i); if(!tex.IsEnabled()) continue; glActiveTexture(GL_TEXTURE0 + i); checkForGlError("glActiveTexture"); tex.Create(); tex.Bind(); checkForGlError("tex.Bind"); m_program.SetTex(i); tex.Init(); checkForGlError("tex.Init"); //tex.Save(); } m_vao.Bind(); if(m_indexed_array.m_count) { LoadVertexData(m_indexed_array.index_min, m_indexed_array.index_max - m_indexed_array.index_min + 1); } EnableVertexData(m_indexed_array.m_count ? true : false); InitVertexData(); InitFragmentData(); if(m_indexed_array.m_count) { switch(m_indexed_array.m_type) { case 0: glDrawElements(m_draw_mode, m_indexed_array.m_count, GL_UNSIGNED_INT, nullptr); checkForGlError("glDrawElements #4"); break; case 1: glDrawElements(m_draw_mode, m_indexed_array.m_count, GL_UNSIGNED_SHORT, nullptr); checkForGlError("glDrawElements #2"); break; default: ConLog.Error("Bad indexed array type (%d)", m_indexed_array.m_type); break; } DisableVertexData(); m_indexed_array.Reset(); } if(m_draw_array_count) { glDrawArrays(m_draw_mode, 0, m_draw_array_count); checkForGlError("glDrawArrays"); DisableVertexData(); m_draw_array_count = 0; } m_fragment_constants.Clear(); WriteBuffers(); } else { ConLog.Error("LoadProgram failed."); Emu.Pause(); } Reset(); } void GLGSRender::Reset() { m_program.UnUse(); //m_prog_buffer.Clear(); if(m_cur_shader_prog) m_cur_shader_prog->id = 0; if(m_cur_vertex_prog) m_cur_vertex_prog->id = 0; m_cur_shader_prog_num = 0; m_transform_constants.Clear(); for(uint i=0; i