mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-01-25 18:10:48 +01:00
move module initialization into a module manager, still has some issues like stopping not working and debug crashing add #idef 0 to modules that aren't in the windows project don't double initialize and don't de-initialize for now, since many modules don't expect it and it leads to many errors remove duplicate module lists for empty modules and implemented ones, make Module non-copyable but movable add secondary project, no real use for it now add some memleak config to the emucore and add asmjit path to rpcs3 small rebase error fixed to get it to compile again add filters for emucore re-add the module manager and static file WIP commit, linker errors abound some more abstraction layer stuff fix the remaining linker errors, re-enable platform specific mouse, pad and keyboard handlers rebasing fix memset undefined and re() usage of se_t before declaration Add wxGUI define by default for cmake builds fix copy constructors of Datetime header fix copy constructors of other wx interface classes remove static declarations of global variables make wxGLCanvas constructor non-ambiguous even with wx2.8. compat mode, fix wrong std::exception constructor calls remove duplicate definition for FromUTF8 and ToUTF8 temp changes
1276 lines
32 KiB
C++
1276 lines
32 KiB
C++
#include "stdafx.h"
|
|
#include "Emu/ConLog.h"
|
|
#include "Emu/Memory/Memory.h"
|
|
#include "Emu/System.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];
|
|
GLuint g_flip_tex;
|
|
GLuint g_depth_tex;
|
|
|
|
int last_width = 0, last_height = 0, last_depth_format = 0;
|
|
|
|
GLenum g_last_gl_error = GL_NO_ERROR;
|
|
void printGlError(GLenum err, const char* situation)
|
|
{
|
|
if(err != GL_NO_ERROR)
|
|
{
|
|
ConLog.Error("%s: opengl error 0x%04x", situation, err);
|
|
Emu.Pause();
|
|
}
|
|
}
|
|
void printGlError(GLenum err, const std::string& situation)
|
|
{
|
|
printGlError(err, situation.c_str());
|
|
}
|
|
|
|
#if 0
|
|
#define checkForGlError(x) /*x*/
|
|
#endif
|
|
|
|
|
|
GLGSRender::GLGSRender()
|
|
: GSRender()
|
|
, m_frame(nullptr)
|
|
, m_fp_buf_num(-1)
|
|
, m_vp_buf_num(-1)
|
|
, m_context(nullptr)
|
|
{
|
|
m_frame = new rGLFrame();// new GLGSFrame();
|
|
}
|
|
|
|
GLGSRender::~GLGSRender()
|
|
{
|
|
m_frame->Close();
|
|
delete m_context;
|
|
}
|
|
|
|
void GLGSRender::Enable(bool enable, const u32 cap)
|
|
{
|
|
if(enable)
|
|
{
|
|
glEnable(cap);
|
|
}
|
|
else
|
|
{
|
|
glDisable(cap);
|
|
}
|
|
}
|
|
|
|
extern CellGcmContextData current_context;
|
|
|
|
void GLGSRender::Close()
|
|
{
|
|
Stop();
|
|
|
|
if(m_frame->IsShown()) m_frame->Hide();
|
|
m_ctrl = nullptr;
|
|
}
|
|
|
|
void GLGSRender::EnableVertexData(bool indexed_draw)
|
|
{
|
|
static u32 offset_list[m_vertex_count];
|
|
u32 cur_offset = 0;
|
|
|
|
const u32 data_offset = indexed_draw ? 0 : m_draw_array_first;
|
|
|
|
for(u32 i=0; i<m_vertex_count; ++i)
|
|
{
|
|
offset_list[i] = cur_offset;
|
|
|
|
if(!m_vertex_data[i].IsEnabled() || !m_vertex_data[i].addr) continue;
|
|
|
|
const size_t item_size = m_vertex_data[i].GetTypeSize() * m_vertex_data[i].size;
|
|
const size_t data_size = m_vertex_data[i].data.size() - data_offset * item_size;
|
|
const u32 pos = m_vdata.size();
|
|
|
|
cur_offset += data_size;
|
|
m_vdata.resize(m_vdata.size() + data_size);
|
|
memcpy(&m_vdata[pos], &m_vertex_data[i].data[data_offset * item_size], data_size);
|
|
}
|
|
|
|
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.size());
|
|
|
|
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.size());
|
|
}
|
|
|
|
checkForGlError("initializing vbo");
|
|
|
|
#if DUMP_VERTEX_DATA
|
|
rFile dump("VertexDataArray.dump", rFile::write);
|
|
#endif
|
|
|
|
for(u32 i=0; i<m_vertex_count; ++i)
|
|
{
|
|
if(!m_vertex_data[i].IsEnabled()) continue;
|
|
|
|
#if DUMP_VERTEX_DATA
|
|
dump.Write(wxString::Format("VertexData[%d]:\n", i));
|
|
switch(m_vertex_data[i].type)
|
|
{
|
|
case CELL_GCM_VERTEX_S1:
|
|
for(u32 j = 0; j<m_vertex_data[i].data.size(); j+=2)
|
|
{
|
|
dump.Write(wxString::Format("%d\n", *(u16*)&m_vertex_data[i].data[j]));
|
|
if(!(((j+2) / 2) % m_vertex_data[i].size)) dump.Write("\n");
|
|
}
|
|
break;
|
|
|
|
case CELL_GCM_VERTEX_F:
|
|
for(u32 j = 0; j<m_vertex_data[i].data.size(); j+=4)
|
|
{
|
|
dump.Write(wxString::Format("%.01f\n", *(float*)&m_vertex_data[i].data[j]));
|
|
if(!(((j+4) / 4) % m_vertex_data[i].size)) dump.Write("\n");
|
|
}
|
|
break;
|
|
|
|
case CELL_GCM_VERTEX_SF:
|
|
for(u32 j = 0; j<m_vertex_data[i].data.size(); j+=2)
|
|
{
|
|
dump.Write(wxString::Format("%.01f\n", *(float*)&m_vertex_data[i].data[j]));
|
|
if(!(((j+2) / 2) % m_vertex_data[i].size)) dump.Write("\n");
|
|
}
|
|
break;
|
|
|
|
case CELL_GCM_VERTEX_UB:
|
|
for(u32 j = 0; j<m_vertex_data[i].data.size(); ++j)
|
|
{
|
|
dump.Write(wxString::Format("%d\n", m_vertex_data[i].data[j]));
|
|
if(!((j+1) % m_vertex_data[i].size)) dump.Write("\n");
|
|
}
|
|
break;
|
|
|
|
case CELL_GCM_VERTEX_S32K:
|
|
for(u32 j = 0; j<m_vertex_data[i].data.size(); j+=2)
|
|
{
|
|
dump.Write(wxString::Format("%d\n", *(u16*)&m_vertex_data[i].data[j]));
|
|
if(!(((j+2) / 2) % m_vertex_data[i].size)) dump.Write("\n");
|
|
}
|
|
break;
|
|
|
|
// case CELL_GCM_VERTEX_CMP:
|
|
|
|
case CELL_GCM_VERTEX_UB256:
|
|
for(u32 j = 0; j<m_vertex_data[i].data.size(); ++j)
|
|
{
|
|
dump.Write(wxString::Format("%d\n", m_vertex_data[i].data[j]));
|
|
if(!((j+1) % m_vertex_data[i].size)) dump.Write("\n");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ConLog.Error("Bad cv type! %d", m_vertex_data[i].type);
|
|
return;
|
|
}
|
|
|
|
dump.Write("\n");
|
|
#endif
|
|
|
|
static const u32 gl_types[] =
|
|
{
|
|
GL_SHORT,
|
|
GL_FLOAT,
|
|
GL_HALF_FLOAT,
|
|
GL_UNSIGNED_BYTE,
|
|
GL_SHORT,
|
|
GL_UNSIGNED_BYTE,
|
|
};
|
|
|
|
static const bool gl_normalized[] =
|
|
{
|
|
true,
|
|
false,
|
|
false,
|
|
true,
|
|
false,
|
|
false,
|
|
};
|
|
|
|
if(m_vertex_data[i].type >= 1 && m_vertex_data[i].type <= 7)
|
|
{
|
|
if(!m_vertex_data[i].addr)
|
|
{
|
|
switch(m_vertex_data[i].type)
|
|
{
|
|
case CELL_GCM_VERTEX_S32K:
|
|
case CELL_GCM_VERTEX_S1:
|
|
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 CELL_GCM_VERTEX_F:
|
|
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 CELL_GCM_VERTEX_CMP:
|
|
case CELL_GCM_VERTEX_UB:
|
|
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; i<m_vertex_count; ++i)
|
|
{
|
|
if(!m_vertex_data[i].IsEnabled() || !m_vertex_data[i].addr) continue;
|
|
glDisableVertexAttribArray(i);
|
|
checkForGlError("glDisableVertexAttribArray");
|
|
}
|
|
m_vao.Unbind();
|
|
}
|
|
|
|
void GLGSRender::InitVertexData()
|
|
{
|
|
GLfloat scaleOffsetMat[16] = {1.0f, 0.0f, 0.0f, 0.0f,
|
|
0.0f, 1.0f, 0.0f, 0.0f,
|
|
0.0f, 0.0f, 1.0f, 0.0f,
|
|
0.0f, 0.0f, 0.0f, 1.0f};
|
|
int l;
|
|
|
|
for(u32 i=0; i<m_transform_constants.size(); ++i)
|
|
{
|
|
const RSXTransformConstant& c = m_transform_constants[i];
|
|
const std::string name = fmt::Format("vc[%u]", c.id);
|
|
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 + fmt::Format(" %d [%f %f %f %f]", l, c.x, c.y, c.z, c.w));
|
|
}
|
|
|
|
// Scale
|
|
scaleOffsetMat[0] = (GLfloat&)methodRegisters[NV4097_SET_VIEWPORT_SCALE + (0x4*0)] / (RSXThread::m_width / 2.0f);
|
|
scaleOffsetMat[5] = (GLfloat&)methodRegisters[NV4097_SET_VIEWPORT_SCALE + (0x4*1)] / (RSXThread::m_height / 2.0f);
|
|
scaleOffsetMat[10] = (GLfloat&)methodRegisters[NV4097_SET_VIEWPORT_SCALE + (0x4*2)];
|
|
|
|
// Offset
|
|
scaleOffsetMat[3] = (GLfloat&)methodRegisters[NV4097_SET_VIEWPORT_OFFSET + (0x4*0)] - (RSXThread::m_width / 2.0f);
|
|
scaleOffsetMat[7] = (GLfloat&)methodRegisters[NV4097_SET_VIEWPORT_OFFSET + (0x4*1)] - (RSXThread::m_height / 2.0f);
|
|
scaleOffsetMat[11] = (GLfloat&)methodRegisters[NV4097_SET_VIEWPORT_OFFSET + (0x4*2)] - 1/2.0f;
|
|
|
|
scaleOffsetMat[3] /= RSXThread::m_width / 2.0f;
|
|
scaleOffsetMat[7] /= RSXThread::m_height / 2.0f;
|
|
|
|
l = m_program.GetLocation("scaleOffsetMat");
|
|
glUniformMatrix4fv(l, 1, false, scaleOffsetMat);
|
|
}
|
|
|
|
void GLGSRender::InitFragmentData()
|
|
{
|
|
if(!m_cur_shader_prog)
|
|
{
|
|
ConLog.Error("InitFragmentData: m_cur_shader_prog == NULL");
|
|
return;
|
|
}
|
|
|
|
for(u32 i=0; i<m_fragment_constants.size(); ++i)
|
|
{
|
|
const RSXTransformConstant& c = m_fragment_constants[i];
|
|
|
|
u32 id = c.id - m_cur_shader_prog->offset;
|
|
|
|
//ConLog.Warning("fc%u[0x%x - 0x%x] = (%f, %f, %f, %f)", id, c.id, m_cur_shader_prog->offset, c.x, c.y, c.z, c.w);
|
|
|
|
const std::string name = fmt::Format("fc%u", id);
|
|
const int l = m_program.GetLocation(name);
|
|
checkForGlError("glGetUniformLocation " + name);
|
|
|
|
glUniform4f(l, c.x, c.y, c.z, c.w);
|
|
checkForGlError("glUniform4f " + name + fmt::Format(" %u [%f %f %f %f]", l, c.x, c.y, c.z, c.w));
|
|
}
|
|
|
|
//if(m_fragment_constants.GetCount())
|
|
// ConLog.SkipLn();
|
|
}
|
|
|
|
bool GLGSRender::LoadProgram()
|
|
{
|
|
if(!m_cur_shader_prog)
|
|
{
|
|
ConLog.Warning("LoadProgram: m_cur_shader_prog == NULL");
|
|
return false;
|
|
}
|
|
|
|
if(!m_cur_vertex_prog)
|
|
{
|
|
ConLog.Warning("LoadProgram: m_cur_vertex_prog == NULL");
|
|
return false;
|
|
}
|
|
|
|
m_fp_buf_num = m_prog_buffer.SearchFp(*m_cur_shader_prog, m_shader_prog);
|
|
m_vp_buf_num = m_prog_buffer.SearchVp(*m_cur_vertex_prog, m_vertex_prog);
|
|
|
|
//ConLog.Write("Create program");
|
|
|
|
if(m_fp_buf_num == -1)
|
|
{
|
|
ConLog.Warning("FP not found in buffer!");
|
|
m_shader_prog.DecompileAsync(*m_cur_shader_prog);
|
|
m_shader_prog.Wait();
|
|
m_shader_prog.Compile();
|
|
checkForGlError("m_shader_prog.Compile");
|
|
|
|
rFile f(rGetCwd() + "/FragmentProgram.txt", rFile::write);
|
|
f.Write(m_shader_prog.GetShaderText());
|
|
}
|
|
|
|
if(m_vp_buf_num == -1)
|
|
{
|
|
ConLog.Warning("VP not found in buffer!");
|
|
m_vertex_prog.DecompileAsync(*m_cur_vertex_prog);
|
|
m_vertex_prog.Wait();
|
|
m_vertex_prog.Compile();
|
|
checkForGlError("m_vertex_prog.Compile");
|
|
|
|
rFile f(rGetCwd() + "/VertexProgram.txt", rFile::write);
|
|
f.Write(m_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)
|
|
{
|
|
// RSX Debugger: Check if this program was modified and update it
|
|
if (Ini.GSLogPrograms.GetValue())
|
|
{
|
|
for(auto& program : m_debug_programs)
|
|
{
|
|
if (program.id == m_program.id && program.modified)
|
|
{
|
|
// TODO: This isn't working perfectly. Is there any better/shorter way to update the program
|
|
m_vertex_prog.shader = program.vp_shader;
|
|
m_shader_prog.SetShaderText(program.fp_shader);
|
|
m_vertex_prog.Wait();
|
|
m_vertex_prog.Compile();
|
|
checkForGlError("m_vertex_prog.Compile");
|
|
m_shader_prog.Wait();
|
|
m_shader_prog.Compile();
|
|
checkForGlError("m_shader_prog.Compile");
|
|
glAttachShader(m_program.id, m_vertex_prog.id);
|
|
glAttachShader(m_program.id, m_shader_prog.GetId());
|
|
glLinkProgram(m_program.id);
|
|
checkForGlError("glLinkProgram");
|
|
glDetachShader(m_program.id, m_vertex_prog.id);
|
|
glDetachShader(m_program.id, m_shader_prog.GetId());
|
|
program.vp_id = m_vertex_prog.id;
|
|
program.fp_id = m_shader_prog.GetId();
|
|
program.modified = false;
|
|
}
|
|
}
|
|
}
|
|
m_program.Use();
|
|
}
|
|
else
|
|
{
|
|
m_program.Create(m_vertex_prog.id, m_shader_prog.GetId());
|
|
checkForGlError("m_program.Create");
|
|
m_prog_buffer.Add(m_program, m_shader_prog, *m_cur_shader_prog, m_vertex_prog, *m_cur_vertex_prog);
|
|
checkForGlError("m_prog_buffer.Add");
|
|
m_program.Use();
|
|
|
|
// RSX Debugger
|
|
if (Ini.GSLogPrograms.GetValue())
|
|
{
|
|
RSXDebuggerProgram program;
|
|
program.id = m_program.id;
|
|
program.vp_id = m_vertex_prog.id;
|
|
program.fp_id = m_shader_prog.GetId();
|
|
program.vp_shader = m_vertex_prog.shader;
|
|
program.fp_shader = m_shader_prog.GetShaderText();
|
|
m_debug_programs.push_back(program);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void GLGSRender::WriteDepthBuffer()
|
|
{
|
|
if(!m_set_context_dma_z)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Reset the flag
|
|
m_set_context_dma_z = false;
|
|
|
|
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, RSXThread::m_width, RSXThread::m_height, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, &Memory[address]);
|
|
checkForGlError("glReadPixels");
|
|
|
|
glBindTexture(GL_TEXTURE_2D, g_depth_tex);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, RSXThread::m_width, RSXThread::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");
|
|
}
|
|
|
|
void GLGSRender::WriteColourBufferA()
|
|
{
|
|
if(!m_set_context_dma_color_a)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Reset the flag
|
|
m_set_context_dma_color_a = false;
|
|
|
|
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, RSXThread::m_width, RSXThread::m_height, GL_BGRA, 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;
|
|
}
|
|
|
|
// Reset the flag
|
|
m_set_context_dma_color_b = false;
|
|
|
|
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, RSXThread::m_width, RSXThread::m_height, GL_BGRA, 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;
|
|
}
|
|
|
|
// Reset the flag
|
|
m_set_context_dma_color_c = false;
|
|
|
|
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, RSXThread::m_width, RSXThread::m_height, GL_BGRA, 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;
|
|
}
|
|
|
|
// Reset the flag
|
|
m_set_context_dma_color_d = false;
|
|
|
|
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, RSXThread::m_width, RSXThread::m_height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, &Memory[address]);
|
|
checkForGlError("glReadPixels(GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)");
|
|
}
|
|
|
|
void GLGSRender::WriteColorBuffers()
|
|
{
|
|
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
|
|
|
switch(m_surface_colour_target)
|
|
{
|
|
case CELL_GCM_SURFACE_TARGET_NONE:
|
|
return;
|
|
|
|
case CELL_GCM_SURFACE_TARGET_0:
|
|
WriteColourBufferA();
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_TARGET_1:
|
|
WriteColourBufferB();
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_TARGET_MRT1:
|
|
WriteColourBufferA();
|
|
WriteColourBufferB();
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_TARGET_MRT2:
|
|
WriteColourBufferA();
|
|
WriteColourBufferB();
|
|
WriteColourBufferC();
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_TARGET_MRT3:
|
|
WriteColourBufferA();
|
|
WriteColourBufferB();
|
|
WriteColourBufferC();
|
|
WriteColourBufferD();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GLGSRender::OnInit()
|
|
{
|
|
m_draw_frames = 1;
|
|
m_skip_frames = 0;
|
|
RSXThread::m_width = 720;
|
|
RSXThread::m_height = 576;
|
|
|
|
last_width = 0;
|
|
last_height = 0;
|
|
last_depth_format = 0;
|
|
|
|
m_frame->Show();
|
|
}
|
|
|
|
void GLGSRender::OnInitThread()
|
|
{
|
|
m_context = m_frame->GetNewContext();//new rGLContext(m_frame->GetCanvas());
|
|
|
|
//m_frame->GetCanvas()->SetCurrent(*m_context);
|
|
m_frame->SetCurrent(m_context);
|
|
|
|
InitProcTable();
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
#ifdef _WIN32
|
|
glSwapInterval(Ini.GSVSyncEnable.GetValue() ? 1 : 0);
|
|
// Undefined reference: glXSwapIntervalEXT
|
|
/*#else
|
|
if (GLXDrawable drawable = glXGetCurrentDrawable()){
|
|
glXSwapIntervalEXT(glXGetCurrentDisplay(), drawable, Ini.GSVSyncEnable.GetValue() ? 1 : 0);
|
|
}*/
|
|
#endif
|
|
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
|
glGenTextures(1, &g_depth_tex);
|
|
glGenTextures(1, &g_flip_tex);
|
|
}
|
|
|
|
void GLGSRender::OnExitThread()
|
|
{
|
|
glDeleteTextures(1, &g_flip_tex);
|
|
glDeleteTextures(1, &g_depth_tex);
|
|
|
|
m_program.Delete();
|
|
m_rbo.Delete();
|
|
m_fbo.Delete();
|
|
m_vbo.Delete();
|
|
m_vao.Delete();
|
|
m_prog_buffer.Clear();
|
|
}
|
|
|
|
void GLGSRender::OnReset()
|
|
{
|
|
m_program.UnUse();
|
|
|
|
//m_shader_prog.id = 0;
|
|
//m_vertex_prog.id = 0;
|
|
|
|
if(m_vbo.IsCreated())
|
|
{
|
|
m_vbo.UnBind();
|
|
m_vbo.Delete();
|
|
}
|
|
|
|
m_vao.Delete();
|
|
}
|
|
|
|
void GLGSRender::ExecCMD()
|
|
{
|
|
//return;
|
|
if(!LoadProgram())
|
|
{
|
|
ConLog.Error("LoadProgram failed.");
|
|
Emu.Pause();
|
|
return;
|
|
}
|
|
|
|
if(!m_fbo.IsCreated() || RSXThread::m_width != last_width || RSXThread::m_height != last_height || last_depth_format != m_surface_depth_format)
|
|
{
|
|
ConLog.Warning("New FBO (%dx%d)", RSXThread::m_width, RSXThread::m_height);
|
|
last_width = RSXThread::m_width;
|
|
last_height = RSXThread::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, RSXThread::m_width, RSXThread::m_height);
|
|
checkForGlError("m_rbo.Storage(GL_RGBA)");
|
|
}
|
|
|
|
m_rbo.Bind(4);
|
|
|
|
switch(m_surface_depth_format)
|
|
{
|
|
// case 0 found in BLJM60410-[Suzukaze no Melt - Days in the Sanctuary]
|
|
// [E : RSXThread]: Bad depth format! (0)
|
|
// [E : RSXThread]: glEnable: opengl error 0x0506
|
|
// [E : RSXThread]: glDrawArrays: opengl error 0x0506
|
|
case 0:
|
|
m_rbo.Storage(GL_DEPTH_COMPONENT, RSXThread::m_width, RSXThread::m_height);
|
|
checkForGlError("m_rbo.Storage(GL_DEPTH_COMPONENT)");
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_Z16:
|
|
m_rbo.Storage(GL_DEPTH_COMPONENT16, RSXThread::m_width, RSXThread::m_height);
|
|
checkForGlError("m_rbo.Storage(GL_DEPTH_COMPONENT16)");
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_Z24S8:
|
|
m_rbo.Storage(GL_DEPTH24_STENCIL8, RSXThread::m_width, RSXThread::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(fmt::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 = RSXThread::m_width;
|
|
}
|
|
|
|
if(!m_set_surface_clip_vertical)
|
|
{
|
|
m_surface_clip_y = 0;
|
|
m_surface_clip_h = RSXThread::m_height;
|
|
}
|
|
|
|
m_fbo.Bind();
|
|
|
|
if(Ini.GSDumpDepthBuffer.GetValue())
|
|
WriteDepthBuffer();
|
|
|
|
static const GLenum draw_buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
|
|
|
|
switch(m_surface_colour_target)
|
|
{
|
|
case CELL_GCM_SURFACE_TARGET_NONE:
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_TARGET_0:
|
|
glDrawBuffer(draw_buffers[0]);
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_TARGET_1:
|
|
glDrawBuffer(draw_buffers[1]);
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_TARGET_MRT1:
|
|
glDrawBuffers(2, draw_buffers);
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_TARGET_MRT2:
|
|
glDrawBuffers(3, draw_buffers);
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_TARGET_MRT3:
|
|
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_viewport_x, m_viewport_y, m_viewport_w, m_viewport_h);
|
|
//checkForGlError("glViewport");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
Enable(m_set_depth_test, GL_DEPTH_TEST);
|
|
Enable(m_set_alpha_test, GL_ALPHA_TEST);
|
|
Enable(m_set_depth_bounds_test, GL_DEPTH_BOUNDS_TEST_EXT);
|
|
Enable(m_set_blend, GL_BLEND);
|
|
Enable(m_set_logic_op, GL_LOGIC_OP);
|
|
Enable(m_set_cull_face, GL_CULL_FACE);
|
|
Enable(m_set_dither, GL_DITHER);
|
|
Enable(m_set_stencil_test, GL_STENCIL_TEST);
|
|
Enable(m_set_scissor_horizontal && m_set_scissor_vertical, GL_SCISSOR_TEST);
|
|
Enable(m_set_line_smooth, GL_LINE_SMOOTH);
|
|
Enable(m_set_poly_smooth, GL_POLYGON_SMOOTH);
|
|
Enable(m_set_point_sprite_control, GL_POINT_SPRITE);
|
|
Enable(m_set_specular, GL_LIGHTING);
|
|
Enable(m_set_poly_offset_fill, GL_POLYGON_OFFSET_FILL);
|
|
Enable(m_set_poly_offset_line, GL_POLYGON_OFFSET_LINE);
|
|
Enable(m_set_poly_offset_point, GL_POLYGON_OFFSET_POINT);
|
|
Enable(m_set_restart_index, GL_PRIMITIVE_RESTART); // Requires OpenGL 3.1+
|
|
|
|
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_front_polygon_mode)
|
|
{
|
|
glPolygonMode(GL_FRONT, m_front_polygon_mode);
|
|
checkForGlError("glPolygonMode(Front)");
|
|
}
|
|
|
|
if (m_set_back_polygon_mode)
|
|
{
|
|
glPolygonMode(GL_BACK, m_back_polygon_mode);
|
|
checkForGlError("glPolygonMode(Back)");
|
|
}
|
|
|
|
if (m_set_point_size)
|
|
{
|
|
glPointSize(m_point_size);
|
|
checkForGlError("glPointSize");
|
|
}
|
|
|
|
if (m_set_poly_offset_mode)
|
|
{
|
|
glPolygonOffset(m_poly_offset_scale_factor, m_poly_offset_bias);
|
|
checkForGlError("glPolygonOffset");
|
|
}
|
|
|
|
if (m_set_logic_op)
|
|
{
|
|
glLogicOp(m_logic_op);
|
|
checkForGlError("glLogicOp");
|
|
}
|
|
|
|
if (m_set_scissor_horizontal && m_set_scissor_vertical)
|
|
{
|
|
glScissor(m_scissor_x, m_scissor_y, m_scissor_w, m_scissor_h);
|
|
checkForGlError("glScissor");
|
|
}
|
|
|
|
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);
|
|
//ConLog.Warning("glDepthFunc(0x%x)", m_depth_func);
|
|
checkForGlError("glDepthFunc");
|
|
}
|
|
|
|
if(m_set_depth_bounds)
|
|
{
|
|
//ConLog.Warning("glDepthBounds(%f, %f)", m_depth_bounds_min, m_depth_bounds_max);
|
|
glDepthBoundsEXT(m_depth_bounds_min, m_depth_bounds_max);
|
|
checkForGlError("glDepthBounds");
|
|
}
|
|
|
|
if(m_set_clip)
|
|
{
|
|
//ConLog.Warning("glDepthRangef(%f, %f)", m_clip_min, m_clip_max);
|
|
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_front_face)
|
|
{
|
|
// Sanity check . Disgaea 3 return 0x1d0 here and cause openGL 0x0500
|
|
if (m_front_face == GL_CW || m_front_face == GL_CCW)
|
|
{
|
|
glFrontFace(m_front_face);
|
|
checkForGlError("glFrontFace");
|
|
}
|
|
}
|
|
|
|
if(m_set_alpha_func && m_set_alpha_ref)
|
|
{
|
|
glAlphaFunc(m_alpha_func, m_alpha_ref/255.0f);
|
|
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_set_restart_index)
|
|
{
|
|
glPrimitiveRestartIndex(m_restart_index); // Requires OpenGL 3.1+
|
|
checkForGlError("glPrimitiveRestartIndex");
|
|
}
|
|
|
|
if(m_indexed_array.m_count && m_draw_array_count)
|
|
{
|
|
ConLog.Warning("m_indexed_array.m_count && draw_array_count");
|
|
}
|
|
|
|
for(u32 i=0; i<m_textures_count; ++i)
|
|
{
|
|
if(!m_textures[i].IsEnabled()) continue;
|
|
|
|
glActiveTexture(GL_TEXTURE0 + i);
|
|
checkForGlError("glActiveTexture");
|
|
m_gl_textures[i].Create();
|
|
m_gl_textures[i].Bind();
|
|
checkForGlError(fmt::Format("m_gl_textures[%d].Bind", i));
|
|
m_program.SetTex(i);
|
|
m_gl_textures[i].Init(m_textures[i]);
|
|
checkForGlError(fmt::Format("m_gl_textures[%d].Init", i));
|
|
}
|
|
|
|
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 CELL_GCM_DRAW_INDEX_ARRAY_TYPE_32:
|
|
glDrawElements(m_draw_mode - 1, m_indexed_array.m_count, GL_UNSIGNED_INT, nullptr);
|
|
checkForGlError("glDrawElements #4");
|
|
break;
|
|
|
|
case CELL_GCM_DRAW_INDEX_ARRAY_TYPE_16:
|
|
glDrawElements(m_draw_mode - 1, 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)
|
|
{
|
|
//ConLog.Warning("glDrawArrays(%d,%d,%d)", m_draw_mode - 1, m_draw_array_first, m_draw_array_count);
|
|
glDrawArrays(m_draw_mode - 1, 0, m_draw_array_count);
|
|
checkForGlError("glDrawArrays");
|
|
DisableVertexData();
|
|
}
|
|
|
|
if (Ini.GSDumpColorBuffers.GetValue())
|
|
WriteColorBuffers();
|
|
}
|
|
|
|
void GLGSRender::Flip()
|
|
{
|
|
switch (m_surface_colour_target)
|
|
{
|
|
case CELL_GCM_SURFACE_TARGET_0:
|
|
case CELL_GCM_SURFACE_TARGET_1:
|
|
{
|
|
// Fast path for non-MRT using glBlitFramebuffer.
|
|
GLfbo::Bind(GL_DRAW_FRAMEBUFFER, 0);
|
|
// Renderbuffer is upside turn , swapped srcY0 and srcY1
|
|
GLfbo::Blit(0, RSXThread::m_height, RSXThread::m_width, 0, 0, 0, RSXThread::m_width, RSXThread::m_height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
}
|
|
break;
|
|
|
|
case CELL_GCM_SURFACE_TARGET_NONE:
|
|
case CELL_GCM_SURFACE_TARGET_MRT1:
|
|
case CELL_GCM_SURFACE_TARGET_MRT2:
|
|
case CELL_GCM_SURFACE_TARGET_MRT3:
|
|
{
|
|
// Slow path for MRT/None target using glReadPixels.
|
|
if (m_set_scissor_horizontal && m_set_scissor_vertical)
|
|
{
|
|
glScissor(0, 0, RSXThread::m_width, RSXThread::m_height);
|
|
checkForGlError("glScissor");
|
|
}
|
|
|
|
static u8* src_buffer = nullptr;
|
|
static u32 width = 0;
|
|
static u32 height = 0;
|
|
GLenum format = GL_RGBA;
|
|
|
|
if (m_read_buffer)
|
|
{
|
|
format = GL_BGRA;
|
|
gcmBuffer* buffers = (gcmBuffer*)Memory.GetMemFromAddr(m_gcm_buffers_addr);
|
|
u32 addr = GetAddress(re(buffers[m_gcm_current_buffer].offset), CELL_GCM_LOCATION_LOCAL);
|
|
|
|
if (Memory.IsGoodAddr(addr))
|
|
{
|
|
width = re(buffers[m_gcm_current_buffer].width);
|
|
height = re(buffers[m_gcm_current_buffer].height);
|
|
src_buffer = (u8*)Memory.VirtualToRealAddr(addr);
|
|
}
|
|
else
|
|
{
|
|
src_buffer = nullptr;
|
|
}
|
|
}
|
|
else if (m_fbo.IsCreated())
|
|
{
|
|
format = GL_RGBA;
|
|
static std::vector<u8> pixels;
|
|
pixels.resize(RSXThread::m_width * RSXThread::m_height * 4);
|
|
m_fbo.Bind(GL_READ_FRAMEBUFFER);
|
|
glReadPixels(0, 0, RSXThread::m_width, RSXThread::m_height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, pixels.data());
|
|
|
|
src_buffer = pixels.data();
|
|
width = RSXThread::m_width;
|
|
height = RSXThread::m_height;
|
|
}
|
|
else
|
|
src_buffer = nullptr;
|
|
|
|
if (src_buffer)
|
|
{
|
|
glDisable(GL_STENCIL_TEST);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_CLIP_PLANE0);
|
|
glDisable(GL_CLIP_PLANE1);
|
|
glDisable(GL_CLIP_PLANE2);
|
|
glDisable(GL_CLIP_PLANE3);
|
|
glDisable(GL_CLIP_PLANE4);
|
|
glDisable(GL_CLIP_PLANE5);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, g_flip_tex);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, GL_UNSIGNED_INT_8_8_8_8, src_buffer);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0, 1, 0, 1, 0, 1);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
GLfbo::Bind(GL_DRAW_FRAMEBUFFER, 0);
|
|
|
|
m_program.UnUse();
|
|
m_program.Use();
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_ACCUM_BUFFER_BIT);
|
|
|
|
glColor3f(1, 1, 1);
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2i(0, 1);
|
|
glVertex2i(0, 0);
|
|
|
|
glTexCoord2i(1, 1);
|
|
glVertex2i(1, 0);
|
|
|
|
glTexCoord2i(1, 0);
|
|
glVertex2i(1, 1);
|
|
|
|
glTexCoord2i(0, 0);
|
|
glVertex2i(0, 1);
|
|
glEnd();
|
|
}
|
|
|
|
if (m_set_scissor_horizontal && m_set_scissor_vertical)
|
|
{
|
|
glScissor(m_scissor_x, m_scissor_y, m_scissor_w, m_scissor_h);
|
|
checkForGlError("glScissor");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Draw Objects
|
|
for (uint i = 0; i<m_post_draw_objs.size(); ++i)
|
|
{
|
|
m_post_draw_objs[i].Draw();
|
|
}
|
|
|
|
m_frame->Flip(m_context);
|
|
|
|
}
|
|
|
|
u32 LinearToSwizzleAddress(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;
|
|
}
|